[Unity Engine] 유니티 엔진 게임 개발 일지 #7

신형석·2023년 5월 1일
0

게임 개발 일지

목록 보기
9/10

저번 게시글에 이어서, 이번에는 스태미너 시스템과 Key의 개수를 나타내주는 UI를 구현해보겠다.

우선 카메라에 붙어있는 UI에, 스태미너와 Key 개수를 보여주는 Text UI를 붙여보겠다.

우리가 게임을 할 때, 스태미너와 체력은 흔히 왼쪽 아래 쪽에 붙어있는 모습을 자주 확인할 수 있다. 그래서 작성자 본인도 스태미너는 왼쪽 아래 부분에 숫자로 표시할 수 있도록 해놓았다.

또한, 우리가 물품의 개수를 확인하려면 원래는 인벤토리를 열어서 확인하는 방법이 기본이지만, 본인의 지식으로는 인벤토리까지는 아직 구현하지 못했기 때문에 왼쪽 윗 부분에 표시할 수 있게 해놓았다.


게임 내부에서는 이렇게 확인할 수 있을 것이다.

그 다음, 스태미너 시스템을 한번 확인해 보겠다.

스태미너 구현

스태미너란, 플레이어가 일정한 수치를 가지고 이 수치를 사용하면서 달릴 수 있는 시스템을 의미한다. 게임을 구상할 때, 플레이어는 몬스터나 적보다 기본적으로 더 느린 속도를 가지지만, 뛰게 된다면 몬스터보다 훨씬 빠르게 다니게 되는 게임이 많다. 그러나, 이러한 게임에서 스태미너라는 존재가 없다면?

플레이어는 게임 시작부터 끝까지 달리기만 하게 될 것이고, 이는 플레이어의 재미 하락까지 이어질 수도 있을 것이다. 이러한 문제점을 줄이기 위해 만드는 시스템이 바로 스태미너이다.

대부분의 게임에서, 달리기는 쉬프트(Shift)로 구현되어있다. 그럼 이렇게 한번 생각해보자.

  1. 플레이어는 일정 수치의 스태미너(Stamina)를 가지고 있다.
  2. 게이머가 쉬프트(Shift) 키를 꾹 누르면, 플레이어는 달리는 애니메이션을 취하고, 이동속도가 빨라진다.
    2-1. 만약 달리는 상태에서, 스태미너가 다 닳지 않았다면 계속 달리는 모션을 취한다.
    2-2. 스태미너가 다 닳았다면 걷는 모션을 취하게 된다.
  3. 플레이어가 뛰지 않고, 스태미너가 가득 채워져있지 않다면, 스태미너는 최대 수치까지 일정 속도로 회복된다.

스태미너에 관련된 내용은 이 정도로 정리할 수 있겠다. 우선,

1. 플레이어는 일정 수치의 스태미너(Stamina)를 가지고 있다

를 먼저 해결해보자. 이건 아주 간단하다. 그저 변수를 하나 지정해주면 되는 문제이다.

public float stamina = 1000f;
private float maxStamina;

stamina라는 float형 변수를 설정, public으로 공개한다. 스태미너가 줄어들면 난이도가 올라가므로, stamina 변수는 바꿀 수 있으면 좋겠다고 판단하였다.

private형으로, float형 변수 maxStamina도 만들었다. 이는 Start()에서 stamina의 값과 같게 만들어 줄 것이다.

또한, 외부에서 정의한 Text_UI 변수도 필요하기 때문에

[SerializeField] TMP_Text stamina_UI;

[SerializeField]를 이용하여 외부에서 받아올 수 있도록 하였다.

maxStamina = stamina;
stamina_UI.text = ((int)(stamina / maxStamina * 100f)).ToString() + "%";

Start() 함수 안에 구현한 명령문들의 내용이다. 위에서 말했듯이, stamina 변수의 값을 maxStamina에 넣어준다. 그 후, stamina_UI의 text를 위의 수식과 같이 바꾼다. 우리는 퍼센테이지 단위 (%)를 사용할 것이기 때문에, 현재 나의 스태미너(stamina)를 최대 스태미너(maxStamina)로 나눈 후, 100f로 나눈다. 이를 (int)로 정수형으로 만든 뒤, ToString()을 이용하여 문자열로 만든다. 그 후 "%"를 붙여주면 "XX%"로 표현이 될 것이다.

2. 게이머가 쉬프트(Shift) 키를 꾹 누르면, 플레이어는 달리는 애니메이션을 취하고, 이동속도가 빨라진다.

이 문제를 해결하여 보자. 우선, Shift를 눌렀다는 입력을 받기 위해서는 Input.GetKey()의 도움이 필요할 것이다. 또한, 달리는 상태인지 아닌지를 구별하기 위해서는 bool 형태의 변수가 하나 필요하다고 판단하였다.

private bool canRun = false;

변수를 코드 초기에 하나 설정해주고,

if (Input.GetKey(KeyCode.LeftShift) && stamina > 0) 
// stamina > 0 코드를 설정해주지 않으면, 쉬프트를 누르고 있는 한 계속 달리는 모션을 취할 것이다. 
    {
    canRun = true;
}
else {
    canRun = false;
}

Update() 함수 안에 이렇게 써주면, 우리는 Shift 키를 눌렀을 때는 달리는 것, 그렇지 않을 때는 걷는 것이라고 canRun 변수를 보고 판단할 수 있을 것이다. 하지만, 이렇게 해서는 실제로 stamina가 줄거나 행동이 바뀌거나 하지는 않는다. 조금 내용을 추가해보겠다.

2번 문제의 소항목에 대해

우리는 이제, 스태미너에 따라 모션이 달라지고 실제로 UI에 적용되게 만들어야 한다. 우선, UI에 적용되도록 하기 위해 새로운 함수를 하나 정의했다.

private void UpdateST()
{
    // stamina in UI system. It shows how many stamina left.
    stamina_UI.text = ((int)(stamina / maxStamina * 100f)).ToString() + "%";
} 

UpdateST라는 이 함수는, 현재 스태미너의 값을 계산하여 UI를 바꿔주는 간단한 함수이다. 이 함수를

if (Input.GetKey(KeyCode.LeftShift) && stamina > 0) 
// stamina > 0 코드를 설정해주지 않으면, 쉬프트를 누르고 있는 한 계속 달리는 모션을 취할 것이다. 
    {
    canRun = true;
    UpdateST();
}
else {
    canRun = false;
    UpdateST();
}

위에 적힌 코드에 적어주면 간단하게 UI에 적용되는 기능을 만들 수 있다.
이렇게 설정을 해주어도, stamina가 실제로 바뀌는 건 없기 때문에 수치가 직접 바뀌는 것은 볼 수 없을 것이다. 그래서 스태미너를 사용하는 첫 If 문에

// I added this codes to make stamina system
if (Input.GetKey(KeyCode.LeftShift) && stamina > 0)
// When I press leftShift key and stamina is over than 0, 
{
    // use stamina
    stamina = stamina - 2;
    canRun = true; // make bool var to true to know that player can run
    UpdateST();
}
else if(stamina < maxStamina) {
// When I don't press leftShift and stamina is lower than maxStamina, 
    stamina += .5f; // Stamina restores and
    canRun = false; // make bool var to false to know that player can't run
    UpdateST();
}

주석까지 곁들여서 코드를 완성해 보았다. 이로서, 쉬프트를 누르면 UI의 스태미너가 줄어들고, 쉬프트를 때면 스태미너가 회복하는 것을 알 수 있다.

속도를 바꿔주는 것은 은근히 쉬웠다. 본인은 이미 유니티에서 제작된 ThirdPersonController.cs에 코드를 작성하는 것이었기 때문에 위치가 헷갈렸지만, 생각보다는 간단한 것이었다.

이 코드에서는, Update() 함수 안에 여러 함수들이 있고, 그 중 Move() 함수가 있다. 이 함수는 말 그대로 플레이어 마네킹의 움직임을 담당하는 함수이다.

Move() 함수의 첫 줄은 이렇다:

float targetSpeed = _input.sprint ? SprintSpeed : MoveSpeed;

간단하다. targetSpeed가 _input.sprint라면, SprintSpeed가 되고 아니면 MoveSpeed가 되는 코드이다. 이 뒤의 코드에서는 targetSpeed만을 가지고 연산을 하기 때문에, 우리는 여기서 targetSpeed를 살짝만 바꿔주면 된다.

if (!canRun)
{
	targetSpeed = MoveSpeed;
}

위에서 설정한 전역 private bool형 변수 canRun을 사용, 달릴 수 없다면 MoveSpeed로 강제로 바꿔주기만 하면 된다. 나머지는 코드 안에서 다 바뀌므로, 이렇게만 바꿔도 충분히 제 기능을 하는 함수가 되었다.

완료된 작업은 이곳에서 확인할 수 있다. 스태미너 작업이 끝났으니, 이제 Key의 개수를 UI로 출력하는 방법을 알아보겠다.

Key는 이렇게 작동할 것이다:

  1. Key에 가까이 가면, 주울 수 있는 키가 플레이어에게 뜬다.
  2. 그 키를 누르면, Key는 월드에서 삭제되고 Key Text에 1을 추가한다.
  3. 키를 누르지 않고 밖에 나간다면, 주울 수 있는 키 UI가 없어진다.

1, 3번은 이미 이전에 구현해 놓았다. 그러므로, 이번에는 2번만 구현하면 된다.

키를 누르면, Key는 월드에서 삭제되고 Key Text에 1을 추가한다.

우선 한 가지 주의사항이 있다.

만약 우리가 Key의 근처에 있어도, Key를 가져갈 수 없는 상황인 경우에는 가져갈 수 없어야 한다. 그러므로, 그러한 상황을 확인할 수 있는 bool 변수가 필요한데, 그것이 이 전 포스팅에서 짚고 넘어가지 않은 isReadyToTaken 함수이다.

private bool isReadyToTaken = false;

이 변수만 본다면, 우리는 플레이어가 Key를 가지고 갈 수 있는지 없는지에 대해 판단할 수 있다.

이를 바탕으로, 함수를 하나 만들었다.

void TakeObject(){
    // Function to take key
    if (Input.GetKeyDown(KeyCode.E) && isReadyToTaken){
        // Define pressing key and bool var
        currentKey++; // we gained a key! - 1
        string new_Text = "Key : " + currentKey.ToString(); // change UI Text - 2
        key_UI.text = new_Text; // - 3
        isReadyToTaken = false; // when we take key, we can't take key anymore - 4
        tmp.gameObject.SetActive(false); // off key UI Text - 5
        Destroy(key); // Destroying key - 6
    }
}
  1. 우선, if문을 이용하여 E를 눌렀을 경우와 플레이어의 Key를 가져갈 수 있는지 여부를 파악해, 둘 다 true라면 현재 Key 변수(currentKey)에 1을 더해준다.

  2. 그 후, 현재 Key 갯수를 보여줄 수 있는 새로운 문자열(new_Text)를 만든다.

  3. key_UI의 text에 위 변수의 값을 대입하면서 UI를 수정해주고,

  4. isReadyToTaken 변수를 false로 바꿔준다.

  5. 그 후, Key가 사라진 판정이기 때문에 UI의 키를 보여주는 Text를 꺼준 후,

  6. Destroy() 함수를 사용하여 Key를 월드에서 삭제한다.

이렇게 간단하게 Key를 얻으면 삭제되고, UI에 반영되는 스크립트를 작성해보았다.

결과는 이곳에서 확인할 수 있다. 다음은, 몬스터 AI에 대해 고민한 내용을 포스팅해보도록 하겠다.

0개의 댓글

관련 채용 정보