플레이어의 체력(Health), 배고픔(Hunger), 스태미나(Stamina)는 다음과 같은 구조를 가진다.
- 배고픔은 시간이 지나면서 지속적으로 감소하며, 0이되면 체력이 서서히 감소한다.
- 스태미나는 특정 행동을 할 때 감소하며, 0이되면 특정 행동을 할수없게 된다.
- 스태미나는 소모되지 않는 행동을 할 경우 서서히 회복된다.
배고픔의 경우 꾸준히 음식을 먹어줘야 하며 모든 스탯은 아이템을 통하여 즉시 회복이 가능하다.
이러한 구조를 위해 지금껏 스탯을 계속 나눠주었던거고 PlayerCondition에서 해당 코드를 작성해주었다.
public class PlayerCondition : StatManager
{
public void Update()
{
curHunger = Mathf.Max(curHunger - passiveStamina * Time.deltaTime, 0f);
curStamina = Mathf.Min(curStamina + passiveStamina * Time.deltaTime, maxStamina);
if (curHunger <= 0f)
{
curHealth = Mathf.Max(curHealth - noHungerHealthDecay * Time.deltaTime, 0f);
}
}
public bool UseStamina(float amount)
{
if (curStamina - amount < 0f)
{
return false;
}
curStamina -= amount;
return true;
}
}
Update에서 배고픔 값은 지속적으로 감소, 배고픔이 0이되면 체력이 감소하며 스태미나는 점점 회복한다.
UseStamina에선 지정한 값(amount)만큼 스태미나를 소비하고, 스태미나 사용이 성공하면 true, 실패하면 false를 반환하되 스태미나가 부족하면 false를 반환하여 행동을 막는 로직까지 추가해주었다.
이 로직을 작성한 이유는 공격을 하거나 특정한 행동을 할 경우 스태미나가 감소되는걸 다른곳에서 편하게 사용하기 위해서다.
조작 기능은 PlayerController에서 관리하는데, 기본적인 움직임(움직임, 점프 등)은 작성되어 있었으나 달리기가 없어 추가해주고자 하였다.
public class PlayerController : MonoBehaviour
{
public StatManager statManager;
private PlayerCondition playerCondition;
private float moveSpeed;
private Rigidbody _rigidbody;
void FixedUpdate()
{
Move();
}
void Move()
{
Vector3 dir = transform.forward * curMovementInput.y + transform.right * curMovementInput.x;
dir *= moveSpeed;
dir.y = _rigidbody.velocity.y;
_rigidbody.velocity = dir;
}
public void OnMove(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
curMovementInput = context.ReadValue<Vector2>();
}
else if (context.phase == InputActionPhase.Canceled)
{
curMovementInput = Vector2.zero;
}
}
- 이동 입력/키보드 입력(OnMove)을 받아 curMovementInput에 저장, 키를 떼면 0으로 만들어서 멈춘다.
- 이동 처리(Move)는 입력된 방향으로 속도를 계산하고 현재속도를 Rigidbody에 적용해서 이동, 매 프레임마다 Move()를 실행해서 이동 반영하는 원리로 작동한다.
키보드 입력은 InputSystem을 이용하여 설정해주었다.
달리기 키는 Left Shift로 누르는 동안에 달리기가 되는 방식으로 하고자 했고, 내 생각에선 단순히 달리기는 OnMove가 작동할때 속도가 2배로 적용되면 되겠지 라는 생각으로 코드를 작성하기 시작했다.
public void OnRun(InputAction.CallbackContext context)
{
playerCondition = GetComponent<PlayerCondition>();
if (context.phase == InputActionPhase.Performed)
{
if (playerCondition.UseStamina(10f))
{
moveSpeed *= 2;
}
}
else if (context.phase == InputActionPhase.Canceled)
{
moveSpeed /= 2;
}
}
정말 단순하게 계산했다.
이동(OnMove)하는 순간에만 작동이 되고, 그 속도에서 2배가 되면 달리기라고 생각했기 때문에 키가 입력받는 동안은 이동속도가 2배가 되게하고, 달리기를 무한으로 하게할순 없으니 달리는동안 스태미나를 닳게하고, 스태미나가 일정수준 이하가 되면 달리지 못하도록 막아줬다.
그런데.. 달리기는 잘 작동이 되는데.. 스태미나가 시프트를 누른 최초의 순간만 딱 닳고 계속 회복이 되는게 아닌가......
내가 뭘 놓친거지 싶어 Run()에 moveSpeed *= 2를 넣어주고 OnRun에는 Run()만 호출해주는 방식으로도 해보았고
스태미나가 지속적으로 닳게 따로 코드도 추가도 해보았고.. 정말 몇시간동안 열심히 삽질했다.
나는 OnRun과 UseStamina에 문제가 있는줄 알고 계속 수정하다 UseStamina는 지속적으로 감소하는 로직이 없으니 안되는게 아닐까 싶어 LoseStamina라는 메서드를 추가해주었다.
public class PlayerCondition : StatManager
{
void LoseStamina(float amount)
{
value -= amount * Time.deltaTime;
if (value < 0)
{
value = 0;
}
}
}
위 함수에선 초마다 감소는 하되 0이 되면 행동을 하지 못하게 하는 로직은 빠져있는데, 근데 일단 이 함수조차 실행이 되지 않고 계속 키를 누를때 1번만 스태미나가 감소하고 끝이었다.
다시 열심히 삽질하고... 결국 혼자서 해결하다 포기하고 도움받아 문제를 찾았는데
PlayerCondition의 Update에 보면 지속적으로 스태미나를 회복할수있게 해주었는데 스태미나가 지속적으로 회복만 되는 구조로 되어있어 소모하는 로직이 필요하다면 코드를 수정해야 했던 것이었다.
public class PlayerCondition : StatManager
{
public bool useStamina = false;
public void Update()
{
if (useStamina)
{
curStamina = Mathf.Max(curStamina - passiveStamina * Time.deltaTime, 0f);
}
else
{
curStamina = Mathf.Min(curStamina + passiveStamina * Time.deltaTime, maxStamina);
}
}
}
useStamina라는 변수를 bool값으로 만들어주었고, true일땐 스태미나가 소모, false일땐 스태미나가 회복되는 식으로 작성해주었다.
LoseStamina는 간단하게 스태미나가 true이면 false로 바꾸는 식으로 반전되게 해주었다.
그리고 OnRun에서 LoseStamina를 호출해주면 아무것도 하지않아 useStamina가 false로 되어있는 것을 true로 바꿔주어 소모할수 있게 해주었다.
public class PlayerController : MonoBehaviour
{
public void OnRun(InputAction.CallbackContext context)
{
var playerCondition = GetComponent<PlayerCondition>();
if (context.phase == InputActionPhase.Performed)
{
if (playerCondition.curStamina >= 10f)
{
moveSpeed *= 2f;
playerCondition.LoseStamina();
}
}
else if (context.phase == InputActionPhase.Canceled)
{
moveSpeed /= 2f;
playerCondition.LoseStamina();
}
}
}
키를 누르면 스태미나가 10 이상일 때 이동속도를 2배로 만들고 스태미나를 소모, 키를 떼면 원상복구와 동시에 스태미나 소모를 멈추게 하였다.
이렇게 하여 달리기 기능도 정상적으로 된다!
이제 애니메이션 해야겠지..?
'Unity > Unity Study' 카테고리의 다른 글
| [Unity] 3D Surviver 팀 프로젝트 중, 상태(Stat) UI (0) | 2025.03.20 |
|---|---|
| [Unity] 3D Surviver 팀 프로젝트 중, Player Animation 설정 (feat. Mixamo) (0) | 2025.03.19 |
| [Unity] 3D Surviver 팀 프로젝트 중, 스탯 부분 리팩토링 (2) | 2025.03.18 |
| [Unity] 3D Surviver 팀 프로젝트 중, 다른 타입으로 발생한 null (0) | 2025.03.14 |
| [Unity] 개인 프로젝트 중, 자원 획득 및 오브젝트 파괴 (0) | 2025.03.13 |