Input System은 PlayerInput 컴포넌트를 사용하는 방법과 PlayerInput컴포넌트 없이 직접 InputAction을 생성하고 액션을 정의하는 방식이 있다. 새로운 스크립트를 만들고 PlayerCtrlByEvent로 이름변경한다.
UnityEngine.InputSystem 네임스페이스를 명시하고 이동과 공격 액션을 저장할 변수를 선언한다.
private InputAction moveAction; //액션 저장용 변수
private InputAction attackAction; //액션 저장용 변수
InputAction창에서 정의했던 액션을 InputAction()함수를 사용해 정의한다. moveAction은 WASD및 방향키등 여러가지 binding을 구현해야 하므로 moveAction.AddCompositeBinding()를 이용해 binding해야한다. ".With()" 메소드를 사용해 추가할 수 있다. 여기서는 방향키는 추가하지 않았다. 바인딩타입이 2DVector이므로 "2DVector"를 parameter로 사용하였다.
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");
attackAction = new InputAction("Attack",
InputActionType.Button,
"<Keyboard>/space");
액션의 preformed,cancled 이벤트에 람다식을 연결하는 방식은 이전과 같습니다. action의 활성화를 해주어야 합니다.
moveAction.Enable();
attackAction.Enable();
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;
// Start is called before the first frame update
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);
};
moveAction.canceled += ctx => {
moveDir = Vector3.zero;
anim.SetFloat("Movement", 0.0f);
};
//Move 액션의 활성화
moveAction.Enable();
//Attack 액션 생성
attackAction = new InputAction("Attack",
InputActionType.Button,
"<Keyboard>/space");
//Attack 액션의 performed 이벤트 연결
attackAction.performed += ctx => {
anim.SetTrigger("Attack");
};
//Attack 액션의 활성화
attackAction.Enable();
}
// Update is called once per frame
void Update() {
if (moveDir != Vector3.zero) {
//진행 방향으로 회전
transform.rotation = Quaternion.LookRotation(moveDir);
//회전한 후 전진방향으로 이동
transform.Translate(Vector3.forward * Time.deltaTime * 4.0f);
}
}
}
하이라키뷰에 warrior모델을 하나 더 끌어다 놓고 Animator컴포넌트에 AnimationController_Warrior컨트롤러를 연결하고 PlayerCtrlByEvent 스크립트도 연결한다.
2개의 warrior가 Input System의 구현은 다르지만 동일하게 동작하는 걸 확인할 수 있다.
Behavor 속성중 Invoke C Sharp Events 옵션은 C# 스크립트에서 직접 이벤트를 연결해 사용하는 방식이다. Player Input컴포넌트를 사용할 수도 있고 Input Action에셋과 Player Inptu 컴포넌트 없이 모두 다 스크립트로 처리할 수도 있다.
Input Behavior를 Invoke Unity Events로 선택하면 다음과 같이 유저가 정의한 액션인 Move와 Attack이 Unity Event타입의 속성으로 표시된다. 일반적은 UI Button이벤트를 연결하는 방식이다. 공토으로 발생하는 3개의 이벤트도 같이 표시된다.
이상태에서는 스크립트에서 연결할 이벤트가 없으므로 PlayerCtrl을 다음과 같이 수정해준다.
#region SEND_MESSAGE#region UNITY_EVENTS 는 코드의 영역을 정의하는 전처리기 이다.
Invoke Unity Events를 선택했을때 넘어오는 파라미터는 InputAction.CallbackContex타입으로 입력값은 ReadValude<T>() 함수를 이용해 전달 받는다. Move Action을 Vector2로 정의했기 때문에
Vector2 dir = ctx.ReadValue<Vector2>(); 와 같이 받아와야 한다. 나머지는 Send Messages 옵션과 동일하다
#pragma warning disable IDE0051 //함수나 변구를 정의후 사용하지 않는 경고문구를 비활성화하는 구문
using UnityEngine;
using UnityEngine.InputSystem; //Inputsystem사용을 위해 추가된 namespace
public class PlayerCtrl : MonoBehaviour {
private Animator anim;
private Vector3 moveDir;
void Start () {
anim = GetComponent<Animator>(); //Animator컴포넌트를 연결
}
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>();
moveDir = new Vector3(dir.x,0, dir.y);
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>();
moveDir = new Vector3(dir.x,0,dir.y);
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
}
스크립트에서 이벤트를 정의해 줬으니 이제 연결하자
Player Action 옆 ▶를 눌러 펼치면 Move(CallbackContex) List is Empty인데 +를 눌러준후 하이라키의 warrior를 끌어다 연결해준다. Attack도 끌어다 준다
Broadcast Messages의 경우, 하위 계층에 있는 오브젝트들까지 제어를 할 수 있다.
함수명이 "On + Actions name"으로 구성된다.
앞에서 Move와 Attack액션을 정의했기 때문에OnMove, OnAttack함수가 SendMessage를 통해 호출된다.
새로운 스크립트를 만들고 "PlayerCtrl"이라고 변경한다. Warrior에 연결해준다.
#pragma warning disable IDE0051 //함수나 변구를 정의후 사용하지 않는 경고문구를 비활성화하는 구문
using UnityEngine;
using UnityEngine.InputSystem; //Inputsystem사용을 위해 추가된 namespace
public class PlayerCtrl : MonoBehaviour {
// Start is called before the first frame update
void OnMove(InputValue value) {
Vector2 dir = value.Get<Vector2>();
Debug.Log($"Move = ({dir.x},{dir.y})");
}
void OnAttack() {
Debug.Log("Attack");
}
}
이제 키입력을 통해 애니메이션과 이동 처리 로직을 구현한다. PlayerCtrl 스크립트를 다음과 같이 수정한다.
스크립트는 키입력의 이벤트를 통해 애니메이션을 컨트롤 한다.
애니메이션은 Idel, Run, Attack 3가지 뿐이고 방향은 rotation시킨다.
float Movement파라메터를 이용 Idle과 Run애니메이션을 선택하고bool Attack이 Attack애니메이션을 트리거해준다
#pragma warning disable IDE0051 //함수나 변구를 정의후 사용하지 않는 경고문구를 비활성화하는 구문
using UnityEngine;
using UnityEngine.InputSystem; //Inputsystem사용을 위해 추가된 namespace
public class PlayerCtrl : MonoBehaviour {
private Animator anim;
private Vector3 moveDir;
void Start () {
anim = GetComponent<Animator>(); //Animator컴포넌트를 연결
}
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>();
moveDir = new Vector3(dir.x,0, dir.y);
anim.SetFloat("Movement", dir.magnitude);
Debug.Log($"Move = ({dir.x},{dir.y})");
}
void OnAttack() {
Debug.Log("Attack");
anim.SetTrigger("Attack");
}
}
새로운 Input System은 기존 Input Class와는 다른게 외부 입력장치와 동작하는 코드를 완벽하게 분리할 수 있다. 코드에서 어떤 입력장치로 부터입력됐는지를 확인할 필요가 없다. 다른 입력장치로 변경했을 경우 Biding 정보만 변경해 적용할 수 있기 때문에 코드의 수정없이 쉽게 적용이 가능하다.
입력 시스템 워크플로우
새로운 입력 시스템을 사용하는 방법은 다음 4가지 방법이 있습니다. 스크립트와 Asset을 적절히 같이 사용할 수 있습니다.
Directly Reading Device States
Using Embedded Actions
Using an Actions Asset
Using an Actions Asset and PlayerInput Component
Input System의 구조
실습 텔플릿은 3D로 이름은 "Warrior"으로 새로운 프로젝트를 만들었다. (아래 New Input은 오타)
Project폴더에서 기존 Scenes를 01.Scenes로 변경하고 02.Scripts폴더를 생성한다.
Package Manager 왼쪽 상단 Packages: In Project 팝업을 눌러 Unity Registry를 선택한다.
아래 창에서 Input System를 찾아 오른쪽 Install을 눌러 설치한다.
설치가 끝나면 다이얼로그박스가 뜨는데 yes해주면 유니티가 재시작된다.
Active Input Handling 확인
메뉴[Edit]{Project Setting][Player]인스펙터뷰의 Other Settings안의 아래쪽 Active Input Handling이 Both로 잘되어 있다.
테스트 환경 제작
내려받은 Resources폴더에서 다음 2가지 패키지를 임포트 한다.
Resources/Models: Warrior 패키지
Resources/Textures:Images 패키지
하이라키뷰에서 Palne을 추가하고 Transform Scale을 (5,5,5)로 입력 가로, 세로의 크기를 50cm, 50cm로 설정한다.
프로젝트 뷰의 Images/Magerials 폴더의 GridEmissive 머터리얼을 Plane에 연결한다.
프로젝트뷰의 Warrior/Models폴더의 Warrior_bindpose 모델을 하이라키뷰에 추가한다.
인스펙터뷰에서 Animator컴포넌트의 Controller Item의 ◉브라우저를 눌러 AnimationController_Warrior를 연결한다.
Input Action에셋
새로운 Input System을 사용하기 위해서는 먼저 Input Action 에셋을 생성해야한다. Input Action은 액션과 바인딩 정보를 정의하는 에셋으로 다음과 같이 2가지 방법으로 생성할수 있다.
프로젝트뷰의+를 눌러Input Action을 선택
Warrior캐릭터에 Player Input 스크립터를 추가한 후 Input Action을 생성하는 Crate Action버튼을 클릭
02.Scripts하위에 InputAction폴더를 생성한후 Input Action에셋을 생성한다. 이름은 MainAction으로 바꾼다.
MainAction을 더블클릭하면 InputActions창이 열린다.
이 화면에 보이는 용어들을 짚어보자.
Control Scheme : 특정 키 입력을 사용하기 위해 필요한 요구사항의 집합. 예를 들어 키보드+마우스나, 게임패드+마우스처럼 특정 입력 도구 집합을 사용할 때, Action에 바인딩된 키를 다르게 할 수 있다. Scheme가 달라도 ActionMap과 Action 구조는 항상 같다.
Action Maps : UI에서 작동하는 키 입력 방식과 플레이어에게 필요한 키 입력 방식은 다르다. 작동 중인 Action Map을 변경함으로써 그걸 구현할 수 있다.
Action : 실제 액션 이름과 그에 할당된 키 값은 이곳에 작성한다.
Control Schemes
MainActions창 좌상을 보면 No Control Scheme을 눌러 Add Control Scheme을 선택 새로운 Scheme을 만들자