์ ๋ฒ ๊ธ์์ ํ์ฅ์ฑ์ ๊ณ ๋ คํ์ฌ ๋ฌด๊ธฐ ์์คํ ์ ๊ธฐ๋ฐ์ ๋ง๋ค์์์ต๋๋ค. ์ด์ ๋ ์ง์ง ๋ฌด๊ธฐ์ ๊ณต๊ฒฉ ๊ตฌํ์ ํด๋ณผ ์ฐจ๋ก๋ค์.
๊ณต๊ฒฉ ํค๋ฅผ ์ ๋ ฅ ๋ฐ์์ผ ํ๋ ์๋ก์ด ์ ๋ ฅ ํค ๋ฐ์ธ๋ฉ์ด ํ์ํ ๊ฒ์ด๊ณ , ๋จ์ํ ๋๋ ์ ๋๋ ์๋๋ฉด ๋๋ฅด๊ณ ์ ์งํ๊ณ ์์ ๋๋ ๋ฑ๋ฑ์ ์ํธ์์ฉ๋ ์ถ๊ฐํด์ค์ผ ํ ๊ฒ๋๋ค. ์ฐ์ ์ ๋ ฅ ์ฒ๋ฆฌ๋ถํฐ ๋จผ์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
1. ๊ณต๊ฒฉ ํค ๋ฐ์ธ๋ฉ ๋ฐ ์ ๋ ฅ ๋ถ๊ธฐ ์ฒ๋ฆฌํ๊ธฐ
New Input System์ "์ํธ ์์ฉ(Interaction)"
๊ณต๊ฒฉ์ ๋ง์ฐ์ค ์ข ํด๋ฆญ์ผ๋ก ์ ๋ ฅ์ ๋ฐ์ต๋๋ค. Player Input Actions์ ๋ค์ด๊ฐ์ ํด๋น ํค ๋ฐ์ธ๋ฉ์ ํด์ฃผ๋๋ก ํ์ฃ .
์ผ์ชฝ ๋ง์ฐ์ค ๋ฒํผ์ ๋ฐ์ธ๋ฉ ํด์ฃผ์์ต๋๋ค. ๊ทธ๋ฐ๋ฐ Interactions์ Hold์ Press๊ฐ ์ถ๊ฐํ ๊ฑธ ๋ณผ ์ ์๋๋ฐ์.
์ํธ์์ฉ(Interaction)์ ํด๋น ํค ์ ๋ ฅ์ ๋ํด์ ์ด๋ค ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ ๊ฒ์ธ์ง์ ๋ํด ์ค์ ํด์ค ์ ์๋ ์ต์ ์ ๋๋ค. ์ํธ ์์ฉ์ ์ถ๊ฐํ ์์๋๋ก ์ฐ์ ์์๋ฅผ ๊ฐ์ง๊ฒ ๋ฉ๋๋ค. ์์ ์ฌ์ง์ ์๋ก ๋ ๋ค๋ฉด, Hold → Press ์์๋๋ก ์ฐ์ ๊ถ์ ๊ฐ์ง ๊ฒ๋๋ค.
์ ํฌ ๊ฒ์์ ๋ฌด๊ธฐ ๊ณต๊ฒฉ ์ข ๋ฅ์๋ ์ฐจ์ง ๊ณต๊ฒฉ(Charging Attack)์ด ์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๋ง์ฐ์ค ๋ฒํผ์ ์ผ์ ์๊ฐ ์ด์ ๋๋ฅด๊ณ ์๋์ง๋ฅผ ๋จผ์ ๊ฒ์ฌํด์ผ ํ์ฃ . ๋ฐ๋ผ์ Hold ์ํธ ์์ฉ์ ๋จผ์ ๊ฒ์ฌํ๋ ๊ฒ์ด๋ฉฐ, ๋ง์ฝ Hold๊ฐ ์คํจํ๊ฒ ๋๋ฉด ๋ค์ ์ฐ์ ์์ ์ํธ์์ฉ์ธ Press๊ฐ ๋ฐ๋๋ ๊ถํ์ ์ป๊ฒ ๋ ๊ฒ๋๋ค.
์ผ๋ฐ ๊ณต๊ฒฉ์ ๊ฒฝ์ฐ์๋ ๋ง์ฐ์ค ํด๋ฆญํ๊ณ ๋์ ๋ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ๋๋, Press๋ฅผ ์ถ๊ฐํ๊ณ Trigger Behavior๋ฅผ Release Only๋ก ํด์ฃผ์์ต๋๋ค. ์ค๋ช ์ด ๊ธธ์ด์ก์ง๋ง, ๋ด์ฉ์ ์์ฝํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ๋ฐ์ธ๋ฉํ ํค์ ๋ํ์ฌ ์ ๋ ฅ์ ์ด๋ค ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ ์ง์ ๋ํ ์ต์ ์ ์ค ์ ์์ผ๋ฉฐ, ์ด๊ฑธ ์ํธ์์ฉ(Interaction)์ด๋ผ๊ณ ํ๋ค.
- ์ํธ์์ฉ์ ์ถ๊ฐํ ์์๋๋ก ์ฐ์ ์์๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค.
- ํ์ฌ ์ํธ์์ฉ์ด ์ฑ๊ณต์ด ๋์๋ค๋ฉด, ๊ทธ ๋ค์ ์ฐ์ ์์ ์ํธ์์ฉ์ ๊ถํ์ ์ป์ง ๋ชปํ๋ค.
- ex) ์ฐจ์ง ๊ณต๊ฒฉ์ ํ๋ ค๊ณ ๋ง์ฐ์ค ๋ฒํผ์ ๋๋ฅด๊ณ ์์ด, Hold ์ํธ ์์ฉ์ด ์ฑ๊ณตํ๋ค๋ฉด ๊ทธ ๋ค์์ธ Press ์ํธ ์์ฉ์ ์คํ๋์ง ์๋๋ค.
- ํ์ฌ ์ํธ์์ฉ์ด ์คํจํ๋ค๋ฉด, ๊ทธ ๋ค์ ์ฐ์ ์์์ ์ํธ ์์ฉ์ด ๊ถํ์ ์ป์ด ์คํ๋๋ค.
- ex) ์ผ๋ฐ ๊ณต๊ฒฉ์ ์ํด ๋ง์ฐ์ค ๋ฒํผ์ ํด๋ฆญํ๊ณ ๋๋ค๋ฉด, Hold ์ํธ ์์ฉ์ ์คํจํ ๊ฒ์ด๋ฏ๋ก ๊ทธ ๋ค์์ธ Press ์ํธ ์์ฉ์ด ์คํ๋๋ค.
์ด๋ฌํ ๋ด์ฉ์ ์์งํ ์ฑ๋ก, ์ด์ ๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ์ ๋ํ ์ด๋ฒคํธ ์ฝ๋ฐฑ ํจ์๋ฅผ ์ ์ํ์ฌ ๋ฑ๋กํด์ฃผ๋๋ก ํฉ์๋ค.
// PlayerController
public void OnClickLeftMouse(InputAction.CallbackContext context)
{
...
}
์ด์ ๋ง์ฐ์ค ์ผ์ชฝ ์ ๋ ฅ์ ๋ฐ์ ์ ์๊ฒ ๋์์ต๋๋ค. ์ด์ ํจ์ ๋ด์์ ์ด๋ค ์ํธ์์ฉ์ด ๋ค์ด์๋์ง๋ฅผ ๊ฒ์ฌํ์ฌ ๋ถ๊ธฐ ์ฒ๋ฆฌ ํ๋ฉด ๋๊ฒ ๋ค์.
์ํธ์์ฉ ๊ฒ์ฌํ์ฌ ๋ถ๊ธฐ ์ฒ๋ฆฌํ๊ธฐ
์ํธ ์์ฉ ์ ๋ ฅ ์ฝ๋ฐฑ์๋ ๋ํ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ด ๋ค ๊ฐ์ง๊ฐ ์์ต๋๋ค.
- Waiting : ์ํธ ์์ฉ์ด ์ ๋ ฅ์ ๊ธฐ๋ค๋ฆฌ๊ณ ์๋ ์ํ
- Started : ์ํธ ์์ฉ์ด ์์๋์์ง๋ง(์์ ์ ๋ ฅ ์ค ์ผ๋ถ๋ฅผ ์์ ๋ฐ์), ์์ง ์๋ฃ๋ ์ ๋ ์ํ
- Performed : ์ํธ ์์ฉ์ด ์์ ํ ๋ค ์ด๋ฃจ์ด์ง ์ํ
- Canceled : ์ํธ ์์ฉ์ด ์ด๋ฃจ์ด ์ง๋ ๊ณผ์ ์ค ์กฐ๊ฑด์ ์ถฉ์กฑํ์ง ๋ชปํด ์ค๋จ๋ ์ํ
- ์๋ฅผ ๋ค์ด, ๋ฒํผ์ ๋๋ ๋ค๊ฐ ๋๋๋ฐ ํ์ํ ์ต์ ์๊ฐ์ด ์๋๋ฐ ๊ทธ ์๊ฐ ์ ์ ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ์์ ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค.
์ด ์ฝ๋ฐฑ ์ ๋ณด๋ฅผ ์ด์ฉํ์ฌ ๋ค์๊ณผ ๊ฐ์ด ์ฝ๋๋ฅผ ์ง์, ์ฐจ์ง ๊ณต๊ฒฉ์ผ ๋์ ์ผ๋ฐ ๊ณต๊ฒฉ์ผ ๋๋ฅผ ๋ถ๋ฆฌํ์์ต๋๋ค.
public void OnClickLeftMouse(InputAction.CallbackContext context)
{
if (context.performed)
{
if (context.interaction is HoldInteraction) // ์ฐจ์ง ๊ณต๊ฒฉ
{
}
else if (context.interaction is PressInteraction) // ์ผ๋ฐ ๊ณต๊ฒฉ
{
}
}
}
๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ์ ๋๋ ๋ค๋ฉด ๊ฐ์ฅ ๋จผ์ Hold Interaction์ด ์คํ๋๊ฒ ์ง์. ํ๋ ์ด์ด๊ฐ ์ฐจ์ง ๊ณต๊ฒฉ์ ํ๊ธฐ ์ํด ๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ์ ๊ณ์ ๋๋ฅด๊ณ ์๋๋ค๋ฉด, ํด๋น ์ํธ ์์ฉ์ ์ฑ๊ณต์ผ๋ก ๊ฐ์ฃผ๋์ด Performed ์ฝ๋ฐฑ์ ํธ์ถํด์ค ๊ฒ๋๋ค.
ํ๋ ์ด์ด๊ฐ ์ผ๋ฐ ๊ณต๊ฒฉ์ ํ๊ธฐ ์ํด ๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ์ ๋๋ ๋ค๊ฐ ๋๋ค๋ฉด, Hold Interaction์ ์ฝ๋ฐฑ์ Canceled๊ฐ ํธ์ถ ๋ ๊ฒ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋์ ๋ค์ ์ํธ ์์ฉ์ธ Press Interaction์ด ์คํ๋๊ฒ ์ง์. ๋๋ ๋ค ๋์ผ๋ ์ฑ๊ณตํ์ผ๋ฏ๋ก Performed๊ฐ ํธ์ถ๋ ๊ฒ๋๋ค.
์ด๋ฐ ์ฝ๋ฐฑ ๊ณผ์ ์ ์๊ฐํ์ฌ ์์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ์ฌ ๋ถ๋ฆฌํด์ฃผ์์ต๋๋ค. ์ด์ ์ ๋ ฅ ๋ฐ๋ ๊ฒ์ ์ฒ๋ฆฌํด๋จ์ผ๋, ๋ง์ฐ์ค ์๋ ์ขํ๋ฅผ ์ป์ด์ค๋ ๊ฑธ ํด๋ณด๋๋ก ํฉ์๋ค.
2. ๋ง์ฐ์ค ์๋ ์ขํ ์ป์ด์ค๊ธฐ
๋ง์ฐ์ค์ ์ขํ๋ ๋ค์ ์ฝ๋๋ฅผ ํตํด ์ป์ด์ฌ ์ ์์ต๋๋ค.
Mouse.current.position.ReadValue(); // ์คํฌ๋ฆฐ ์ขํ
ํ์ง๋ง ์์ ์ฝ๋๋ฅผ ํตํด ์ป์ด์ค๋ ๊ฒ์ ์คํฌ๋ฆฐ ์ขํ๊ณ์ ๋ง์ฐ์ค ์ขํ์ ๋๋ค. ๋ชจ๋ํฐ ์คํฌ๋ฆฐ์ 2์ฐจ์์ผ๋ก ๋์ด ์๊ธฐ ๋๋ฌธ์ x์ y ๊ฐ๋ง ์กด์ฌํ๊ณ z๊ฐ์ ํญ์ 0์ ๋๋ค.
ํ์ง๋ง ์ฐ๋ฆฌ ์บ๋ฆญํฐ๋ ์คํฌ๋ฆฐ ์ขํ๊ณ์ ์กด์ฌํ๋ ๊ฒ์ด ์๋๋ผ ๊ฒ์ ์๋ ์ขํ๊ณ์ ์กด์ฌํฉ๋๋ค. ๊ทธ๋ ๋ค๋ฉด ์คํฌ๋ฆฐ ์ขํ๊ณ์ ๋ง์ฐ์ค ์ขํ๋ฅผ ํตํด ์ด๋ป๊ฒ ์๋ ์ขํ๋ฅผ ์ป์ ์ ์์๊น์?
๋ง์ฐ์ค๋ฅผ ํด๋ฆญํ ์ง์ ์ผ๋ก๋ถํฐ Raycast๋ฅผ ์๋ฉด ๋ฉ๋๋ค.
์ฆ, ๊ฒ์ ์๋ ๋ด์ ์นด๋ฉ๋ผ๋ก๋ถํฐ Raycast๊ฐ ๋ป์ด ๋์ ์ค์ ๊ฒ์ ์๋ ๋ด์ ์ค๋ธ์ ํธ์ ์ถฉ๋ํ๋ ๊ฒ์ด์ง์.
์ฐ๋ฆฌ๊ฐ ๋ง์ฐ์ค๋ก ํด๋ฆญํ์ฌ ๊ณต๊ฒฉ์ ํ๋ค๋ฉด, ์บ๋ฆญํฐ๋ ํด๋น ํด๋ฆญํ ๋ฐฉํฅ์ ๋ฐ๋ผ๋ด์ผ ํฉ๋๋ค. Raycast์ ์ถฉ๋ํ ์ง์ ์ผ๋ก๋ถํฐ ํ์ฌ ์บ๋ฆญํฐ์ ์ขํ๋ฅผ ๋นผ์, ํด๋น ๋ฐฉํฅ์ผ๋ก์ ๋ฒกํฐ๋ฅผ ์ป์ด๋ด๋๋ก ํฉ์๋ค.
// PlayerController
protected Vector3 GetMouseWorldPosition()
{
Vector3 mousePosition = Mouse.current.position.ReadValue();
Ray ray = Camera.main.ScreenPointToRay(mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 1000f, Color.red, 5f); // Raycast ์๊ฐํ
if (Physics.Raycast(ray, out RaycastHit HitInfo, Mathf.Infinity))
{
Vector3 target = HitInfo.point;
Vector3 myPosition = new Vector3(transform.position.x, 0f, transform.position.z);
target.Set(target.x, 0f, target.z);
return (target - myPosition).normalized;
}
return Vector3.zero;
}
์ด์ ์ด ๋ฉ์๋๋ฅผ ํตํด ๋ง์ฐ์ค ์๋ ์ขํ๋ฅผ ๊ณ์ฐํด ์ ์ฅํด๋๋๋ก ํฉ์๋ค.
// PlayerController
public Vector3 MouseDirection { get; private set; }
public void OnClickLeftMouse(InputAction.CallbackContext context)
{
if (context.performed)
{
MouseDirection = GetMouseWorldPosition();
LookAt(MouseDirection);
if (context.interaction is HoldInteraction) // ์ฐจ์ง ๊ณต๊ฒฉ
{
}
else if (context.interaction is PressInteraction) // ์ผ๋ฐ ๊ณต๊ฒฉ
{
}
}
}
ํด๋ฆญํ ๋ฐฉํฅ์ผ๋ก ์บ๋ฆญํฐ๊ฐ ์ ๋ณด๋์ง ํ ์คํธ ํด๋ด ์๋ค.
์ด์ ๊ณต๊ฒฉ ์ํ(Attack State)๋ฅผ ์ ์ํ์ฌ, ์ํ ๋จธ์ (StateMachine)์ ๋ฑ๋กํด๋ด ์๋ค.
3. ๊ณต๊ฒฉ ์ํ(Attack State) ์ถ๊ฐํ์ฌ ๋ฑ๋กํ๊ธฐ
์ฐ์ ์ผ๋ฐ ๊ณต๊ฒฉ ์ํ๋ฅผ ๊ตฌํํด๋ณด๋๋ก ํ์ฃ . ๋ค์๊ณผ ๊ฐ์ด AttackState ํด๋์ค๋ฅผ ์ ์ํด ์ฃผ์์ต๋๋ค.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace CharacterController
{
public class AttackState : BaseState
{
public static bool IsAttack = false;
public const float CanReInputTime = 1f;
public AttackState(PlayerController controller) : base(controller) { }
public override void OnEnterState()
{
IsAttack = true;
Player.Instance.weaponManager.Weapon?.Attack(this);
}
public override void OnExitState()
{
}
public override void OnFixedUpdateState()
{
}
public override void OnUpdateState()
{
}
}
}
WeaponManager๊ฐ ํ์ฌ ๋ค๊ณ ์๋ ๋ฌด๊ธฐ์ Attack() ๋ฉ์๋๋ฅผ ์คํํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค. ์ถ์ ๋ฉ์๋๋ก ์ ์ํ์ผ๋, ๋ค๊ณ ์๋ ๋ฌด๊ธฐ์ ๋ฐ๋ผ ์ ๊ฐ๊ฐ ๋ค๋ฅธ ๊ณต๊ฒฉ์ด ๋๊ฐ๊ฒ ์ง์. ๊ทธ๋ฐ๋ฐ, ๋ฌด๊ธฐ ์์คํ ์ ๋ง๋ค ๋ ์๊ฐํด์ผ ํ๋ ๊ฒ ํ ๋๊ฐ์ง๊ฐ ์๋๋๊ตฐ์.
- ๋ฌด๊ธฐ๋ง๋ค ๊ณต๊ฒฉ ์ ๋๋ฉ์ด์ ์ด ๋ค๋ฅผํ ๋ฐ, ์ด๊ฑธ ์ด๋ป๊ฒ ๋ฌด๊ธฐ๋ง๋ค ๋ค๋ฅธ ์ ๋๋ฉ์ด์ ์ด ๋ฐ๋๋๋๋ก ๊ด๋ฆฌํ ์ ์์๊น?
- ๊ณต๊ฒฉ ์ ๋๋ฉ์ด์ ๋ง๋ค ๊ธธ์ด๊ฐ ๋ค๋ฅผ ๊ฒ์ด๊ณ , ๋ํ ๊ณต๊ฒฉ ์๋์ ๋ฐ๋ผ ๋ ๋น ๋ฅด๊ฑฐ๋ ๋ ๋๋ฆฌ๊ฒ ์ฌ์๋ ์๋ ์์ํ ๋ฐ ํด๋น ๊ณต๊ฒฉ ๋ชจ์ ์ด ๋๋ฌ๋ค๋ ๊ฒ์ ์ด๋ป๊ฒ ์ ์ ์์๊น?
- ๊ณต๊ฒฉ ํค ์ ๋ ฅ์ ๋ฐ๊ณ ์ผ์ ์๊ฐ ๋ด์ ์ ๋ ฅ์ด ๋ค์ด์๋์ง ์ฒดํฌ๋ฅผ ํด์ผ ํ๋๋ฐ, ์ด๋ค ๋ฐฉ๋ฒ์ ์จ์ ํ ๊ฒ์ธ๊ฐ?
ํ๋์ฉ ํ ๋ฒ ์์๋ณด๋๋ก ํฉ์๋ค.
4. ๊ณต๊ฒฉ ์ ๋๋ฉ์ด์
Weapon Animation Layer ์์ฑํ๊ณ , ๊ธฐ๋ณธ ๊ณต๊ฒฉ ์ ๋๋ฉ์ด์ ์ฐ๊ฒฐํ๊ธฐ
์๋์ ์ ๋๋ฉ์ดํฐ์์ ์๋ก์ด ์ ๋๋ฉ์ด์ ๋ ์ด์ด๋ฅผ ์์ฑํ๊ณ , Weapon Layer๋ผ๊ณ ์ด๋ฆ ์ง์ด ์ฃผ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๊ฐ์ค์น(Weight) ๊ฐ์ 1๋ก ์ฃผ์์ฃ . ์ด๋ ๊ฒ ๋๋ฉด Weapon Layer ๋ํ Base Layer์ ํจ๊ป ๊ฐ์ด ์ฌ์๋๋ค๋ ๊ฑธ ๋งํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ ๊ณต๊ฒฉ ์ ๋๋ฉ์ด์ ๋ค์ ๋ฐฐ์นํด ์ฃผ์์ต๋๋ค.
๊ทธ๋ฅ Base Layer์์ ํ๋ฉด ๋์ง, ์ ์ด๋ ๊ฒ Layer๋ฅผ ๋๋์ด์ ํ๋๊ณ ์๊ฐ์ด ๋ค ์๋ ์๋๋ฐ, ์ด๊ฒ์ ๋์ค์ ๋ฌด๊ธฐ๋ณ๋ก ๋ค๋ฅธ ์ ๋๋ฉ์ด์ ์ ์ฌ์ํด์ผ ํ๋ ๋ถ๋ถ์ ํด๊ฒฐํด ์ค ๊ฒ๋๋ค.
Empty ์ํ์๋ ํ ๋น๋ ์ ๋๋ฉ์ด์ ์ด ์๊ธฐ ๋๋ฌธ์ ๋ง ๊ทธ๋๋ก ์๋ฌด ํ๋๋ ํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ค๊ฐ ํน์ ์กฐ๊ฑด์ ๋ฐ๋ผ ํธ๋์ง์ ์ด ์ผ์ด๋๋ฉด ํด๋น ์ ๋๋ฉ์ด์ ์ ์ฌ์ํ๊ฒ ์ง์. ๊ทธ๋ฆฌ๊ณ ์ ๋๋ฉ์ด์ ๋งค๊ฐ๋ณ์๋ก ๋ค์๊ณผ ๊ฐ์ด ์ธ ๊ฐ๋ฅผ ์ ์ธํด ์ฃผ์์ต๋๋ค.
AttackCombo ๋ณ์๋ ํ์ฌ ๋ช ๋ฒ์งธ ์ฝค๋ณด ๊ณต๊ฒฉ์ธ์ง๋ฅผ ๋ํ๋ ๋๋ค.
AttackSpeed๋ ํ์ฌ ๋ด ๋ฌด๊ธฐ์ ๊ณต๊ฒฉ ์๋์ ๋ฐ๋ผ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋๋ฅผ ๋ค๋ฅด๊ฒ ํด์ค ๋ณ์์ ๋๋ค.
IsAttack ๋ณ์๋ ์ข ๋ ๋งค๋๋ฝ๊ณ ์ ํํ ํธ๋์ง์ ์ ์ํด ๋ง๋ค์ด ์ค ๋ณ์์ ๋๋ค.
3ํ ์ ๋๋ฉ์ด์ ๊ฐ๊ฐ์ AttackSpeed ๊ฐ์ ์ฌ์ ์๋์ ๊ณฑํ๋๋ก ๋ง๋ค์ด ์ฃผ์์ต๋๋ค.
์ด์ ๋ฌด๊ธฐ ๊ณต๊ฒฉ ์๋ ๊ฐ์ ์ฝ์ด์์ AttackSpeed ๋ณ์์ ์ ์ฅํ๋ค๋ฉด, ๋ฌด๊ธฐ์ ๊ณต๊ฒฉ ์๋ ๊ฐ์ ๋ฐ๋ผ ์ ๋๋ฉ์ด์ ์ฌ์ ์๋๊ฐ ์ ๋์ ์ผ๋ก ๋ฌ๋ผ์ง ๊ฒ๋๋ค. ์๋ฌดํผ ์ด๋ฐ ์์ผ๋ก ์ธํ ํด์ฃผ๊ณ , ๊ฐ ์ ๋๋ฉ์ด์ ๊ฐ ํธ๋์ง์ ์กฐ๊ฑด๋ ์ธํ ํด์ฃผ๋๋ก ํฉ๋๋ค.
*์ธํ ๊ฐ์ ๋๋ณด๊ธฐ์ ์ ์ด ๋์์ต๋๋ค.
์ ๋๋ฉ์ด์ ์ด๋ฒคํธ ํจ์ ๋ฑ๋กํ๊ธฐ
์ ๋๋ฉ์ด์ ์์ ํน์ ํค ํ๋ ์์ ์ด๋ฒคํธ ํจ์๋ฅผ ๋ฑ๋กํ ์ ์์ต๋๋ค. ์ด๊ฑธ ์ด์ฉํ์ฌ ๊ณต๊ฒฉ ๋ชจ์ ์ด ๋๋ฌ๋ค๊ณ ์ด๊ธฐํ ํด์ฃผ๋ ์ด๋ฒคํธ ํจ์๋ฅผ ํธ์ถํด์ค ๊ฒ๋๋ค. ๋ชจ์ ์ด ๊ฑฐ์ ๋๋๊ฐ ๋ ์ฏค์ ๋ฑ๋กํด์ค์๋ค.
๊ทธ๋ฆฌ๊ณ SondolAnimationEvents๋ผ๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋ง๋ค์ด ์๋์๊ฒ ๋ฌ์์ฃผ์์ต๋๋ค. ์ ์ ๋ณ์๋ค๋ก๋ง ์ ๊ทผ์ ํ๊ธฐ ๋๋ฌธ์, ์ฌ์ค์ ์๋์ด ์๋๋ผ ๋ค๋ฅธ ๋น ์ค๋ธ์ ํธ์ ๋ฌ๊ณ ์ ๋๋ฉ์ด์ ์ด๋ฒคํธ๋ง ๊ด๋ฆฌํ๋ ์ ๋ฅผ ๋ง๋ค์ด๋ ๊ด์ฐฎ๊ฒ ๋ค์.
using CharacterController;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SondolAnimationEvents : MonoBehaviour
{
public void OnFinishedAttack()
{
AttackState.IsAttack = false;
Player.Instance.animator.SetBool("IsAttack", false);
Player.Instance.stateMachine.ChangeState(StateName.MOVE);
}
}
๊ทธ๋ฆฌ๊ณ ์ด์ ์ ๋ฑ๋กํด ๋ ์ ๋๋ฉ์ด์ ์ด๋ฒคํธ์ ์ ํจ์๋ฅผ ๋ฑ๋กํด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ์ ๋๋ฉ์ด์ ์ฌ์ ์๋๊ฐ ๋ฌ๋ผ๋, ๋ชจ์ ์ด ๋๋ ์ง์ ์ ์ ํํ๊ฒ ํ์ ํ ์ ์์ ๊ฒ๋๋ค.
๊ณต๊ฒฉ ๋ชจ์ ์ด ๋๋ฌ์ ๋, ๊ณต๊ฒฉ๊ณผ ๊ด๋ จ๋ ๋ณ์๋ค์ ์ด๊ธฐํํด์ฃผ๋ ๊ฒ์ด์ง์.
์ด์ ๋ง์ง๋ง์ผ๋ก ๊ธฐ๋ณธ ๋ฌด๊ธฐ์ธ ์นด๋ก ์ ๋ ธ(CharonPaddle) ํด๋์ค๋ฅผ ๋ง๋ค์ด์ ๊ณต๊ฒฉ ๋ก์ง์ ๊ตฌํํด๋ด ์๋ค.
5. ์นด๋ก ์ ๋ ธ(Charon's Paddle) ๋ฌด๊ธฐ ํด๋์ค ๋ง๋ค๊ธฐ
์ด์ ์ ๋ง๋ค์๋ BaseWeapon ์ถ์ ํด๋์ค๋ฅผ ์์ ๋ฐ์ ์ถ์ ๋ฉ์๋๋ค์ ๊ตฌํํด์ฃผ๋ฉด ๋ฉ๋๋ค.
using CharacterController;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharonPaddle : BaseWeapon
{
public readonly int hashIsAttackAnimation = Animator.StringToHash("IsAttack");
public readonly int hashAttackAnimation = Animator.StringToHash("AttackCombo");
public readonly int hashAttackSpeedAnimation = Animator.StringToHash("AttackSpeed");
private Coroutine checkAttackReInputCor;
public override void Attack(BaseState state)
{
ComboCount++;
Player.Instance.animator.SetFloat(hashAttackSpeedAnimation, AttackSpeed);
Player.Instance.animator.SetBool(hashIsAttackAnimation, true);
Player.Instance.animator.SetInteger(hashAttackAnimation, ComboCount);
CheckAttackReInput(AttackState.CanReInputTime);
}
public override void ChargingAttack(BaseState state)
{
}
public override void DashAttack(BaseState state)
{
}
public override void Skill(BaseState state)
{
}
public override void UltimateSkill(BaseState state)
{
}
public void CheckAttackReInput(float reInputTime)
{
if (checkAttackReInputCor != null)
StopCoroutine(checkAttackReInputCor);
checkAttackReInputCor = StartCoroutine(CheckAttackReInputCoroutine(reInputTime));
}
private IEnumerator CheckAttackReInputCoroutine(float reInputTime)
{
float currentTime = 0f;
while (true)
{
currentTime += Time.deltaTime;
if (currentTime >= reInputTime)
break;
yield return null;
}
ComboCount = 0;
Player.Instance.animator.SetInteger(hashAttackAnimation, 0);
}
}
๊ธฐ๋ณธ ๊ณต๊ฒฉ์ ์ฝค๋ณด ์นด์ดํธ ์๋ฅผ ๋๋ฆฌ๋ฉด์ ํด๋น ์ ๋๋ฉ์ด์ ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฐ์ดํธํ๋ฉด์ ์ฌ์ํด์ฃผ๋ ๊ฒ์ด๊ตฌ์. ๋ ํ๋์ ํต์ฌ์ ๊ธฐ๋ณธ ๊ณต๊ฒฉ ํ, ์ผ์ ์๊ฐ ๋ด์ ๋ค์ ๊ณต๊ฒฉ์ ํ์ง ์์ผ๋ฉด ์ฝค๋ณด๋ก ์ด์ด์ง์ง ์๊ณ ์ฒ์ ๊ณต๊ฒฉ์ผ๋ก ์ด๊ธฐํ ๋๋ค๋ ์ ์ ๋๋ค.
๊ทธ๋์ ๊ทธ ์๊ฐ์ ์ฒดํฌํ๋ ์ฉ๋๋ก ์ฝ๋ฃจํด(Coroutine)์ ์ฌ์ฉํ์์ต๋๋ค. ๋ง์ฝ ์ผ์ ์๊ฐ ๋ด์ ์ ๋ ฅ์ด ๋ค์ ๋ค์ด์๋ค๋ฉด, ํ์ฌ ์ฝ๋ฃจํด์ ์ค๋จํ๊ณ ์๋ก ์์ํ๋ ๊ฒ์ด์ฃ . ์ผ์ ์๊ฐ์ด ์ง๋๋ฒ๋ ธ๋ค๋ฉด, ์ฝค๋ณด ์นด์ดํธ์ ์ ๋๋ฉ์ด์ ๋ณ์๋ฅผ ์ด๊ธฐํ ํด์ค๋๋ค.
์ฌ๋ด์ผ๋ก, ์ฝ๋ฃจํด์ด ์ค์ง๊ฐ ๋์ง ์๊ธธ๋ ์ ์ ๋๋ ํ๋ค๊ฐ ๊ธฐ์กด ์ฝ๋ฃจํด์ ๋ ๋๊ณ , ์๋ก ํ ๋นํ์ฌ ๊ทธ๊ฑธ ์ค์งํ๊ณ ์์์ต๋๋ค... ์ฆ, ๋ค์๊ณผ ๊ฐ์ด ํ๊ณ ์์๋จ ์๋ฆฌ์ง์.
public void CheckAttackReInput(float reInputTime)
{
if (checkAttackReInputCor != null)
StopCoroutine(CheckAttackReInputCoroutine(reInputTime));
StartCoroutine(CheckAttackReInputCoroutine(reInputTime));
}
์ฌ๋ฌ๋ถ๋ค์ ์ด๋ฐ ์ค์ ํ์ง ๋ง์ธ์... ์ด๊ฒ ๋๋ฌธ์ 30๋ถ ๋ ๋ ธ์ต๋๋ค. ์ด์ ๋ง์ง๋ง์ผ๋ก ๋ ๊ฐ์ง ์์ ๋ง ๋จ์์ต๋๋ค.
6. Animator Override Controller ๋ง๋ค๊ธฐ
์์์ ์ ๋๋ฉ์ด์ ์ธํ ํ๊ณ ๋งค๊ฐ๋ณ์ ๋ง๋ค์ด ์ฃผ๊ณ ํ๋ ์ฐ๋ฆฌ ์๋์ Animator๋ฅผ ์ฐํด๋ฆญํ์ฌ, Animator Override Controller๋ฅผ ๋ง๋ค์ด ์ฃผ๋๋ก ํฉ์๋ค.
๊ทธ๋ฆฌ๊ณ ์นด๋ก ์ ๋ ธ ๋ฌด๊ธฐ์ ํด๋นํ๋ ์ ๋๋ฉ์ด์ ๋ค์ ํ ๋นํ์ฌ ์ฌ์ฉํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ "CharonPaddleController"๋ผ๊ณ ์ด๋ฆ ์ง๊ฒ ์ต๋๋ค. ์๋์ ์ ๋๋ฉ์ดํฐ ํ๊ฒฝ์ ๊ทธ๋๋ก ๊ฐ์ ธ์ฌ ๊ฒ์ด๋ฏ๋ก Controller์ ์๋ ์ ๋๋ฉ์ดํฐ๋ฅผ ์ฐ๊ฒฐํด์ค๋๋ค.
Animator Override Controller๋ ๋ง ๊ทธ๋๋ก ์ ํํ Animator์ Layer, ๋งค๊ฐ๋ณ์, ์ํ(State)๋ฑ์ ํ๊ฒฝ์ ๊ทธ๋๋ก ๊ฐ์ ธ์ค์ง๋ง, Override๋ ๋ง ๋ต๊ฒ ๋คํ์ฑ์ ์ ์ฉํ์ฌ ์ฌ์ฉํ ์ ์๋ ๊ฒ์ ๋งํฉ๋๋ค.
๋ฌด๊ธฐ๊ฐ 3๊ฐ์ง ์ข ๋ฅ๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๊ณ , ๊ทธ ๋ฌด๊ธฐ๋ค ๋ชจ๋ 3ํ ๊ณต๊ฒฉ์ด ์๋ ๊ฒ์ ๋๊ฐ์ง๋ง ์ ๊ฐ๊ฐ ๋ชจ์ ์ ๋ค๋ฅด๋ค๊ณ ํด๋ณด๋ฉด, ์ด๋ฐ ๋ฐฉ์์ ํตํด ์ ๋๋ฉ์ด์ ๋ง ๋ณ๊ฒฝํ์ฌ ํจ์จ์ ์ผ๋ก ํด๋น ๋ฌด๊ธฐ ์ ๋๋ฉ์ด์ ๋ค์ ๊ด๋ฆฌํ ์ ์๋ ๊ฒ์ด์ฃ . ๊ทธ๋ฆฌ๊ณ ๋ฌด๊ธฐ๋ฅผ ๊ต์ฒดํ ๋ ์ ๋๋ฉ์ดํฐ๋ ๊ฐ์ด ๊ต์ฒดํด์ฃผ๋ฉด, ๋ณ ๋ค๋ฅธ ์ธํ ์์ด ๋ฌด๊ธฐ๋ง๋ค ์๋ก ๋ค๋ฅธ ์ ๋๋ฉ์ด์ ์ฌ์์ ๊ตฌํํ ์ ์๊ฒ ๋ฉ๋๋ค.
BaseWeapon์ RuntimeAnimatorController๋ผ๋ ๋ณ์๊ฐ ์๋ ๊ฑธ ๊ธฐ์ตํ์๋์? ๋ฐ๋ก ์ด ์ฉ๋๋ฅผ ์ํด ๋ฃ์ด๋จ์ต๋๋ค.
using CharacterController;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class BaseWeapon : MonoBehaviour
{
public int ComboCount { get; set; }
public WeaponHandleData HandleData { get { return weaponhandleData; } }
// ์ด ๋ฌด๊ธฐ๋ฅผ ์ฌ์ฉํ ๋์ ์ ๋๋ฉ์ดํฐ
public RuntimeAnimatorController WeaponAnimator { get { return weaponAnimator; } }
...
}
๋ฌด๊ธฐ๋ฅผ ๊ต์ฒดํ ๋๋ง๋ค, ์๋ Animator์๋ค๊ฐ ํด๋น ๋ฌด๊ธฐ์ RuntimeAnimatorController๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋ฉ๋๋ค.
์ด ๋ถ๋ถ์ WeaponManager์ SetWeapon()์ ์ด๋ฏธ ๊ตฌํํด ๋จ์์ต๋๋ค.
// ๋ฌด๊ธฐ ๋ณ๊ฒฝ
public void SetWeapon(GameObject weapon)
{
if (Weapon == null)
{
weaponObject = weapon;
Weapon = weapon.GetComponent<BaseWeapon>();
weaponObject.SetActive(true);
/// ์ ๋๋ฉ์ดํฐ ๋ณ๊ฒฝ
Player.Instance.animator.runtimeAnimatorController = Weapon.WeaponAnimator;
return;
}
for(int i = 0; i < weapons.Count; i++)
{
if (weapons[i].Equals(Weapon))
{
weaponObject = weapon;
weaponObject.SetActive(true);
Weapon = weapon.GetComponent<BaseWeapon>();
/// ์ ๋๋ฉ์ดํฐ ๋ณ๊ฒฝ
Player.Instance.animator.runtimeAnimatorController = Weapon.WeaponAnimator;
continue;
}
weapons[i].SetActive(false);
}
}
์ด์ ์นด๋ก ์ ๋ ธ ๋ฌด๊ธฐ์๋ค๊ฐ ์์์ ์์ฑํ๋ "CharonPaddleController" ์ ๋๋ฉ์ดํฐ๋ฅผ ๋ฃ์ด๋๋๋ก ํฉ์๋ค.
ํ์ฌ๋ ๋ฌด๊ธฐ๊ฐ ์นด๋ก ์ ๋ ธ ๋ฐ์ ์๊ธฐ ๋๋ฌธ์ Override Controller์ ์ ๋๋ฉ์ด์ ๋ค์ ๋ค๋ฅธ ๊ฑธ๋ก ๊ต์ฒดํ ํ์๊ฐ ์์ต๋๋ค.
๋์ค์ ๋ฌด๊ธฐ๊ฐ ์ถ๊ฐ๋ก ์๊ธฐ๊ฒ ๋๋ฉด, ์ ๋๋ฉ์ด์ ํด๋ฆฝ๋ค๋ง ๊ต์ฒดํด์ฃผ๊ณ ํด๋น ์ ๋๋ฉ์ดํฐ๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ฉด ๋๊ฒ ์ง์.
์ด์ ์ง์ง ๋ง์ง๋ง์ผ๋ก, ์ ๋ ฅ ์ฒ๋ฆฌ ๋ถ๋ถ์์ ๊ณต๊ฒฉ ์ํ๋ก ์ ํํ๋ ์ฝ๋๋ง ๋ฃ์ด์ฃผ๋ฉด ๋ฉ๋๋ค.
7. ๊ณต๊ฒฉ ์ํ ์ ํ ์ฝ๋ ์ ๋ ฅํ๊ธฐ
ํ์ฌ ๊ณต๊ฒฉ ์ค์ด ์๋๊ณ , ์ฝค๋ณด ์นด์ดํธ๊ฐ 3 ๋ฏธ๋ง์ผ ๋๋ง ๊ณต๊ฒฉ ์ํ๋ก ์ ํ์ด ์ผ์ด๋๋๋ก ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
// PlayerController
public void OnClickLeftMouse(InputAction.CallbackContext context)
{
if (context.performed)
{
MouseDirection = GetMouseWorldPosition();
if (context.interaction is HoldInteraction) // ์ฐจ์ง ๊ณต๊ฒฉ
{
/// ์ฐจ์ง ๊ณต๊ฒฉ ์ํ ์ ํ
}
else if (context.interaction is PressInteraction) // ์ผ๋ฐ ๊ณต๊ฒฉ
{
if (DashState.IsDash) // ์ผ๋จ์ ๋ง์๋จ์
{
/// ๋์ ๊ณต๊ฒฉ ์ํ์ ํ
return;
}
bool isAvailableAttack = !AttackState.IsAttack &&
(player.weaponManager.Weapon.ComboCount < 3);
if (isAvailableAttack)
{
LookAt(MouseDirection);
player.stateMachine.ChangeState(StateName.ATTACK);
}
}
}
}
์ด์ ์ง์ง๋ก ๋ชจ๋ ๊ฒ ๋๋ฌ์ต๋๋ค. ์ ๋ง ๊ธธ์๋ค์. ๋ฌด๊ธฐ ์์คํ ํ๋ ๊ตฌํํ๋๋ฐ, ์ด๋ ๊ฒ ๋ง์ ๊ฒ๋ค์ ์๊ฐํด์ผ ํ ์ค์ ๋ชฐ๋์ต๋๋ค. ์ถํ ํ์ฅ์ ํธํ๊ฒ ํ๊ธฐ ์ํด, ๊ธฐ๋ฐ์ ์ ๋ค์ง๋ ๊ฒ์ ์ค์ํ ์ผ์ด๋ ๊ณ ๋ฏผ์ ๋ง์ด ํ์์ต๋๋ค.
ํ ์คํธ ํ ๋ฒ ํด๋ณด๊ณ ๋ง์น๋ฉด ๋๊ฒ ๋ค์.
๋์ค์ ์ ์ ๋ ฅ์ ๋ฐ์ ์ ์๋๋ก ์ ์ฉํ๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค. ์๋ํ๋ฉด ์ ๋๋ฉ์ด์ ์ฌ์ ์๊ฐ์ด ์ฝค๋ณด ์ฌ์ ๋ ฅ ๊ธฐ์ค ์๊ฐ๋ณด๋ค ๊ธธ๊ฒ ๋๋ฉด, ๋ค์ ์ฝค๋ณด๋ก ๊ฐ ์๊ฐ ์๊ฑฐ๋ ์. ์ด ๋ถ๋ถ์ ๋์ค์ ํ ๋ฒ ๊ณ ๋ฏผํด์ ์ ์ฉํด ๋ณด๊ฒ ์ต๋๋ค.
์ค๋ ๊ธ์ด ๊ต์ฅํ ๊ธธ์๋๋ฐ, ์ฝ์ด์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค!