One_KWS

게임 개발 일지 #9 - 아이템 본문

게임 개발

게임 개발 일지 #9 - 아이템

One-Kim 2023. 3. 7. 22:20

몬스터를 잡아 아이템을 획득하고 이후 아이템으로 교환할 수 있도록 구현하려고 한다. 이번 일지에는 아이템을 획득하는 것까지 구현한 내용을 정리했다.

아이템 루팅

아이템 

Item GameObject를 생성하고 Collider와 Rigidbody를 붙여주었다. 아이템에 둥둥 떠있는 모션을 넣기 위해 애니메이션을 추가하여 y축으로 올라갔다가 내려왔다가 하도록 구현했다. 

y축으로 0.5만큼 올라갔다가 내려오는 애니메이션
아이템 애니메이션

Item 스크립트를 생성하여 붙여주고 아래와 같이 코드를 작성했다. Target을 정해주면 해당 타겟을 따라가는 코드이다. 부드럽게 따라가도록 Vector3.SmoothDamp를 사용했다. (목표까지 일정한 속도로 움직이는 것이 아니라 속도가 점점 변하면서 목표 지점까지 이동한다.)

public Transform Target { get; set; }
private readonly float SMOOTH_TIME = 10f; 
private Vector3 velocity;

private void Awake() {
    collider = GetComponent<Collider>();
}

private void FixedUpdate() {
    if (Target == null) {
        return;
    }

    transform.position = Vector3.SmoothDamp(transform.position, Target.position, ref velocity,
                Time.deltaTime * SMOOTH_TIME);
}

 

몬스터의 자식 오브젝트로 Item을 놓고 EnemyController의 코드를 수정했다.

private void OnEnemyDead() {
    ResetBehaviour();

    characterController.enabled = false;

    DropItem();
}

private void DropItem() {
    item.gameObject.SetActive(true);

    item.Target = target;
}

 

실행시켜보니 아이템이 잘 따라온다.

 

아이템 드랍

위의 코드처럼 하면 몬스터가 죽자마자 아이템이 나온다. 자연스럽게 만들기 위해 몬스터가 죽고 조금 뒤에 아이템이 나오도록 했다. 아이템이 나오기 전 몬스터가 터지는 효과도 추가했다.

[SerializeField] private GameObject deathEffect;

...

 private async UniTaskVoid DropItem() {
    // 몬스터가 죽고 2초 뒤에 이펙트가 재생되고 아이템이 나온다.
    await UniTask.Delay(2000); 

    body.SetActive(false);
    deathEffect.SetActive(true);
    item.gameObject.SetActive(true);

    // 아이템이 나오고 0.8초 뒤에 플레이어를 따라간다.
    await UniTask.Delay(800);

    item.Target = target;
}
몬스터가 죽을 떄 위의 효과가 실행되도록 했다.

실행 결과 몬스터가 죽고 이펙트가 터지면서 아이템도 잘 따라오는 것을 볼 수 있었다. 

 

아이템 획득

플레이어의 아이템 획득을 구현하기 위해 Item 클래스에 OnTriggerEnter를 추가하고 플레이어와 충돌했을 경우 Destroy 하도록 구현했다.

private void OnTriggerEnter(Collider other) {
    if (other.CompareTag("Player")) {
        Destroy(gameObject);
    }
}

아이템 획득

아이템 획득을 좀더 자연스럽게 하기 위해 이펙트를 추가했다. Item의 자식 오브젝트로 추가한 후 Item이 Destroy 되기 전 이펙트를 실행해주도록 수정했다. 충돌 후에도 Collider가 동작하지 않도록 한번의 충돌후 enable을 false로 바꿨고 이펙트만 보여지도록 Item의 몸체는 비활성화 시켜주었다. 

[SerializeField] private GameObject grabEffect;
[SerializeField] private GameObject body;

private Collider collider;

private void Awake() {
    collider = GetComponent<Collider>();
}

private void OnTriggerEnter(Collider other) {
    if (other.CompareTag("Player")) {
        collider.enabled = false;
        body.SetActive(false);
        // 이펙트가 충돌된 위치가 아닌 플레이어 위치에서 플레이되도록 위치를 조정한다.
        transform.position = other.transform.position; 
        
        OnGrabItem().Forget();
    }
}

private async UniTaskVoid OnGrabItem() {
    //이펙트 실행 전 0.5초 기다리게 하여 좀 더 자연스럽게 보이도록 했다.
    UniTask.Delay(500);
    grabEffect.SetActive(true);
    
    Destroy(gameObject, 2f);
}

아이템 획득 이펙트 추가

 

획득한 아이템 개수 표시

아이템이 플레이어와 충돌했을 때 아이템을 구별하기 위해 아이템의 Tag를 수정했다.

 

PlayerController에 OnGetItem 델리게이트와 OnTriggerEnter 함수를 추가하고 충돌한 오브젝트가 Item일 경우 OnGetItem를 실행시켜주도록 구현했다.

public Action OnGetItem;

private void OnTriggerEnter(Collider other) {
    if (other.transform.CompareTag("Item")) {
        OnGetItem?.Invoke();
    }
}

 

GameManager에서 플레이어가 아이템을 획득했을 경우 현재 아이템 개수를 증가시키도록 이벤트를 연결했다.  

private void Start() {
    ...
    playerController.OnGetItem = OnGetItem;
    ...
}

private void OnGetItem() {
    itemCount += 1;
}

 

itemCount가 제대로 증가하는지 확인하기 위해 UI에 표시해주었다.

 

아이템 개수 표시는 다음에 플레이어 정보 UI나 아이템 UI를 만들면 그쪽에 표시해 주는 방식으로 수정해야겠다. 

 

 

사용 에셋

Character

POLYGON Modular Fantasy Hero Characters (Synty Studios)

POLYGON Fantasy Rivals (Synty Studios)

 

Animation

Oriental Sword AnimSet (wemakethegame)

DOTween (Demigiant)

 

VFX

Magic Arsenal (Magic Arsenal)

 

UI

GUI PRO Kit - Fantasy RPG (Layer Lab)

 

ETC

UniTask (neuecc - Yoshifumi Kawai)

 

참고한 자료

Drop Loot Auto Flyback to Player with Unity - Indie Nuggets (YouTube)