Terrain

Terrain으로 지형을 생성하고 꾸밀 수 있다.

Terrain

맨 우측 Terrain Settings에서 Terrain에 대한 설정을 할 수 있다.

- 너비, 길이, 높이 조절 가능하다.

Terrain Settings
Resolution

Paint Terrain에서 Terrain의 모양을 꾸밀 수 있다.

Paint 모드

- 좌클릭시 모양에 따라 Terrain이 그려지고

- Shift + 좌클릭시 지워진다.

- Brush Size : 붓 크기 (단축키 -> 키우기: ] / 줄이기: [ )

- Opacity : 그려지는 속도를 조절한다. (값이 작을 수록 조금씩 그려짐)

Paint Terrain

Terrain 작업을 할 땐 좌측 아래를 기준점으로 잡고 시작하는 게 좋다.

Terrain 위젯

Set Height 모드

- Height에서 값을 조정하여 최대 높이에 제한을 둘 수 있다.

- Flatten All을 통해 전체 Terrain의 높이를 설정할 수 있다. (평평해짐)

Set Height
Set Height 2

- Flatten All과 Terrain Settings의 최대 높이 설정을 통해 아래 깊이와 높이의 제한을 설정할 수 있다.

ex) 아래로 100 위로 600

깊이 설정

Terrain을 깊이만큼 내려서(-높이 y축 만큼) (0,0,0)에서 작업해주면 좋다.

기초 설정

추가로 마우스 우클릭 + W,A,S,D로 이동할 때 카메라 이동이 점점 빨라져서 불편하다면 씬의 카메라 아이콘을 클릭하여 Camera Acceleration을 끄면 된다.

카메라 가속 끄기

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 아르곤 침공' 카테고리의 다른 글

Input Manager와 Input System  (0) 2022.08.25
Timeline  (0) 2022.08.25
Terrain Texture, Tree  (0) 2022.08.25
Terrain Tools  (0) 2022.08.25
아르곤 침공 게임 디자인  (0) 2022.08.25

아르곤 침공

플레이어 경험

- 혼돈

핵심 메커니즘

- 피하고 쏘기

핵심 게임 루프

- 가능한 높은 점수를 얻기 위해서 최대한 멀리 가고 죽으면 처음부터 다시 시작하기

 

게임 테마

행성 Argon을 침공한 적들이 행성을 파괴하지 못하도록 지키는 것

 

요소들 - 우선순위

1. 카메라 레일 : 레벨에 경로가 있고 카메라가 따라가는 것 - 시네머신 사용

2. 플레이어 움직임 : 수평, 수직 움직임

3. 쏘기 : 플레이어가 상대편에 피해를 입히는 총알을 발사한다.

4. 체력 : 적들에게 체력 추가 - 총알을 맞았을 때 체력 줄어듬

5. 적 경로 : 적들은 정해진 경로를 따라서 이동 - 디자이너가 배치한 경로

6. 점수 : 적들을 죽여서 얻는 점수

7. 게임 루프 : 죽으면 레벨을 재시작한다.

 

고려 해볼만한 것들

- 다중 레벨

- 플레이어 쉴드

- 아이템

- 피격시 일시적 무적 기능

- 무기 업그레이드

 

 

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 아르곤 침공' 카테고리의 다른 글

Input Manager와 Input System  (0) 2022.08.25
Timeline  (0) 2022.08.25
Terrain Texture, Tree  (0) 2022.08.25
Terrain Tools  (0) 2022.08.25
Terrain  (0) 2022.08.25

게임 디자인 접근법

순간(moment)을 디자인 하고 레벨로 확장시킨다 -> moment들을 조합하기

ex) 주변 환경 활용 moment - Fly under, Fly over, Fly through a gap(틈 사이로 날아가기), 타이밍 맞춰서 날아가기, 움직이는 플랫폼에 착륙하기, 좁은 터널을 지나가기

ex2) 이미 게임에 있는 요소 활용 - 특정 레벨에서 로켓을 느리게하기(손상된 상태 - 왼쪽 회전만 된다든지), 부스터 아이템, 더 어두운 레벨, 밝은 레벨, 카메라 가까운 레벨, 더 큰 로켓, 조작키를 반대로 하기 등등

 

어플리케이션 종료

Application.Quit();는 빌드된 상태일 때 어플리케이션을 종료하는 함수이다.

Application.Quit();

 

어플리케이션 빌드

File - BuildSettings - Build 선택

Build
build된 모습
빌드 후 플레이

 

코드로 장애물 움직이기

양 옆으로 왔다 갔다 하는 장애물을 만들 예정이다.

[SerializeField]하고 [Range( , )]를 flaot 값 앞에 붙이면 인스펙터에 슬라이더처럼 생긴다.

[Range(,)]
슬라이더

 

Sine

Sin은 펜으로 나무디스크를 돌 때 균일한 속도로 종이가 움직이며 생긴 파형처럼 생성된다.

- 주기 : x축으로 다시 같은 지점까지 올 때까지의 크기 (걸리는 시간)

- 진폭 : y축으로 중앙부터 꼭짓점까지의 크기

SIn Cos

Tau

반지름이 1인 원에서 반지름을 길이 1의 호로 나타냈을 때의 각도가 radian이다. 여기서 3.14~~radian이 ㅠ(파이)고 6.28~~radian T(타우)다.  

타우

 

Nan 오류

어떠한 값을 0으로 나누려 할 때 뜬다.

NaN

float 값을 0과 비교해야할 일이 있을 때 Mathf.Epsilon을 사용한다.

Mathf.Epsilon

 

Oscillator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Oscillator : MonoBehaviour
{
    private Vector3 startingPosition;
    
    [SerializeField] private float period = 2f;
    
    [SerializeField] private Vector3 movementVector;
    private float movementFactor;
    void Start()
    {
        startingPosition = transform.position;
        Debug.Log(startingPosition);
    }

    void Update()
    {
        //if (period <= 0) return; 
        if (period == Mathf.Epsilon) return; // float 같은 소수에 0과 비교할 때 사용 
        float cycles = Time.time / period; // 시간에 따라 계속 증가
        
        const float tau = Mathf.PI * 2; // 6.283 일정한 값
        float rawSinWave = Mathf.Sin(cycles * tau); // -1~1

        movementFactor = (rawSinWave + 1f) / 2; // 0~2 -> 0~1
        
        Vector3 offset = movementVector * movementFactor;
        transform.position = startingPosition + offset;
    }
}

 

 

플레이 영상

잘 움직이는 모습

플레이 영상

 

치트키

L을 누르면 다음 레벨로 가게, C를 누르면 충돌 효과를 안 받게 하도록 하였다.

bool 타입은 아래처럼 사용하여 꺼져있으면 키고 켜져있으면 끄게 사용 가능하다.

toggle

 

CollisionHandler.cs

using System;
using UnityEngine;
using UnityEngine.SceneManagement;

public class CollisionHandler : MonoBehaviour
{
    private bool isTransitioning = false;
    private bool collisionDisabled = false;
    
    private AudioSource _audioSource;
    private Movement _movement;
    private Collider _collider;
    
    [SerializeField] float levelLoadDelay = 1f;

    [SerializeField] private AudioClip crashSound;
    [SerializeField] private AudioClip landingSound;
    [SerializeField] private ParticleSystem crashParticle;
    [SerializeField] private ParticleSystem landingParticle;

    void Start()
    {
        _audioSource = GetComponent<AudioSource>();
        _movement = GetComponent<Movement>();
        _collider = GetComponent<Collider>();
    }

    void Update()
    {
        RespondToDebugKeys();
    }
    
    void RespondToDebugKeys()
    {
        if (Input.GetKeyDown(KeyCode.L))
        {
            LoadNextLevel();
        }
        else if (Input.GetKeyDown(KeyCode.C))
        {
            collisionDisabled = !collisionDisabled; // toggle collision (true면 false, false면 true)
        }
    }

    void OnCollisionEnter(Collision collision)
    {
        if (isTransitioning || collisionDisabled)
            return;

        switch (collision.gameObject.tag)
        {
            case "Friendly":
                Debug.Log("This thing is firendly");
                break;
            case "Finish":
                StartSuccessSequence();
                break;
            default:
                StartCrashSequence();
                break;
        }
    }

    void StartCrashSequence()
    {
        isTransitioning = true;
        _audioSource.Stop();
        PlaySound(crashSound);
        crashParticle.Play();
        _movement.enabled = false;
        Invoke(nameof(ReloadLevel), levelLoadDelay);
    }

    void LoadNextLevel()
    {
        int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
        int nextSceneIndex = currentSceneIndex + 1;
        if (nextSceneIndex == SceneManager.sceneCountInBuildSettings)
        {
            nextSceneIndex = 0;
        }
        SceneManager.LoadScene(nextSceneIndex);
    }

    void StartSuccessSequence()
    {        
        isTransitioning = true;
        _audioSource.Stop();
        PlaySound(landingSound);
        landingParticle.Play();
        _movement.enabled = false;
        Invoke(nameof(LoadNextLevel), levelLoadDelay);
    }

    void ReloadLevel()
    {
        int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
        SceneManager.LoadScene(currentSceneIndex);
    }

    void PlaySound(AudioClip clip)
    {
        GetComponent<AudioSource>().PlayOneShot(clip);
    }


}

 

 

외부 환경 만들기

라이팅 

Main Directional Light(sun) - 위치는 상관 없고 방향이 중요하다.(그림자에 영향을 줌)

Environment Lighting : 반사광

Scene Lights : Point Light, Spot Light

- PointLight : 전구와 비슷함, 범위가 있고 가운데에서 멀어질 수록 밝기가 낮음

ex) 가로등

Point Light

- Spot Light : 극장에서의 스포트라이트와 비슷함

Spot Light

* 라이팅을 어둡게 설정하고 싶은데 씬 창에서 조작하기 불편하다 하면 조명키를 눌러주면 된다. (대신 조명 설정은 못 봄)

씬창 조명 키기

- 장애물과 출발지, 도착지에는 Point Light 캐릭터에는 스포트라이트를 두어 어디로 가는지 어디가 위험한지 직관적으로 표현한다.

캐릭터 Spot Light

Material의 Emssion을 활용하면 발광체를 만들 수도 있다.

Emission

Window - Rendering - Lighting에서 Enviroment의 Skybox Material부터 바꿔줬다.

Lighting

이후 프로젝트 창에서 Material을 하나 생성해서 Shader 탭에서 Skybox 중 Procedual으로 바꿔줬다.

Shader - Skybox - Procedual

 이 쉐이더에서 skybox 부분 요소를 정할 수 있다.

지평선이 덜 어둡게 보인다.

이후 카메라에서 Clear Flags를 Solid Color로 해주고 Background를 검정색으로 하면 뒷 배경도 어두워진다.

뒷 배경도 어두워졌다.

 

피봇 사용하기

씬에서 오브젝트를 다룰 때 Global 대신 Local을 사용하면 그 오브젝트의 피봇을 기준으로 조작할 수 있다.

피봇

Extract Method

Extract Method를 통해 기능마다 함수로 나누어 가독성을 높인다.

- 드래그 후 단축키 Ctrl + R + M *라이더 기준

Extract Method

- 이후 이름 짓기 * 나중에 F2 버튼을 통해 다시 지어도 됨 *라이더 기준

이름 짓기(동사 + 명사)

 

Movement.cs

순서를 맞추면 가독성이 높아진다. Update 안에 ProcessThrust, ProcessRotation -> 이후 순서도 ProcessThrust, ProcessRotation

- 이후에 left right stop 등

using UnityEngine;
using UnityEngine.Serialization;

public class Movement : MonoBehaviour
{
    private Rigidbody _rigid;
    private AudioSource _audioSource;
    
    [SerializeField] private float mod = 100f;
    [SerializeField] private float rotationSpeed = 1f;
    
    [SerializeField] private AudioClip mainEngine;
    [SerializeField] private ParticleSystem thrustParticle;
    [SerializeField] private ParticleSystem rightThrustParticle;
    [SerializeField] private ParticleSystem leftThrustParticle;
    
    void Start()
    {
        _rigid = GetComponent<Rigidbody>();
        _audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        ProcessThrust();
        ProcessRotation();
    }

    void ProcessThrust()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            StartThrusting();
        }
        else
        {
            StopThrusting();
        }
    }
    
    void ProcessRotation()
    {
        if (Input.GetKey(KeyCode.A))
        {
            RotateLeft();
        }
        else if (Input.GetKey(KeyCode.D))
        {
            RotateRight();
        }
        else
        {
            StopRotating();
        }
    }

    void StartThrusting()
    {
        _rigid.AddRelativeForce(Vector3.up * (mod * Time.deltaTime));
        if (!_audioSource.isPlaying)
        {
            _audioSource.PlayOneShot(mainEngine);
        }
        if (!thrustParticle.isPlaying)
        {
            thrustParticle.Play();
        }
    }
    
    void StopThrusting()
    {
        thrustParticle.Stop();
        _audioSource.Stop();
    }

    private void RotateLeft()
    {
        ApplyRotation(rotationSpeed);
        if (!rightThrustParticle.isPlaying)
        {
            rightThrustParticle.Play();
        }
    }
    
    private void RotateRight()
    {
        ApplyRotation(-rotationSpeed);
        if (!leftThrustParticle.isPlaying)
        {
            leftThrustParticle.Play();
        }
    }
    
    private void StopRotating()
    {
        rightThrustParticle.Stop();
        leftThrustParticle.Stop();
    }

    void ApplyRotation(float rotationThisFrame)
    {
        // rigid.constraints = (RigidbodyConstraints)((int)RigidbodyConstraints.FreezeRotationX + (int)RigidbodyConstraints.FreezeRotationY);
        // rigid.constraints = RigidbodyConstraints.FreezeRotation;
        
        _rigid.freezeRotation = true; 
        transform.Rotate(Vector3.forward * (rotationThisFrame * Time.deltaTime));
        _rigid.freezeRotation = false;
    }
    
}

파티클 시스템

Particles : Emitter에서 생성되는 것들 * 각 파티클들은 게임 개체가 아님

Particles System : 이미터와 여러 파티클들로 이루어져있고 게임 객체에 추가되는 컴포넌트 - 게임 오브젝트는 부착된 컴포넌트에 따라 유형이 정해짐 (충돌을 해결할 때 게임 개체에 추가되기도 함)

Emitter : 파티클들을 방출(Emitting)하는 물체, 공간, 혹은 지점

Module : 파티클에 추가 효과 부여

파티클 시스템
모듈

 

 

프리펩에 파티클 추가

파티클을 프리펩 인스펙터 창에서 불러올 때 프로젝트 창에서가 아닌 자식 오브젝트로부터 불러오는 게 좋다 -> 위치나 설정이 잘 안 되어 있을 수 있음

파티클 불러오기

 

좌우 부스터 로직

- 첫 if에서는 오른쪽 부스터가 켜질 때(왼쪽으로 회전할 때), 오른쪽 부스터 파티클이 플레이중이지 않을 때

- else if에서는 왼쪽 부스터가 켜질 때(오른쪽으로 회전할 때), 왼쪽 부스터 파티클이 플레이중이지 않을 때

- else에서는 아니면 다 멈추기

사이드 부스터

 

bool 변수

bool 값을 사용하여 Transition중(이미 충돌)이면 return을 반환하게 하였다.(void 함수 나가기)

bool 변수

- 충돌하는 부분과 클리어하는 부분에 isTransitioning에 true 값을 줘서 충돌을 제어한다.

- AudioSource.Stop()은 해당 오디오 소스의 재생되던 사운드가 꺼지게 하는 것이다.

bool ~ = true

 

 

프리펩

프리펩 이동

프로젝트 창에서 더블클릭 할 때 -> 프리펩만 있는 공간으로 이동

프리펩1

하이러키 창에서 >를 클릭하여 프리펩 모드로 들어갈 때 -> 뒷배경이 보이는 프리펩을 볼 수 있다.

프리펩 2

 

프리펩 특징

1. 큐브, 캡슐 등등 프리펩 하위에 그 오브젝트 타입을 생성하면 크기나 위치가 같아진다.

* 부모와는 다르게 자식의 스케일은 1로 되어있음 => 여기서 여러 문제가 발생하여 보통은 부모의 스케일은 기본 값으로 조정하고 자식에서 스케일이나 등을 조절하는 식으로 많이 만든다.

부모 프리펩
자식 프리펩

2. 자식의 위치는 부모의 상대적인 위치이다. => 부모의 위치는 자식의 피봇이 된다

부모가 자식의 피봇이 된다.

3. 부모의 피봇과 자식오브젝트들의 중심 위치가 비슷해야 어색하지 않게 회전할 수 있다.

피봇 위치 맞추기

 4. 콜라이더는 부모에서 한 번에 처리했다. -> 깔끔

자식 콜라이더 삭제
부모 콜라이더 생성

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 부스트 프로젝트' 카테고리의 다른 글

간단한 리팩토링  (0) 2022.08.23
파티클 시스템  (0) 2022.08.23
오디오 클립  (0) 2022.08.23
Invoke 함수 사용  (0) 2022.08.12
SceneManager  (0) 2022.08.12

다중 오디오 클립

다중으로 오디오 클립을 캐싱하여 조건마다 소리를 낼 수 있다.

오디오 캐싱

CollisionHandler.cs

using System;
using UnityEngine;
using UnityEngine.SceneManagement;

public class CollisionHandler : MonoBehaviour
{
    private AudioSource _audioSource;
    private Movement _movement;
    
    [SerializeField] float levelLoadDelay = 1f;

    [SerializeField] private AudioClip crashSound;
    [SerializeField] private AudioClip landingSound;

    private void Start()
    {
        _audioSource = GetComponent<AudioSource>();
        _movement = GetComponent<Movement>();
    }

    private void OnCollisionEnter(Collision collision)
    {
        switch (collision.gameObject.tag)
        {
            case "Friendly":
                Debug.Log("This thing is firendly");
                break;
            case "Finish":
                StartSuccessSequence();
                break;
            default:
                StartCrashSequence();
                break;
        }
    }

    void StartCrashSequence()
    {
        PlaySound(crashSound);
        _movement.enabled = false;
        Invoke(nameof(ReloadLevel), levelLoadDelay);
    }

    void LoadNextLevel()
    {
        int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
        int nextSceneIndex = currentSceneIndex + 1;
        if (nextSceneIndex == SceneManager.sceneCountInBuildSettings)
        {
            nextSceneIndex = 0;
        }
        SceneManager.LoadScene(nextSceneIndex);
    }

    void StartSuccessSequence()
    {
        PlaySound(landingSound);
        _movement.enabled = false;
        Invoke(nameof(LoadNextLevel), levelLoadDelay);
    }

    void ReloadLevel()
    {
        int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
        SceneManager.LoadScene(currentSceneIndex);
    }

    void PlaySound(AudioClip clip)
    {
        GetComponent<AudioSource>().PlayOneShot(clip);
    }
}

Movement.cs

using UnityEngine;

public class Movement : MonoBehaviour
{
    private bool isAlive;
    
    private Rigidbody _rigid;
    private AudioSource _audioSource;
    
    [SerializeField] private float mod = 100f;
    [SerializeField] private float rotationSpeed = 1f;
    
    [SerializeField] private AudioClip _mainEngine;
    void Start()
    {
        _rigid = GetComponent<Rigidbody>();
        _audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        ProcessThrust();
        ProcessRotation();
    }

    void ProcessThrust()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            _rigid.AddRelativeForce(Vector3.up * (mod * Time.deltaTime));
            if (!_audioSource.isPlaying)
                _audioSource.PlayOneShot(_mainEngine);
        }
        else
            _audioSource.Stop();
    }

    void ProcessRotation()
    {
        if (Input.GetKey(KeyCode.A))
        {
            ApplyRotation(rotationSpeed);
        }
        else if (Input.GetKey(KeyCode.D))
        {
            ApplyRotation(-rotationSpeed);
        }
    }

    private void ApplyRotation(float rotationThisFrame)
    {
        // rigid.constraints = (RigidbodyConstraints)((int)RigidbodyConstraints.FreezeRotationX + (int)RigidbodyConstraints.FreezeRotationY);
        // rigid.constraints = RigidbodyConstraints.FreezeRotation;
        
        _rigid.freezeRotation = true; 
        transform.Rotate(Vector3.forward * (rotationThisFrame * Time.deltaTime));
        _rigid.freezeRotation = false;
    }
}

 

플레이 영상

다중 오디오 클립

착지 이후에 충돌 소리가 나는 버그는 나중에 수정하도록 하자 (collision 스크립트를 끈다던지)

'유데미 강의 > C#과 Unity로 3D 게임 개발하기 : 부스트 프로젝트' 카테고리의 다른 글

파티클 시스템  (0) 2022.08.23
bool 변수로 제어하기, 로켓 꾸미기  (0) 2022.08.23
Invoke 함수 사용  (0) 2022.08.12
SceneManager  (0) 2022.08.12
Switch  (0) 2022.08.12

배운 것들

이번 주 수업에서는 쉐이더를 배웠다.

유니티는 기본적으로 Standard라는 쉐이더형을 제공해서 알아서 물리적인 연산을 처리해준다.

Material의 Shader

 

쉐이더 파일

- 쉐이더 파일은 프로젝트 폴더에 우클릭하여 필요한 쉐이더 종류를 선택하고 생성하면 된다. (이 글에서는 Unlit Shader를 선택하였다 - UI 쪽에 사용되는 쉐이더)

- 쉐이더 파일에서 Material 부분의 Shader의 경로를 지정할 수 있는데 아래 사진의 파란색 Shader 뒷 부분의 경로가 해당 쉐이더 파일의 경로이다(자신이 수정 가능).

Shader 파일

- Properties는 인스펙터 창에서 조절할 수 있는 것들을 적는 부분이다.

- SubShader Tags 에는 렌더 타입을 정할 수 있다.

Properties

- Pass를 여러개 작성하면 여러 효과도 줄 수 있지만 하나당 드로우콜이 발생하여 성능에 과부하를 준다.

- Pass의 CGPROGRAMENDCG는 이 사이에 Cg언어를 사용하겠다는 표시다.

- #pragma는 vertex와 fragment 쉐이더에 어떤 이름의 함수로 지정할 것인가를 정의한다.

- struct type의 vertex, fragment에서 필요한 매개변수들을 초기화하고 실제로 사용할 변수들(sampler2D~float - 위에서 매칭한 것들)까지 선언해준다.

Pass

- 이후 vert에서 버텍스를 연결해주고 frag에서 색을 입혀주면 쉐이더가 만들어진다.

vert, frag

 

위에서의 결과물

- vert의 o.uv = o.uv + _Time;에서 움직이는 텍스처를 만들어줬다.

- frag의 return c - lerp~에서 위의 _DissolveTextrue의 빨간색 삼각형 부분을 빼줬다.

결과물

 

 

과제 내용

- 첫 번째 과제는 버튼을 눌렀을 때 PlayOneShot을 사용하여 여러번 클릭하더라도 중복으로 소리가 안 들리게 하는 것이다.

- 두 번째 과제는 Capture버튼을 눌렀을 때 UI를 제외하여 캡처하면서 텍스처 해상도는 반으로 줄이는 것이다. - 짝수행과 짝수 열만 출력되게 옮기기

과제 동영상

1920x1080 -> 960x540

 

 

한 주 후기

 이번 주는 쉐이더에 대해 배웠다. 나는 Material을 이용할 줄만 알지 쉐이더에 대한 건 전혀 알지 못했었는데 이번 교육을 들으면서 Shader의 구조도 알아보고 사용법도 간단하게 알게 되었다. 사실 처음 쉐이더 강의를 듣게 되었을 때는 조금 어려운 느낌이 들었다. 지금까지 교육을 들으면서 고비가 2번 있었는데 UI강의를 들을 때(UI 종류가 너무 많았다) 이번에 쉐이더를 들으면서 cg언어라는 새로운 언어를 접하고 안에 경험해보지 못한 형식에 따라 코드를 작성하는 부분이 어렵게 느껴졌던 것 같다. 그런데 긍정적으로 따라가다 보니까 대략적으로나마 이런식으로 사용하는구나! 하고 이해할 수 있게되어 다행이었다.

짝수행과 열만 출력하기

 

 이번 주 과제를 진행하면서 첫 번째 과제인 사운드 부분은 쉽게 해결할 수 있었는데 두 번째 과제인 텍스처의 해상도를 줄이는 작업은 쉽지 않았던 것 같다. 특히나 위 사진의 짝수 행과 짝수 열만 옮기는 로직을 생각하는 게 과제를 받은 날의 저녁 늦게까지 하면서 고민했던 문제였다. 최근에 그래도 테스트를 위해 알고리즘 문제를 여럿 풀어봤던 것이 도움이 되었는지 생각을 비우고 다음날 몇 번 적어서 테스트를 해보니까 '이렇게 쉽게 해결할 수 있던 거였어?' 할 문제였던 것 같다. 정답이라고 할 순 없지만 해결을 했다는 것에 뿌듯함을 느낄 수 있었던 과제였다.

 이번 주는 스타터스 조기 인턴으로 인한 내부 테스트와 동기들 회식으로 정신 없었던 한 주였던 것 같다. 테스트를 보고나서 내가 어느 부분이 얼마나 부족한지 객관적으로 판단하는 계기가 되었고 동기들과의 회식을 참여하면서 동기들과 더 친해질 수 있는 기회가 되었다. 다음 주도 재밌게 수업 들어야겠다. 파이팅!

 

 

유데미코리아 바로가기

본 포스팅은 유데미-웅진씽크빅 취업 부트캠프 유니티 1기 과정 후기로 작성되었습니다.

 

 

+ Recent posts