-
DNF Input System게임 클라이언트 개발/MakeDNF 2023. 12. 11. 21:51
MyDNF 프로젝트를 시작했던 이유는 단 한 가지였다. "지하철에서도 던파를 핸드폰으로 플레이할 수 있음 진짜 재밌겠다."라고 생각했고, 모바일 버전 던전 앤 파이터를 만들고자 했다.
하지만 모바일 버전 게임이라고 꼭 모바일에서만 플레이하는가에 대해 생각해보니, 나만 하더라도 '녹스'라는 애뮬레이터 프로그램을 사용하여 부계정을 PC에서도 돌렸었다. 즉, 하나의 앱을 여러 플랫폼에서 플레이할 수 있어야 함을 의미한다.
주로 세븐나이츠의 부계정을 PC에서 플레이했는데, 세븐나이츠는 캐릭터 컨트롤 방식이 스킬 UI를 클릭하는 것이 전부여서 PC를 위한 Input System을 구축할 필요가 없다.
하지만, 던파의 경우 모바일용 Input System으로 PC에서 플레이한다면 마우스로 조이스틱과 스킬을 사용해야하니 플레이가 불가능하다.
모바일 버전과 PC 버전을 따로 배포할 수 있다면 Input System을 따로 구축할 필요가 없지만, 하나의 앱으로 다양한 플랫폼을 대응해야했기에 각 디바이스의 입력을 하나의 입력값으로 치환해주는 Input System을 구축했다.
플레이어의 입력의 종류를 방향 버튼의 입력값 PlayerJoystick과 방향 버튼을 제외한 나머지 버튼 입력값 PlayerButton 두 가지로 분리했고, 추상 클래스로 선언하여 플렛폼 별 입력을 해당 클래스를 상속받아 구현할 수 있도록 설계했다.
PlayerJoystick
플레이어의 디바이스 플랫폼 별 방향 입력값(Vector2)을 DNF 좌표계의 이동 방향 벡터값(Vector3)으로 치환해주는 클래스이다.
PC - Keyboard
Keyboard의 방향 입력의 경우 Unity의 Input Manager를 사용했었다. Input 클래스의 GetAxisRaw 함수를 통해 간단하게 방향 입력을 구현했었다.
private void Update() { float h = Input.GetAxisRaw("Horizontal"); float v = Input.GetAxisRaw("Vertical"); moveDirection = new Vector3(h, 0f, v); }
다만 이렇게 구현할 경우 문제점이 두 가지 발생했다.
- 방향키 입력을 런타임에 플레이어가 설정할 수 없다.
- GetAxisRaw의 파라미터로 받는 키 이름은 Unity의 Input Manager를 통해 설정한다.
- 다만, Unity의 Input Manager의 키는 Unity Editor에서만 수정할 수 있어 플레이어가 방향키를 수정할 수 없다.
- 즉, Input 클래스를 이용한다면, 키 세팅 기능을 구현할 수 없다.
- 반대 방향의 방향키를 동시에 입력할 경우, 캐릭터가 움직이지 않는다.
- 던전 앤 파이터에선 좌측 방향키를 누른 상태로 우측 방향키를 누르면 캐릭터가 우측으로 이동한다.
- 다시 우측 방향키를 누르지 않으면 캐릭터가 좌측으로 이동한다.
- 하지만, GetAxisRaw로 구현한다면, 좌측 방향키를 누른 상태로 우측 방향키를 누르면 x축의 방향으로 0이 반환되어 캐릭터가 움직이지 않는다.
이를 해결하기 위해 방향을 입력할 KeyCode를 직접 설정할 수 있도록 변수로 선언했고, Update 문에서 방향 입력을 확인하고 각 축마다 마지막으로 눌린 방향을 저장하여 반대 방향의 키가 눌리면 반대 방향으로 이동하도록 구현했다.
Mobile - Screen
Screen의 방향 입력의 경우 IPointerDownHandler, IDragHandler, IPointerUpHandler 인터페이스를 활용하여 방향 입력을 구현했다.
Screen Joystick으로 게임할 때 불편했던 점이 화면에 손을 때지 않고도 캐릭터를 멈추고 싶지만 완벽하게 $(0, 0)$으로 조이스틱을 설정할 수 없어 캐릭터가 미세하게 움직였었다.
이를 해결하고자 Joystick Handler의 방향 벡터의 Min Value를 설정하여 화면에 손을 때지 않고도 캐릭터를 멈출 수 있게 구현했다.
PlayerButton
플레이어의 플랫폼 별 버튼 입력값을 토대로 방향 입력을 위한 버튼을 제외한 나머지 버튼의 상태를 반환하는 클래스이다.
bool 타입의 두 변수를 활용하여 버튼의 상태를 설정했다.
private void Update() { if (isPressed) { if (onPressed) { // Button state : Pressed } else { // Button state : Down onPressed = true; } } else { if (onPressed) { // Button state : Up onPressed = false; } else { // Button state : Idle } } }
PC - Keyboard
Keyboard의 버튼 입력의 경우 Input 클래스의 GetKeyDown, GetKeyUp 함수를 통해 간단하게 버튼 입력을 구현했다.
private void SetButtonState() { if (Input.GetKeyDown(keyCode)) { isPressed = true; } if (Input.GetKeyUp(keyCode)) { isPressed = false; } }
Mobile - Screen
Screen의 버튼 입력의 경우 Button Component를 사용할 경우 두 가지 문제점이 발생했다.
- 버튼 클릭 후 Screen Point 위치가 버튼의 Rect을 벗어나도록 드래그해도 버튼 입력이 유지된다.
- 버튼에서 손을 땠을 때의 이벤트를 처리하기 위해선 또 다른 Compoenent가 필요하다.
이를 해결하기 위해 Button Component 대신 Image Component에 IPointerDownHandler, IDragHandler, IPointerUpHandler 인터페이스를 활용하여 버튼 입력을 구현했다.
State Variable
플레이어의 입력값을 어떻게 Input Manager에 전달할 지 고민했었다. 이전 프로젝트에선 방향값과 버튼 상태를 Input Manager에 변수로 선언하고, PlayerJoystick과 PlayerButton에서 Input Manager의 변수들을 Update 문에서 갱신, Character에서 Input Manager의 변수들에 접근하여 방향값과 버튼의 상태를 확인했었다.
이렇게 구현할 경우 PlayerJoystick, PlayerButton에서 매 프레임 방향 벡터 및 모든 버튼의 상태를 업데이트함에도 불구하고, Character 클래스에서 또 다시 매 프레임 Input Manager에 접근하여 방향 벡터 및 모든 버튼의 상태를 확인해야하는 불상사가 발생했다.
// Character class 일부분 private void Update() { if (inputController.GetButtonDown(inputController.XButton) && canAttack) StartCoroutine(CheckCanUseSkill(skillManager.BaseAttack, inputController.XButton)); if (inputController.GetButtonDown(inputController.AButton) && canAttack) StartCoroutine(CheckCanUseSkill(skillManager.ASkill, inputController.AButton)); if (inputController.GetButtonDown(inputController.SButton) && canAttack) StartCoroutine(CheckCanUseSkill(skillManager.SSkill, inputController.SButton)); if (inputController.GetButtonDown(inputController.DButton) && canAttack) StartCoroutine(CheckCanUseSkill(skillManager.DSkill, inputController.DButton)); if (inputController.GetButtonDown(inputController.FButton) && canAttack) StartCoroutine(CheckCanUseSkill(skillManager.FSkill, inputController.FButton)); if (inputController.GetButtonDown(inputController.JButton) && canJump) StartCoroutine(Jump()); } private void FixedUpdate() { if (!CanMove) return; Move(); } private void Move() => moveController.Move(transformController, inputController.Direction);
Delegate
이를 해결하기 위해 Delegate를 활용하여 키 입력 클래스, Input Manager, Character 클래스를 연결했다. Delegate를 사용할 경우 디버깅이 어려워진다는 단점이 존재하지만, Character 클래스에서 매 프레임 키 입력을 검사할 필요가 없어졌고, 초기화 로직에서 Delegate를 등록하여 불필요한 연산을 제거할 수 있었다.
private void Start() { // 캐릭터 이동 Delegate GameManager.Input.SetMovementDelegate(characterMove.Move); // 버튼 입력 시 이벤트 Delegate GameManager.Input.SetButtonDelegate(EKeyName.JUMP, characterMove.Jump); }
'게임 클라이언트 개발 > MakeDNF' 카테고리의 다른 글
Hitbox와 Hitbox Editor 개선 (0) 2024.01.11 Skill 시스템 구현 및 개선 (0) 2024.01.10 DNF 충돌 시스템 (0) 2023.12.03 Hitbox with DNFTransform (0) 2023.12.02 2.5D 좌표계, DNFTransform (0) 2023.12.01 - 방향키 입력을 런타임에 플레이어가 설정할 수 없다.