본문 바로가기
게임 프로그래밍/게임개발 중급

게임개발 중급(40) - Monster Killer(2)

by jyppro 2023. 5. 31.

Monster Killer(2)

저번부터 밤송이게임을 변형시켜 Monster Killer라고 이름붙힌 새로운 게임을 만들기 시작했습니다. 에셋으로 몬스터를 배치하고 점수를 적용하였고, 오늘은 플레이어의 시점이동 및 부위별 데미지 세분화를 진행해보겠습니다.

 

플레이어 시점이동

이전의 밤송이 게임에서는 카메라가 고정되어있는 상태에서 한 면만 보이는 타겟을 맞추면 되었기에, 게임을 플레이하는 데 큰 지장이 없었습니다. 하지만 저는 3D 게임의 특성을 살리고자 모든 방면에서 공격이 가능하도록 만들고 싶습니다. 그렇게 하려면 카메라가 이동해야 합니다.

 

하지만 그렇다고, 몬스터의 위나 아래로 이동하여 공격하는 것은 부자연스럽습니다. 그렇기에 저는 양옆으로 움직이면서 공격할 수 있는 시스템을 만들어 보겠습니다.

 

CameraController

카메라를 키입력을 통해 양옆으로 이동시킬 수 있는 코드입니다.

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public Transform target; // 중심이 될 물체
    public float radius = 5f; // 원의 반지름
    public float speed = 1f; // 카메라 이동 속도
    public KeyCode leftKey = KeyCode.A; // 원을 그리는 방향 키
    public KeyCode rightKey = KeyCode.D;

    private float angle = 0f; // 카메라 이동을 위한 각도

    private void Update()
    {
        // 왼쪽 방향 키 입력 시
        if (Input.GetKey(leftKey))
        {
            angle -= speed * Time.deltaTime;
        }
        // 오른쪽 방향 키 입력 시
        else if (Input.GetKey(rightKey))
        {
            angle += speed * Time.deltaTime;
        }

        // 각도를 라디안으로 변환하여 카메라 위치 계산
        float radian = angle * Mathf.Deg2Rad;
        float x = target.position.x + Mathf.Cos(radian) * radius;
        float z = target.position.z + Mathf.Sin(radian) * radius;

        // 카메라 위치 설정
        transform.position = new Vector3(x, transform.position.y, z);

        // 카메라가 항상 물체를 향하도록 설정
        transform.LookAt(target);
    }
}

몬스터를 중심으로 양옆으로 이동해야 하기 때문에 원을 기준으로 잡고 움직임을 구현하였습니다. 원의 중심에는 타겟인 몬스터가 항상 있도록 시선을 고정시키고, 그 주변을 일정 거리를 두고 움직이도록 만들었습니다. A는 오른쪽, D는 왼쪽 방향으로 움직이며, 속도와 거리는 public으로 선언되어있기 때문에 인스펙터 창에서 수치 조절이 가능합니다.

 

이렇게 코드를 작성하면, 몬스터를 중심으로 카메라 이동이 가능해집니다.

하지만, 아직 해결되지 않은것이 있습니다. 밤송이는 이전에 작성한 고정된 위치에서만 생성된다는 것입니다. 저는 이것을 제너레이터에 조금의 코드를 추가하여 카메라 위치에서 발사되도록 만들어 주었습니다.

Vector3 spawnPosition = Camera.main.transform.position; // 카메라의 위치를 가져옴
            bamsongi.transform.position = spawnPosition; // 밤송이의 위치를 카메라의 위치로 설정

Update()문 안에 이 코드를 넣어주면, 밤송이가 소환될 위치를 카메라의 현재 위치로 설정할 수 있습니다.

게임을 실행하여 동작을 확인해 보겠습니다.

동작-확인
카메라 이동 및 밤송이 생성

카메라를 A키를 꾹 누르며 왼쪽으로 이동함과 동시에, 카메라의 위치에서 밤송이를 계속 날리는 모습입니다.

 

부위별 데미지 시스템

몬스터를 공격하는 데에 있어서 항상 같은 데미지만 들어가면 재미가 떨어집니다. 그래서 저는 부위별로 데미지의 차등을 주는 방법을 사용할 것입니다. 해당 시스템은 이전에 타겟에서 사용했던 Distance를 변형하여 적용시켜 볼 수 있습니다.

 

먼저, 한쪽 면만 공격하던 이전과 달리, 모든 면에서 데미지가 들어가야 하기 때문에, 콜라이더를 몬스터의 몸집에 맞게 Capsule Collider를 사용하여 감싸줍니다. 그리고, Distance의 위치를 몬스터의 머리쪽에 배치해줍니다. 이렇게 하면 머리에 가까울수록 높은 데미지가 들어가고, 데미지가 멀어질수록 낮아집니다.

 

하지만, 여기서도 문제점이 있습니다. 이전에는 2D의 벡터값을 사용했다면, 지금은 카메라의 위치에 따라 충분히 공격하는 각도와 위치가 변경될 수 있기에, Vector3을 사용해줘야 합니다. 그런데 Distance와 멀어질수록 데미지가 점점 낮아져 음수로 내려가 점수가 오히려 깎이는 현상이 발생합니다. 저는 이 현상을 해결하기 위해 데미지가 0보다 낮게 들어갔다면, 1~3사이의 데미지를 랜덤하게 주도록 고정시켜주었습니다. 코드를 보시겠습니다.

BamsongiController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BamsongiController : MonoBehaviour
{
    public GameObject BamGenerator;
    public GameObject HeadPoint;

    public void Shoot(Vector3 dir) //인자로 3차원 벡터가 입력되고
    {
        GetComponent<Rigidbody>().AddForce(dir); // 들어온 입력벡터 만큼 오브젝트에 힘이 가해진다.
    }

    
    // Start is called before the first frame update
    void Start()
    {
        this.BamGenerator = GameObject.Find("BamsongiGenerator");
        this.HeadPoint = GameObject.Find("HeadPoint");
    }

    public void OnCollisionEnter(Collision collision) //다른 물체와 충돌하는 순간
    {
        GetComponent<Rigidbody>().isKinematic = true; //중력의 영향을 무시
        GetComponent<ParticleSystem>().Play(); // particle효과를 실행시킨다.

        Vector3 p1 = this.transform.position;
        Vector3 p2 = this.HeadPoint.transform.position;
        float d = (p1 - p2).magnitude; // 벡터의 길이 리턴
        int n = Mathf.CeilToInt(10 - d * 5);

        if(n <= 0) {n = Random.Range(1,3);}

        if (collision.gameObject.tag == "Monster") //몬스터와 충돌하는 경우 점수 상승
        {
            BamGenerator.GetComponent<BamsongiGenerator>().ScorePlus(n);
            Destroy(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}

기존의 Distance를 HeadPoint라고 이름을 바꿔주고, if문을 하나 추가하여 데미지를 고정시켰습니다.

게임이 제대로 실행되는지 확인해보겠습니다.

데미지-체크
데미지 체크

같은 위치의 몸통에 5번의 공격을 시행한 결과, 1 - 1점, 2 - 1점, 3 - 1점, 4 - 2점, 5 - 2점으로 총 7점이 들어갔습니다. 3점이 뜨진 않았지만 랜덤하게 적용되는 것을 확인할 수 있었습니다.

 

<NEXT>

이번에는 카메라의 움직임과 그에따른 공격 위치 그리고 머리를 기준으로 하는 데미지 적용방식에 대해서 알아보았습니다. 다음에는 더 게임다운 모습을 갖추도록 점수를 체력바로 바꿔주는 작업을 해주겠습니다. 감사합니다.