260902 Unity(3) Input Manager, Scene, Transform

무알콜 삼다수·2026년 9월 6일
post-thumbnail

1. Input Manager

1.1. 빌드

  • File/Build Setting/에서 빌드할 수 있다.
  • 상당히 많은 시간이 걸린다.
  • Ctrl Shift B

1.2. 개요

  • 유저 입력을 받는 것을 관리한다.
  • 심도 있는 것은 없다.
  • Unity 6에서는 아예 Input System이라고 해서 기본적으로 사용한다.
  • 그럼에도 이것을 사용하는 것은 구버전은 이것을 사용할 수도 있고 Input System은 이벤트 기관으로 돌아가므로 Input Manager보다는 직관적이지는 않다.

1.3. 설명

  • Input.GetKey가 기본.
  • 키를 누르는 순간에만 반응하는 것도 있고 땔 때 반응하는 것도 있다.
  • 그럴 것을 대비해서 GetKeyDown, GetKeyUp, KeyCode라는 Enum을 통해 직관적으로 구현되어 있다.
  • 위 함수는 bool형이다. 눌렸는가 아니냐이니까.
  • Input.GetAxis, GetAxisRaw의 경우는 특정 축에 입력받는 것이다. Horizontal, Vertical 등 여기에 관련됨.
  • 위 2개는 float로 반환한다.

2. Scene

2.1. 개요

  • 게임 오브젝트들과 설정들을 모아둔 파일을 일컫는다.
  • 큰 기능을 담당하는 장면으로도 유효하다.
  • 어떤 오브젝트가 연결되고 컴포넌트가 연결된 것을 확인할 수 있다.
  • guid가 깨지면 Scene은 쓸 수 없다.
  • 여러 오브젝트가 관련되어 서술되어 있는데 guid가 깨지면 뭘 할 수 없다.
  • 객체지향을 생각해보면 scene 하나에 모든 기능들을 떄려 넣었다고 생각하면 매우 난감하다.
  • 이것도 Scene을 나눠서 설정하면 관리 측면에서 편해진다.
  • 실제 게임에 적용시키려면 Build Setting에 들어가서 설정해야 한다.
  • 테스트나 UI 등을 사용해야 할 때는 별도로 만들어서 설정하는 경우가 많기 때문에 빌드에 추가되지 않도록 한다.
  • 물론 Scene의 이름으로도 불러올 수 있지만 우측에 있는 빌드 인덱스로도 불러올 수 있다.

2.2. 사용

다음 이미지와 같이 Scene을 만들어서 Title을 실행.그리고 상위 폴더에 Script라는 폴더 생성.그 다음, 그 폴더 안에 Script를 만들어 SceneChanger라 명명만든 C# Script를 들어가 사용할 헤더를 설정.Update로 매 프레임마다 한 번씩 Space가 눌렸는지 확인한 다음빌드에 세팅을 올려둔 다음Title Scene에서 게임 오브젝트를 추가하여 그곳에 만든 C# Script를 붙여주고실행 시 Scene 전환이 이뤄지는 것을 확인할 수 있게 된다.기능을 추가해보도록 할 것인데 만일 Esc키를 눌리면이렇게 씬을 넘어갈 수 있도록 설정을 했어도 Esc로 불가하다. Game Scene에서는 추가를 안해놨기 때문이다. 그래서 Scene이 넘어가더라도 살려놔야 한다.
단순하게는 GameScene에도 넣어놔도 상관은 없을 것이다.
그러나 다른 방법이 존재한다.로드가 되더라도 파괴되지 않게끔 하겠다고 직관적인 표현으로 되어 있다.
자동으로 편성이 된다.문제는 이를 반복하면 자동으로 계속 늘어나게 된다. SceneChanger가 늘어나면 늘어날수록 Scene Changer가 다수를 동시 구동한다.추가적인 처리가 필요한다. 이것은 Singleton Pattern에서 다루게 된다.
지금으로써는 동일한 객체가 있다면 죽여놓아야 한다.

 private void Start()
    {
        // Scene 전체적으로 판단할 수 밖에 없으므로 Find를 사용해볼 것이다. 
        SceneChanger s = FindObjectOfType<SceneChanger>();
        // 만일 다른 SceneChanger가 있으면 찾겠지만 단 하나 남은 SceneChanger가 있을 수 있다.
        // 그래서 null이 아니고 자신이 아니어야 한다.
        if (s != null && s != this)
        {
            // 파괴
        }
        DontDestroyOnLoad(gameObject);
    }

파괴의 측면에서 싱글톤 패턴이 이런 아이디어로 접근하게 되는 것.

지금은 직관적으로 두 곳 모두에 이것을 적용시키도록 하자.

현재 문자열 기반으로 실수가 많으므로 빌드 인덱스 기준으로 가보도록 하면첫 화면Space 입력 시Esc 입력 시

  • 부가적인 옵션을 불러오면서 Scene을 불러오는 경우도 존재.
  • 현재는 단일 Scene을 불러오지만 중복으로 열어서 쓰는 경우도 존재한다. LoadSceneMode.Additive
  • 이렇게 특정 영역가면 중복으로 Scene을 로드해서 보여주는 기법도 존재.
  • 추가된 Scene을 언로드하는 방법도 존재.
  • Scene을 비동기 방식으로 불러오는 방식이 존재하는데 크면 클수록 시간이 걸리므로 다른 작업을 수행해줘야 한다.
  • Scene 단위를 불러올 때 멈춰서 좋을 것은 없기 때문에 로딩바를 돌리도록 하거나 하는 식으로 지속적으로 돌아간다. 이럴 때 비동기 방식을 취한다.
  • 물론 규모가 작다면 크게 필요는 없겠지만.
  • 유니티 자체에서 구조체 Scene을 제공하기는 한다.
  • Scene을 다중으로 처리하는 게임이 아닌 이상 적극적으로 이를 사용하진 않음.
  • 특정 키가 눌렸다고 처리는 했지만 나중 Scene은 버튼 하나 추가해서 처리하는 식으로 처리가 될 것.

3. Transform

3.1. 개요

  • 3개 축에 대한 것을 다룰 때 Vector3를 사용한다. 단순히 숫자 3개를 보관할 때 사용하기도 한다.
  • 하나의 구조체라고 생각하면 된다.
  • 게임이라는 것은 공간 개념이 중요하다보니 직관적으로 단위나 방향 벡터 등이 되어 있다.
  • Vector3.zero, Vector3.one, Vector3.사방향

3.2. 단위 벡터

  • 벡터는 단순 좌표로도 표기하지만 얼마나 좌표 이동을 하는지에 대한 값이다.
  • 게임에서 잘 쓰려면 크기를 배제시킨 상태(방향)거나 크기만을 사용하거나가 많다.
  • 방향만을 가지는 벡터에 속도를 곱해주면 매 프레임마다 이동을 할 것이고 구현 원리이다.
  • 이 크기가 1인 벡터를 단위 벡터라고 부른다.
  • 크기만 가진 벡터를 Scalar이다.

3.3. 사용

  • Transform은 행렬 연산을 통해 구동이 된다.
  • Vector3만이 아닌 Vector2도 있다.

GameScene로 이동을 하여 키 입력을 받아보도록 해볼 것이다. Cube 하나와 C# Script를 하나 생성한다.트랜스폼은 모든 게임 오브젝트에 다 달려있다.
이 상황에서 소스 코드에서 제어를 하려면 가져와야 할 것이 있다.

private void Move()
    {
        float x = Input.GetAxisRaw("Horizontal"); // Raw는 단위 벡터로 설정된다.
        float z = Input.GetAxisRaw("Vertical");
        
        Vector3 movement = new Vector3(x, 0, z); // 이제 이것을 토대로 단위 벡터를 만들어야 한다.
        //movement.Normalize(); // 연산을 통해 단위벡터 출력.
        //movement.normalized; // 값만을 가져올 경우.
    }

이제 이동 속도가 필요. transform.을 들어가 position을 살펴보면 기능들이 잘 구현이 되어 있음을 알 수 있다.
Translate, Rotate 등 함수 호출들이 되어 있는 상태.하지만 각 PC마다의 이동 속도의 차이가 프레임에 따라 다를 수 있기 때문에이렇게 Time.deltaTime을 추가 처리해줘야 한다.다음과 같이 플레이 시 이동이 가능함을 볼 수 있다.
하지만 나중에는 물리 엔진에서 속도를 지정해서 명령을 내리는 것이 필요하다. 현재 형태와는 다를 것이다.
이젠 다른 방법을 해볼 것이다.
지금은 적용을 했을 때 내부적으로 더해주는 것.아까와 동일하게 적용이 된다.
그런데 이런 경우도 있다. 타겟팅하여 이동하는 방식도 있다. 이 방식에서는 당연히 찍어서 가면 되는데 이 상태에서는 시도할만한 요소가 존재한다.
MoveTorward를 쓰면 되는데 구조체에서 지원하는 기능을 써볼 것이다.뱀서류를 이렇게 추적시킬 수 있게 된다.
일정한 속도로 움직이게 할 수도 있지만 멀수록 빠르게 가까울수록 느리게 하는 방법도 가능하다. Vector3에서 제공하는 Lerp를 사용해볼 것이다.
Lerp는 선형 보간이다. 이미지 상 확인은 안되지만 실질적으로 속도가 점점 느려져서 다가오는 것을 확인할 수 있다.

3.4. 회전

  • 3축 회전은 짐벌락 현상을 야기할 수 있다.
  • 보간을 할 때 예상치 못한 회전이 일어날 수 있다.
  • 그래서 쿼터니언을 사용한다.
  • 효율적인 연산이 가능하다.
  • 주로 쓸 것은 Euler값 변환을 사용할 것이고 Transform.Rotation은 Quaternion 형식이어야 한다.

바로 사용해보도록 하자. R키를 누르고 있으면 회전하고 이동까지 되는 것을 확인할 수 있다.
물론 Rotate도 보간이 가능하다. Quaternion에서도 Lerp를 지원한다.
RotateAround라는 것이 존재하는데 Goal에 작업을 걸 것인데 이렇게 공전처럼 돌아가는 형태가 된다.
이외에도 LookRotation도 존재. 그럼 이것을 이용하여

using System;
using UnityEngine;

public class CubeMovement : MonoBehaviour
{
    //private Transform _transform;

    private void Awake()
    {
        // CubeMovement를 찾아다가 붙이기.
        // _transform = GetComponent<Transform>();
        // 이미 Transform은 모든 오브젝트에 달려있으므로 transform.으로 바로 얻어와 쉽게 쓸 수 있다.
    }
    
    [SerializeField] private float _moveSpeed;
    [SerializeField] private Transform _target;
    [SerializeField] private float _rotateSpeed;
    
    private void Update()
    {
        Vector3 movement = GetMovement();
        Move(movement);
        CubeRotate();
        /*Move();
        //MoveToTarget();
        CubeRotate();*/
    }

    private void CubeRotate()
    {
        // 키가 계속 눌리는 동안 돌아가게끔 만들 것이다.
        /*if (Input.GetKey(KeyCode.R))
        {
            // 회전은 특정 축을 기준으로 회전한다. up, right, forward를 쓰는데 이게 축 기준의 방향인 것.
            transform.Rotate(Vector3.up * _rotateSpeed * Time.deltaTime);
        }*/
    }

    private void MoveToTarget()
    {
        // 헌재 위치, 타깃은 Transform이므로 포지션으로 접근 가능, moveSpeed에 DeltaTime.
        /*transform.position = Vector3.MoveTowards(transform.position, 
                                                        _target.position, 
                                         _moveSpeed * Time.deltaTime);*/
        // MoveTowards와는 다르게 원리상 0~1의 범위값인데 0.5면 반에 도달하면 멈추고 또 반 이런 식으로 진행된다. 
        transform.position = Vector3.Lerp(transform.position, _target.position, _moveSpeed * Time.deltaTime);
    }

    private void Move(Vector3 movement)
    {
        if (movement == Vector3.zero)
        {
            return;
        }
        
        /*float x = Input.GetAxisRaw("Horizontal"); // Raw는 단위 벡터로 설정된다.
        float z = Input.GetAxisRaw("Vertical");
        
        Vector3 movement = new Vector3(x, 0, z); // 이제 이것을 토대로 단위 벡터를 만들어야 한다.
        movement.Normalize(); // 연산을 통해 단위벡터 출력.*/
        //movement.normalized; // 값만을 가져올 경우.
        // 방향에다가 속도를 곱해서 더해주면 이동이 된다.
        //transform.position += movement * _moveSpeed * Time.deltaTime; 
        // 하지만 프레임 단위가 필요.

        transform.Translate(movement * _moveSpeed * Time.deltaTime);
    }

    private Vector3 GetMovement()
    {
        float x = Input.GetAxisRaw("Horizontal"); // Raw는 단위 벡터로 설정된다.
        float z = Input.GetAxisRaw("Vertical");
        
        Vector3 movement = new Vector3(x, 0, z); // 이제 이것을 토대로 단위 벡터를 만들어야 한다.
        movement.Normalize(); // 연산을 통해 단위벡터 출력.
        //movement.normalized; // 값만을 가져올 경우.

        return movement.normalized;
    }
}

이제 보간과 LookRotation 적용하지만 문제점은 방향 유지가 되어야 하는데 안되고 있다.

using System;
using UnityEngine;

public class CubeMovement : MonoBehaviour
{
    //private Transform _transform;

    private void Awake()
    {
        // CubeMovement를 찾아다가 붙이기.
        // _transform = GetComponent<Transform>();
        // 이미 Transform은 모든 오브젝트에 달려있으므로 transform.으로 바로 얻어와 쉽게 쓸 수 있다.
    }
    
    [SerializeField] private float _moveSpeed;
    [SerializeField] private Transform _target;
    [SerializeField] private float _rotateSpeed;
    
    private void Update()
    {
        Vector3 movement = GetMovement();
        Move(movement);
        CubeRotate(movement);
        /*Move();
        //MoveToTarget();
        CubeRotate();*/
    }

    private void CubeRotate(Vector3 movement)
    {
        // 키가 계속 눌리는 동안 돌아가게끔 만들 것이다.
        /*if (Input.GetKey(KeyCode.R))
        {
            // 회전은 특정 축을 기준으로 회전한다. up, right, forward를 쓰는데 이게 축 기준의 방향인 것.
            transform.Rotate(Vector3.up * _rotateSpeed * Time.deltaTime);
        }*/

        //if (movement == Vector3.zero)
        if (movement.z == 0)
        {
            return;
        }
        
        Quaternion look = Quaternion.LookRotation(movement);
        
        transform.rotation = Quaternion.Slerp(
            transform.rotation, 
            look, 
            _rotateSpeed * Time.deltaTime
            );
    }

    private void MoveToTarget()
    {
        // 헌재 위치, 타깃은 Transform이므로 포지션으로 접근 가능, moveSpeed에 DeltaTime.
        /*transform.position = Vector3.MoveTowards(transform.position, 
                                                        _target.position, 
                                         _moveSpeed * Time.deltaTime);*/
        // MoveTowards와는 다르게 원리상 0~1의 범위값인데 0.5면 반에 도달하면 멈추고 또 반 이런 식으로 진행된다. 
        transform.position = Vector3.Lerp(transform.position, _target.position, _moveSpeed * Time.deltaTime);
    }

    private void Move(Vector3 movement)
    {
        if (movement == Vector3.zero)
        {
            return;
        }
        
        /*float x = Input.GetAxisRaw("Horizontal"); // Raw는 단위 벡터로 설정된다.
        float z = Input.GetAxisRaw("Vertical");
        
        Vector3 movement = new Vector3(x, 0, z); // 이제 이것을 토대로 단위 벡터를 만들어야 한다.
        movement.Normalize(); // 연산을 통해 단위벡터 출력.*/
        //movement.normalized; // 값만을 가져올 경우.
        // 방향에다가 속도를 곱해서 더해주면 이동이 된다.
        //transform.position += movement * _moveSpeed * Time.deltaTime; 
        // 하지만 프레임 단위가 필요.

        transform.Translate(movement * _moveSpeed * Time.deltaTime);
    }

    private Vector3 GetMovement()
    {
        float x = Input.GetAxisRaw("Horizontal"); // Raw는 단위 벡터로 설정된다.
        float z = Input.GetAxisRaw("Vertical");
        
        Vector3 movement = new Vector3(x, 0, z); // 이제 이것을 토대로 단위 벡터를 만들어야 한다.
        movement.Normalize(); // 연산을 통해 단위벡터 출력.
        //movement.normalized; // 값만을 가져올 경우.

        return movement.normalized;
    }
}

완성된 바라보며 이동하는 방식.

using System;
using UnityEngine;

public class CubeMovement : MonoBehaviour
{
    [SerializeField] private float _moveSpeed;
    [SerializeField] private Transform _target;
    [SerializeField] private float _rotateSpeed;
    
    private void Update()
    {
        Vector3 movement = GetMovement();
        Move(movement);
        CubeRotate(movement);
    }

    private void CubeRotate(Vector3 movement)
    {
        if (movement == Vector3.zero)
        {
            return;
        }
        
        Quaternion look = Quaternion.LookRotation(movement);
        
        transform.rotation = Quaternion.Slerp(
            transform.rotation, 
            look, 
            _rotateSpeed * Time.deltaTime
            );
    }

    private void MoveToTarget()
    {
        transform.position = Vector3.Lerp(transform.position, _target.position, _moveSpeed * Time.deltaTime);
    }

    private void Move(Vector3 movement)
    {
        if (movement == Vector3.zero) // 원래가 정답이었음
        {
            return;
        }
        transform.Translate(Vector3.forward * _moveSpeed * Time.deltaTime);
    }

    private Vector3 GetMovement()
    {
        float x = Input.GetAxisRaw("Horizontal"); // Raw는 단위 벡터로 설정된다.
        float z = Input.GetAxisRaw("Vertical");
        
        Vector3 movement = new Vector3(x, 0, z); 

        return movement.normalized;
    }
}

3.5. 정리

  • 이런식으로 캐릭터 이동을 구현하면 되는 것인데 단순 코드 구현이 가능하다.
  • 여기에서 점프나 이런 것을 추가하면 생각이 많아지겠지만 Transform으로 이동 구현은 잘 구축되어 있다는 것을 알 수 있다.
  • 이제 본격적으로 캐릭터 구현을 해볼 수 있는 셈
  • 거리를 측정해서 얻는 기능 Distance. 상대 위치에서 내 위치를 빼면 거리.
  • toTarget은 벡터, magnitude는 실질 크기.
  • 기능은 Vector3.Distance.
profile
아주몹시매우엄청대왕초보 게임 개발자의 끄적

0개의 댓글