위키북스 출판사 이재현 저자님의 '절대강좌! 유니티' 책을 참고하여 필기한 내용입니다.
Player Input 컴포넌트
: Input Actions 에서 정의한 액션이 발생했을 때 코드와 연결해 해당 로직을 실행시킬 수 있는 기능을 처리
주인공 캐릭터를 선택하고 Player Input 컴포넌트를 추가한다.
Action 속성
미리 만들어 놓은 MainActions Actions 를 연결한다.
Actions 속성에 Input Actions 에셋을 연결하면 바로 밑에 Default Scheme과 Default Map 항목이 표시 됨
Behavior 속성
: Input Actions 에셋에 정의한 액션이 발생했을 때 코드의 함수를 어떻게 실행시킬 것인지를 결정하는 속성
Behavior 옵션 | 설명 |
Send Message | Player Input 컴포넌트가 있는 게임오브젝트에 SendMessage 함수를 이용하여 호출 |
Broadcast Message | Send Message 와 동일하지만 BroadcastMessage 함수를 이용해 하위에 있는 게임오브젝트의 함수도 호출 |
Invoke Unity Events | 액션별로 이벤트 연결 인터페이스가 생성되고 각각 호출하려는 함수를 연결해 사용 |
Invoke C Sharp Event | C# 스크립트에서 이벤트를 직접 연결해 사용 |
위 네가지의 Behavior 속성은 입력장치의 연결, 해지, 변경사항에 대해 다음과 같은 이벤트를 기본으로 호출
- OnDeviceLost
- OnDeviceRegained
- OnControlsChanged
Behavior - Send Messages 옵션
void On{액션명}()
PlayerCtrl이라는 새로운 스크립트를 생성하고 아래와 같이 작성한다.
#pragma warning disable IDE0051
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCtrl : MonoBehaviour
{
void OnMove(InputValue value)
{
Vector2 dir = value.Get<Vector2>();
Debug.Log($"Move = ({dir.x},{dir.y})");
}
void OnAttack()
{
Debug.Log("Attack");
}
}
#pragma 전처리기를 사용하여 IDE0051 경고문구를 비활서오하 하는 구문을 추가
IDE0051은 함수나 변수를 정의하고 다른 코드에서 사용하지 않았을 때 Visual Studio 에서 경고를 표시함
#pragma warning disable IDE0051
OnMove 함수의 파라미터 타입은 InputValue 클래스로 Get(), Get<T>() 형식으로 값을 전달 받음
Move 은 Vector2 로 정의 했기 때문에 Get<Vector 2>()를 사용함
void OnMove(InputValue value)
{
Vector2 dir = value.Get<Vector2>();
Debug.Log($"Move = ({dir.x},{dir.y})");
}
OnAttack 함수는 Button을 설정했기 때문에 키가 눌리면 호출되어 인자 없음
void OnAttack()
{
Debug.Log("Attack");
}
정상적으로 작동 되는 것이 확인 되었다면 애니메이션과 연결한 스크립트를 새로 작성한다.
#pragma warning disable IDE0051
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCtrl : MonoBehaviour
{
private Animator anim;
private new Transform transform;
private Vector3 moveDir;
private void Start()
{
anim = GetComponent<Animator>();
transform = GetComponent<Transform>();
}
private void Update()
{
if(moveDir != Vector3.zero)
{
// 진행 방향으로 회전
transform.rotation = Quaternion.LookRotation(moveDir);
// 회전한 후 전진 방향으로 이동
transform.Translate(Vector3.forward * Time.deltaTime * 4.0f);
}
}
void OnMove(InputValue value)
{
Vector2 dir = value.Get<Vector2>();
// 2차원 좌표를 3차원 좌표로 변환
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior _Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
Debug.Log($"Move = ({dir.x},{dir.y})");
}
void OnAttack()
{
Debug.Log("Attack");
anim.SetTrigger("Attack");
}
}
Behavior - Invoke Unity Events 옵션
Player 의 Input 의 Behavior 속성을 Invoke Unity Events로 선택하면 정의한 두 개의 액션인 Move, Attack이 Unity Event 타입의 속성으로 표시됨
공통으로 발생하는 3개의 이벤트도 같이 표시된다.
Events 의 Move, Attack 액션 명이 표시되며 괄호 안에 CallbackContext 라는 파라미터 타입이 표시됨
앞서 만들었떤 함수를 연결할 수 없기 때문에 다음과 같은 형식의 새로운 함수를 정의해야됨
void On{액션명}(InputAction.CallbackContext context)
#pragma warning disable IDE0051
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCtrl : MonoBehaviour
{
private Animator anim;
private new Transform transform;
private Vector3 moveDir;
private void Start()
{
anim = GetComponent<Animator>();
transform = GetComponent<Transform>();
}
private void Update()
{
if(moveDir != Vector3.zero)
{
// 진행 방향으로 회전
transform.rotation = Quaternion.LookRotation(moveDir);
// 회전한 후 전진 방향으로 이동
transform.Translate(Vector3.forward * Time.deltaTime * 4.0f);
}
}
#region SEND_MESSAGE
void OnMove(InputValue value)
{
Vector2 dir = value.Get<Vector2>();
// 2차원 좌표를 3차원 좌표로 변환
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior _Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
Debug.Log($"Move = ({dir.x},{dir.y})");
}
void OnAttack()
{
Debug.Log("Attack");
anim.SetTrigger("Attack");
}
#endregion
#region UNITY_EVENTS
public void OnMove(InputAction.CallbackContext ctx)
{
Vector2 dir = ctx.ReadValue<Vector2>();
// 2차원 좌표를 3차원 좌표로 벼환
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior_Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
}
public void OnAttack(InputAction.CallbackContext ctx)
{
Debug.Log($"ctx.phase={ctx.phase}");
if (ctx.performed)
{
Debug.Log("Attack");
anim.SetTrigger("Attack");
}
}
#endregion
}
#region ~ #endregion 전처리기는 코드의 영역을 정의하는 것
특정 영역을 Collapse 할 수 있음
코드의 라인이 많을 때나 특정 로직을 그룹화 할 때 편리한 방법
Invoke Unity Events 옵션을 선택했을 때 넘어오는 파라미터는 InputAction.CallvackContext 타입으로 입력값은 ReadValue<T>() 함수를 이용하여 전달 받음
InputActions에서 Move 액션을 Vector2 타입으로 정의했기 때문에 2차원 벡터 타입으로 받아와야됨
public void OnMove(InputAction.CallbackContext ctx)
{
Vector2 dir = ctx.ReadValue<Vector2>();
// 2차원 좌표를 3차원 좌표로 벼환
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior_Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
}
Invoke Unity Events 옵셩르 사용해 연결한 함수는 총 3번 호출됨
Input Actions 에 정의한 액션은 시작, 실행, 취소의 콜백 함수를 가각 한 번씩 호출함
어떤 상태로 호출됐는지에 대한 정보는 CallbackContext.phase 속성을 통해 알 수 있음
InputActionPhase 열거형으로 다음과 같이 5개가 정의 됨
InputActionPhase.Started
InputActionPhase.Performed
InputActionPhase.Canceled
InputActionPhase.Disabled
InputActionPhase.Waiting
OnAttack 함수에서 InputActionPhase.Performed 일때만 공격 애니메이션을 실행하도록 처리하지 않으면 공격 애니메이션이 3번 발생하게 됨
Performed 속성은 실행 상태일 때 true값을 반환함
3번 호출하는지 확인하기 위해서 Debug로 확인
public void OnAttack(InputAction.CallbackContext ctx)
{
Debug.Log($"ctx.phase={ctx.phase}");
if (ctx.performed)
{
Debug.Log("Attack");
anim.SetTrigger("Attack");
}
}
이렇게 연결하고 실행해보기
Behavior - Invoke C Sharp Events
C# 스크립트에서 직접 이벤트를 연결해 사용하는 방식
Player Input 컴포넌트를 사용할 수도 있고 Input Actions 에셋과 Player Input 컴포넌트 없이 모두 다 스크립트로 처리 가능함
PlayerCtrl 스크립트 수정
#pragma warning disable IDE0051
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCtrl : MonoBehaviour
{
private Animator anim;
private new Transform transform;
private Vector3 moveDir;
private PlayerInput playerInput;
private InputActionMap mainActionMap;
private InputAction moveAction;
private InputAction attackAction;
private void Start()
{
anim = GetComponent<Animator>();
transform = GetComponent<Transform>();
playerInput = GetComponent<PlayerInput>();
// ActionMap 추출
mainActionMap = playerInput.actions.FindActionMap("PlayerActions");
// Move, Attack 액션 추출
moveAction = mainActionMap.FindAction("Move");
attackAction = mainActionMap.FindAction("Attack");
// Move 액션의 performed 이벤트 연결
moveAction.performed += ctx =>
{
Vector2 dir = ctx.ReadValue<Vector2>();
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior_Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
};
// Move 액션의 canceled 이벤트 연결
moveAction.canceled += ctx =>
{
moveDir = Vector3.zero;
// Warrior_Run 애니메이션 정지
anim.SetFloat("Movement", 0.0f);
};
// Attack 액션의 performed 이벤트 연결
attackAction.performed += ctx =>
{
Debug.Log("Attack by c# event");
anim.SetTrigger("Attack");
};
}
private void Update()
{
if(moveDir != Vector3.zero)
{
// 진행 방향으로 회전
transform.rotation = Quaternion.LookRotation(moveDir);
// 회전한 후 전진 방향으로 이동
transform.Translate(Vector3.forward * Time.deltaTime * 4.0f);
}
}
#region SEND_MESSAGE
void OnMove(InputValue value)
{
Vector2 dir = value.Get<Vector2>();
// 2차원 좌표를 3차원 좌표로 변환
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior _Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
Debug.Log($"Move = ({dir.x},{dir.y})");
}
void OnAttack()
{
Debug.Log("Attack");
anim.SetTrigger("Attack");
}
#endregion
#region UNITY_EVENTS
public void OnMove(InputAction.CallbackContext ctx)
{
Vector2 dir = ctx.ReadValue<Vector2>();
// 2차원 좌표를 3차원 좌표로 벼환
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior_Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
}
public void OnAttack(InputAction.CallbackContext ctx)
{
Debug.Log($"ctx.phase={ctx.phase}");
if (ctx.performed)
{
Debug.Log("Attack");
anim.SetTrigger("Attack");
}
}
#endregion
}
변수 선언부에 PlayerInput 컴포넌트, 액션 맵, 액션을 저장할 변수 선언 후 Start 함수에서 정보 대입
private PlayerInput playerInput;
private InputActionMap mainActionMap;
private InputAction moveAction;
private InputAction attackAction;
playerInput 변수에는 GetComponent로 PlayerInput 컴포넌트를 저장하고 액션 맵을 FindActionMap함수를 사용하여 가져옴
FindActionMap 함수에 전달할 파라미터는 MainActions 에서 정의한 액션맵으로 문자열로 전달함
playerInput = GetComponent<PlayerInput>();
// ActionMap 추출
mainActionMap = playerInput.actions.FindActionMap("PlayerActions");
액션 맵을 추출했으면 해당 액션 맵에 정의한 액션(Move,Attack)을 FindAction ("액션 명") 함수를 사용하여 변수에 대입함
// Move, Attack 액션 추출
moveAction = mainActionMap.FindAction("Move");
attackAction = mainActionMap.FindAction("Attack");
추출한 Move 액션의 performed 이벤트와 canceled 이벤트가 발생했을 때의 로직을 람다식으로 지정함
// Move 액션의 performed 이벤트 연결
moveAction.performed += ctx =>
{
Vector2 dir = ctx.ReadValue<Vector2>();
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior_Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
};
// Move 액션의 canceled 이벤트 연결
moveAction.canceled += ctx =>
{
moveDir = Vector3.zero;
// Warrior_Run 애니메이션 정지
anim.SetFloat("Movement", 0.0f);
};
Attack 액션의 performed 이벤트만 사용함
// Attack 액션의 performed 이벤트 연결
attackAction.performed += ctx =>
{
Debug.Log("Attack by c# event");
anim.SetTrigger("Attack");
};
Direct Binding
PlayerInput 컴포넌트 없이 직접 스크립트에서 InputAction을 생성하고 액션을 정의하는 방식
새로운 스크립트인 PlayerCtrlByEvent를 생성
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerCtrlByEvent : MonoBehaviour
{
private InputAction moveAction;
private InputAction attackAction;
private Animator anim;
private Vector3 moveDir;
private void Start()
{
anim = GetComponent<Animator>();
// Move 액션 생성 및 타입 설정
moveAction = new InputAction("Move", InputActionType.Value);
// Move 액션의 복합 바인딩 정보 정의
moveAction.AddCompositeBinding("2DVector")
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
// Move 액션의 performed, canceled 이벤트 연결
moveAction.performed += ctx =>
{
Vector2 dir = ctx.ReadValue<Vector2>();
moveDir = new Vector3(dir.x, 0, dir.y);
// Warrior_Run 애니메이션 실행
anim.SetFloat("Movement", dir.magnitude);
};
// Move 액션의 canceled 이벤트 연결
moveAction.canceled += ctx =>
{
moveDir = Vector3.zero;
// Warrior_Run 애니메이션 정지
anim.SetFloat("Movement", 0.0f);
};
// Move 액션의 활성화
moveAction.Enable();
// Attack 액션 생성
attackAction = new InputAction("Attack", InputActionType.Button, "<Keyboard>/space");
// Attack 액션의 performed 이벤트 연결
attackAction.performed += ctx =>
{
Debug.Log("Attack by c# event");
anim.SetTrigger("Attack");
};
// Attack 액션의 활성화
attackAction.Enable();
}
private void Update()
{
if(moveDir != Vector3.zero)
{
// 진행 방향으로 회전
transform.rotation = Quaternion.LookRotation(moveDir);
// 회전한 후 전진 방향으로 이동
transform.Translate(Vector3.forward * Time.deltaTime * 4.0f);
}
}
}
InputActions 창에서 정의했던 Move, Attack 액션을 직접 생성함
액션 = new InputAction("액션명", 액션_타입)
// Move 액션 생성 및 타입 설정
moveAction = new InputAction("Move", InputActionType.Value);
WASD 같은 복합적인 바인딩은 아래의 함수르 사용하여 구성하고 .With() 구문으로 필요한 만큼 추가 가능
AddCompositeBinding("복합 바인딩 타입").With("바인딩 명","바인딩 정보")
// Move 액션의 복합 바인딩 정보 정의
moveAction.AddCompositeBinding("2DVector")
.With("Up", "<Keyboard>/w")
.With("Down", "<Keyboard>/s")
.With("Left", "<Keyboard>/a")
.With("Right", "<Keyboard>/d");
복합 바인딩 타입을 다음과 같다.
- 1DAxis(또는 Axis)
- 2DVector(또는 Dpad)
- 3DVector
- OneModifier
- TwoModifiers
Input Bindings | Input System | 1.1.1 (unity3d.com)
Input Bindings | Input System | 1.1.1
Input Bindings An InputBinding represents a connection between an Action and one or more Controls identified by a Control path. An Action can have an arbitrary number of Bindings pointed at it. Multiple Bindings can reference the same Control. Each Binding
docs.unity3d.com
preformed, canceled 이벤트를 연결하는 방법은 이전과 동일함
액션의 선언과 이벤트 연결을 완료했으면 반드시 해당 액션을 활성화 해야됨
// Move 액션의 활성화
moveAction.Enable();
작성을 완료 했다면 Warrior_bindpos 모델을 하나 더 추가하고 방금 작성한 스크립트와 Animator를 연결해준다.
Input Debug
입력 장치로부터 전달되는 값을 모니터링할 수 있는 기능
에디트 모드에서 다양한 외부 입력 장치의 정보를 확인 가능함
입력값을 녹화, 저장, 로드 할 수 있음
책에는 적혀있지 않았는데
Window - Analysis - InputDebugger 를 통해 들어가면확인 가능하다.
'Unity 강의 > Unity Course(2) - 절대강좌! 유니티' 카테고리의 다른 글
[Unity Course 2] 15. 포톤 클라우드를 활용한 네트워크 게임 2 (0) | 2024.07.01 |
---|---|
[Unity Course 2] 15. 포톤 클라우드를 활용한 네트워크 게임 1 (0) | 2024.06.29 |
[Unity Course 2] 14. Input System - 새로운 Input System의 특징 (0) | 2024.06.29 |
[Unity Course 2] 13. 오클루전 컬링 (0) | 2024.06.29 |
[Unity Course 2] 12. 씬 관리 (0) | 2024.06.28 |