강의를 들으면서 따라하기만 해서 따라는 했지만 결국 제대로 이해하지 못한 내용이나, 새롭게 알게 된 내용에 대해 정리했다.
목 차
Input System
Input Actions 설정하기
- Input System 패키지를 다운받는다.
- Input Actions Asset을 생성한다. (Input 폴더)
- Action Maps 및 Actions 추가:
- Action Map 예시: 캐릭터 컨트롤, 메뉴 탐색 등.
- Actions 예시: Move, Jump, Fire 등.
- Bindings 설정: 각 Action에 대해 어떤 키를 눌러야 상호작용할 것인지 등을 정하는 작업.
- Control Schemes: 다양한 입력장치를 위한 것. (키보드&마우스 / 게임패드 등)
스크립트에서 Input Actions 사용하기
다음은 내가 인터넷에서 Input Actions를 적용하는 법을 찾았을 때 나온, 참조를 통해 Input Actions를 받아오는 방법이다.
- Input Actions를 스크립트에서 참조해야 한다.
using UnityEngine.InputSystem; // Input System 네임스페이스 추가 필요. public class PlayerController : MonoBehaviour { //YourInputActionsClassName은 내가 위의 과정을 따라 만든 InputAction의 이름. private YourInputActionsClassName inputActions; // Input Actions 인스턴스 private void Awake() { // Input Actions 인스턴스 생성 inputActions = new YourInputActionsClassName(); } }
- 스크립트에서 참조한 Input Actions를 OnEnable과 OnDisable 함수 등을 통해 활성화 시에만 입력을 받도록 한다.
private void OnEnable() { // Input Actions 활성화 inputActions.Enable(); } private void OnDisable() { // Input Actions 비활성화 inputActions.Disable(); }
- 입력 이벤트에 대한 콜백을 연결한다.
//Awake 함수 안에 구현. inputActions.Player.Move.performed += OnMove; inputActions.Player.Move.canceled += OnMove;
- 실제 움직임을 적용할 메서드 OnMove 등을 만든다.
private void OnMove(InputAction.CallbackContext context) { // 움직임 처리 로직 Vector2 moveInput = context.ReadValue<Vector2>().normalized; }
#CallbackContext
위의 설명에서 inputActions.Player.Move 다음에 나오는 performed와 canceled 등을 칭한다.
또 4번의 OnMove 함수 인자에서도 볼 수 있다.
이 객체는 사용자의 입력을 했을 때와 관련된 상세정보를 담고 있다.
자료형은 InputAction.CallbackContext 이고, 이렇게 선언된 context 뒤에 .을 붙여 프로퍼티처럼 불러올 수 있다.
- action: 이벤트를 발생시킨 액션의 정보를 전달한다.
이때 정보 = Input Action을 만들 때 두 번째 줄에 있던 것들: 액션의 이름(Move), 바인딩, 활성화 상태 등. - readValue: 사용자의 입력 값에 접근하여 처리한다. 주로 이동에 많이 사용된다.
위의 OnMove 함수에 그 예가 있다. - phase: 입력 액션의 상태를 확인하여 그에 맞는 처리를 한다.
- phase의 종류:
Started: 입력이 시작될 때 발생한다.
Performed: 입력이 완전히 수행되었을 때 발생한다. (인식 성공)
Canceled: 입력이 취소될 때 발생한다. (입력 중단 or 다른 조건에 의해 무효화됨.)
Waiting: 액션 설정에서 지연(delay) 설정이 있을 때 입력 완료까지 대기 상태임을 나타낸다.
Disabled: 액션이 비활성화 상태라는 것을 알린다. - 이것들은 위의 3번 예시처럼 사용하거나,
context.phase == InputActionPhase.Performed 등과 같은 조건문으로 활용가능하다.
- phase의 종류:
- time: 입력 이벤트가 발생한 시간을 확인한다.
- control: 이벤트를 발생시킨 특정 입력장치를 확인한다.
이를 통해 어떤 키 또는 버튼이 눌렸는지 알 수 있다.
Player Input 활용
위의 내용이 우리 강의와 달라 조금 더 찾아보니, Player Input 컴포넌트의 유무가 차이를 만든다는 것을 발견했다. 그래서 여기서부터는 송지원 튜터님이 설명하신 그 방법으로 다시 설명한다. 또한, 이번 과제에도 이 방법을 우선 활용해려 한다. 이 설명들은 GPT를 참고하여 작성햇다.
- Player Input 컴포넌트를 Player 게임 오브젝트에 추가한 후, Input Actions(TopDownController2D)를 컴포넌트의 Actions 삽입한다.
- Input Action의 Action 두 개(현재로써는 Move와 Look)을 OnMove와 OnLook 메소드에 연결한다. 이때, InputValue를 자동으로 CallbackContext로 전환해줄 것이다.
참고로, GPT는 OnMove와 OnLook의 인자를 InputValue 대신 InputAction.CallbackContext로 하라고 했다. 만약 그렇게 바꾼다면, 내 코드는 다음과 같아져야 할 것이다:
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerInputController : TopDownController
{
private Camera camera;
private PlayerControls controls; // Input Actions 클래스 //<1>
private void Awake()
{
camera = Camera.main;
// Input Actions 인스턴스 생성
controls = new PlayerControls();
// 입력 이벤트에 메소드 연결
controls.Player.Move.performed += context => OnMove(context); //<2>
controls.Player.Look.performed += context => OnLook(context);
}
public void OnMove(InputAction.CallbackContext context)
{
Vector2 moveInput = context.ReadValue<Vector2>().normalized;
CallMoveEvent(moveInput); //TopDownController의 이벤트 발생.
}
public void OnLook(InputAction.CallbackContext context)
{
Vector2 newAim = context.ReadValue<Vector2>();
Vector2 worldPos = camera.ScreenToWorldPoint(newAim);
newAim = (worldPos - (Vector2)transform.position).normalized;
if (newAim.magnitude >= .9f)
{
CallLookEvent(newAim);
}
}
}
<1>: PlayerControls는 Input System에 기본적으로 딸려오는 클래스이며 게임 내 입력을 관리하기 위한 모든 액션 맵과 액션을 포함한다. 이 클래스의 인스턴스를 통해 (객체 이름).(액션 맵 이름) 에 있는 Action에 대한 이벤트 리스너를 추가할 수 있다.
<2>: 이 코드의 의미는 다음과 같다.
- controls.Player.Move.performed 를 예로 들겠다.
- controls: PlayerControls의 인스턴스. 모든 Input Action을 포함하고 있다.
- Player: 내가 만든 특정 "액션 맵".
- Move: 내가 만든 액션 맵의 "액션" 중 하나.
- performed: Input System에서 제공하는 CallbackContext 중 phase의 이벤트.
- 이벤트 리스너 작동하는 법
- performed는 바로 위의 설명과 같이 이벤트이다. 액션이 활성화 상태이기만 하면 Move 기준 W, A, S, D 중 하나를 누르는 순간 해당 이벤트가 트리거될 것이다.
- += 연산자는 이벤트의 구독으로, performed 이벤트 이후 호출될 다음 함수가 기호 뒤에 들어가게 된다. 여러 함수가 호출되어야 한다면, 순서에 맞게 같은 방식으로 추가해주면 된다.
- += 를 통해 performed와 연결된 리스너에는 InputAction.CallbackContext 타입의 데이터가 제공된다. 여기서 제공된 데이터는 += 뒤의 람다식의 parameter에 들어가게 된다.
- 이 람다식을 통해 OnMove 메서드가 이벤트와 연결된다. 그리고 이벤트가 발생한다면 이 함수 역시 실행된다.
참고로, 람다 식을 사용하지 않으면 그냥 OnMovePerformed; 가 += 뒤에 오고, 메서드의 인자는 InputAction.CallbackContext context로 똑같다.
...공부하다 보니 내용이 너무 방대해졌는데, 알고보니 강의에서 InputValue를 쓴 건 그냥 그게 훨씬 쉽고 간단하기 때문이고, 내가 배운 이 내용들은 숙련 주차 이상부터 준비가 되어 있다고 한다. 뭐 예습했다 치고 넘어가자.
<3>: Action 역시 아직도! 아직도 이해가 잘 되지 않아 조금 더 자세히 공부해보았다.
- Event 선언(TopDownController 클래스): Action은 델리게이트 타입 중 하나로, 반환값이 없다. (있는건 func)
public event Action<Vector2> OnMoveEvent;
- Vector2 타입의 매개변수 하나를 받는 메서드를 참조할 수 있다.
- OnMoveEvent가 이벤트의 이름이다.
- Event 호출(TopDownController 클래스): OnMoveEvent와 연결된 이벤트들을 줄줄이 실행해준다. 만약 연결된 이벤트가 없다면, 아무런 동작도 하지 않는다.
이제 이 함수를 어딘선가 부른다면, OnMoveEvent와 연결된 함수들도 줄줄이 실행될 것이다.public void CallMoveEvent(Vector2 direction) { OnMoveEvent?.Invoke(direction); }
- Event와 연결된 메서드 실행(TopDownMovement 클래스): 위의 OnMoveEvent와 연결된 메서드들이 여기에 있다.
private void Start() { // OnMoveEvent에 Move를 호출하라고 등록함 movementController.OnMoveEvent += Move; } private void Move(Vector2 direction) { // 이동방향만 정해두고 실제로 움직이지는 않음. // 움직이는 것은 물리 업데이트에서 진행(rigidbody가 물리니까) movementDirection = direction; }
즉, 여기까지의 메커니즘을 보면 다음과 같다.
- WASD를 통해 키를 입력받는다.
- 이 키를 눌렀다는 이벤트가 PlayerInputController의 OnMove 함수로 전달되고, OnMove 함수는 입력받은 Vector2를 다시 CallMoveEvent로 전달한다.
- TopDownController 에서 CallMoveEvent 안의 OnMoveEvent 이벤트를 발생시킨다.
- OnMoveEvent의 리스너들은 TopDownMovement에 있는데, 여기의 Move 메서드가 바로 그 리스너이다. 이 Move메서드는 클래스에 방향만 어느 쪽이라 지정해주는 역할을 한다.
- 이렇게 입력된 방향은 같은 클래스의 FixedUpdate에서 ApplyMovement를 통해 이 메서드로 전달이 되고, 다시 ApplyMovement 메서드는 direction에 속도 보정치 5를 곱해준 후(추후 변수로 대체 예정) 해당 클래스에 선언된 Rigidbody2D 객체에 대입되어 실제 플레이어의 움직임을 유발한다.
- 이 때, 키 입력이 없다면 InputValue를 받아올 때 자동으로 Vector2는 zero로 설정이 되고, 이에 따라 키 입력이 없을 때 캐릭터 역시 그 자리에 멈출 수 있게 된다.
좋아! 아마도 이제 Input System을 통한 플레이어의 움직임은 완벽히(?) 이해했어! 라고 믿고 싶다.
플레이어 애니메이션
마우스 방향에 따라 플레이어 방향 바꾸기
우선, 나는 Unity Asset Store에서 2D Character - Astronaut 라는 에셋을 활용해서 플레이어를 만들었다. 여기에는 귀여운 우주인 캐릭터가 각 모션마다 세 방향: 앞 뒤 옆의 이미지가 약 21프레임 정도로 준비가 되어 잇었다. 이걸 끌어서 Animation 클립까지는 만들었지만, 어떻게 해야 내 입력을 통해 Animation 종류를 바꾸는지에 대해 약간 헷갈리는 부분이 있어 찾아보았다.
#방법1: Animation 클립 이름으로 불러오기.
가장 간단한 방법 중 하나는 애니메이터 컨트롤러에 그냥 여러 클립을 추가해놓고 Animation.Play("PlayerFront")와 같은 식으로 불러오는 것이다. 또한, Play 메서드는 두 번째 인자로 layer, 세 번째 인자로 플레이 시간의 비례를 받아올 수 있다.
#방법2: Parameter을 통해 전달하기
애니메이터 컨트롤러에서 Parameters 탭을 클릭하고, 필요한 변수들을 만든다.
Animator.SetBool (Bool 자리에는 맞는 자료형) 한 다음에 첫 번째 인자로는 파라미터의 이름, 두 번째에는 그 파라미터에 들어갈 값을 입력하면 된다.
마무리
으에엥.. 너무 피곤하다.
정말 한게 없는 줄 알았는데 곰곰히 생각해보니 벌써 필수 요구사항을 거의 다 끝낸 나 자신을 발견했다. 뭐지...?
'내일배움캠프 > [P4-Solo.] PartyRoom' 카테고리의 다른 글
[TIL24.05.16] Unity 개인프로젝트 입문 해설 (0) | 2024.05.16 |
---|---|
[TIL 24.05.13] Unity 개인프로젝트 입문(2) (0) | 2024.05.13 |