[Unity][2D-Game] Undead Survivor (7)

suhan0304·2023년 11월 5일
0
post-thumbnail

Review

  • 게임 시간에 따라 레벨이 변화되도록 구축했다.
    - 게임 매니저에 게임 시간, 최대 게임 시간을 선언해서 모든 스크립트에서 시간을 알 수 있도록 작성했다.
    - 스포너에서 해당 정보를 기반으로 레벨 측정한다.
  • 레벨에 따라 몬스터들이 변화할 수 있도록 몬스터들의 정보가 담긴 spawnData 클래스 생성한 후 인스펙터에서 값들을 초기화 해주었다.
  • Enemy에서 해당 정보를 가져와 초기 속성값에 적용시킨 후 스폰했다.

강의영상 (7) - 회전하는 근접무기 구현


개발

프리팹 만들기

Bullet 0 스프라이트로 오브젝트를 하나 생성한 후 Tag를 Bullet으로 생성하고 지정해준다. 이후 Bullet에 적용할 Bullet 스크립트를 새로 작성해준다.

Bullet에는 두가지 속성, 데미지와 관통이 필요하므로 해당 속성들을 변수로 선언해준다.

public class Bullet : MonoBehaviour
{
    public float damage;   //데미지
    public int per;        //관통

    public void init(float damage, int per)
    {
        this.damage = damage;   //Bullet의 데미지를 매개변수 데미지로 초기화
        this.per = per;         //Bullet의 관통을 매개변수 관통으로 초기화
    }
}

충돌 여부를 판단하기 위해 Box Collider 2D를 Component로 추가해준후 따로 충돌 이벤트는 없고 충돌 여부만 판단할 것이므로 IsTrigger에 체크 후 해당 오브젝트를 프리팹으로 드래그드랍해서 프리팹으로 만들어준다.


충돌 로직 작성

피격 판정을 위해 Enemy 스크립트에 이벤트 함수를 작성해준다. 이 때 충돌 감지는 OnTriggerEnter2D 함수를 이용한다.

void OnTriggerEnter2D(Collider2D collision)
{
    if (!collision.CompareTag("Bullet")) // 충돌한 collision이 Bullet인지를 먼저 확인
        return;

    health -= collision.GetComponent<Bullet>().damage; //Bullet 스크립트 컴포넌트에서 damage를 가져와서 체력에서 깍는다.

    if (health > 0)
    {
        // .. 아직 살아있음 -> Hit Action 
    }
    else
    {
        // .. 체력이 0보다 작음 -> Die 
        Dead();
    }
}

void Dead()
{
    //비활성화를 해준다.
    gameObject.SetActive(false);
}

근접 무기 생성

근접 무기 프리팹도 오브젝트 풀링으로 사용한다. 따라서 근접 무기 프리팹을 풀 매니저에 등록해준다.

이 때 Bullet이 플레이어의 자식 오브젝트로 들어가 따라다녀야 하므로 Weapon 0으로 오브젝트를 생성해준다. 그리고 무기 생성 및 관리를 담당하는 스크립트 Weapon을 생성해고 Weapon 0 오브젝트에 등록해준다.

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

public class Weapon : MonoBehaviour
{   
    public int id;          //무기 id
    public int prefabId;    //프리팹 id
    public float damage;    //데미지
    public int count;       //개수
    public float speed;     //속도

    private void Start()
    {
        Init();
    }

    void Update()
    {
        //무기 id에 맞게 로직 구현
        switch (id)
        {
            case 0:
                //z축 방향으로 back 방향으로 회전 (Speed가 음수라서 back 방향으로 지정)
                transform.Rotate(Vector3.back * speed * Time.deltaTime); 
                break;
            default:
                break;
        }
    }

    public void Init()
    {

        //무기 id에 맞게 무기 속성을 설정
        switch(id)
        {
            case 0:
                speed = -150;   //마이너스 = 시계방향
                Batch();        //무기 배치          
                break;
            default:
                break;
        }
    }

    void Batch()// 생성된 무기를 배치하는 함수
    {
        for(int i=0; i<count;i++)
        {
            //prefabId에 해당하는 prefab을 가져오면서 동시에 transform을 지역변수에 저장
            Transform Bullet = GameManager.Instance.pool.Get(prefabId).transform;
            Bullet.parent = transform; //새로 만들어진 Bullet의 부모를 Weapon 0 오브젝트로 설정

            //Bullet의 Bullet 스크립트의 init 함수로 데미지 관통 초기화
            Bullet.GetComponent<Bullet>().init(damage, -1); // -1 is Infinity Per. (근접공격은 무한 관통)
        }
    }
}

현재 Weapon이 플레이어의 위치에서 생성되어 제자리에서만 회전한다. 따라서 초기 위치를 설정한 후 배치해주어 플레이어 주위에서 잘 회전되도록 로직을 구현해야 한다.
(Scene)


근접 무기 배치

360도를 무기 갯수로 나눈 각도 만큼 회전 시킨 후 위쪽 방향으로 1.5만큼 이동시켜주면된다.

위쪽 방향?

Scene 창에서 Local로 설정하게 되면 월드 기준으로 방향이 아니라 오브젝트 기준으로 방향을 확인할 수 있다. 즉 회전되는 각도를 기준으로 방향이 변경된다. Global은 World 기준으로 방향이 고정

  • Global : 방향이 고정

  • Local : 회전 정도에 방향이 변경

따라서 회전 정도에 방향이 변경되는 Local 기준으로 우리는 항상 위쪽으로 1.5만큼만 이동시켜주면 된다.

회전 이후 위쪽으로 1.5만큼만 이동하면 된다. 회전을 시켰기 때문에 동일한 위쪽이어도 이동하는 방향이 다르다.

//Local 기준으로 방향이 위쪽으로 1.5만큼 이동 
//이동 방향이 Space.self가 아니라 World인 이유는? 이미 회전 후 위쪽 방향으로 1.5만큼 이동시키는 것으로 했으므로 이동 방향은 월드를 기준으로 설정
bullet.Translate(bullet.up * 1.5f, Space.World); 

레벨에 따른 배치

무기가 레벨에 따라 데미지와 카운트가 재설정되도록 레벨업 기능 함수를 Weapon안에 구현한다.

Space.World 위치로 프리팹을 생성하면 초기 위치가 이상하게 설정된다. 따라서 플레이어 기준, Local Position과 Local Rotation을 0으로 초기화 한 후에 옮겨 주어야한다.

이미 사용중인 자식 오브젝트, 즉 생성된 무기가 있다면 새로 무기를 생성할 때 이미 가지고 있는 자식 오브젝트를 사용하고 부족하면 그 때 풀링에서 새로운 무기 오브젝트를 꺼내오는 방식으로 구현했다.

void Batch()// 생성된 무기를 배치하는 함수
{
    for(int i=0; i<count;i++)
    {
        //prefabId에 해당하는 prefab을 가져오면서 동시에 transform을 지역변수에 저장
        Transform bullet;
        // 기존 오브젝트를 먼저 활용하고 모자란 것은 풀링에서 가져오기
        if(i < transform.childCount) // 자식을 가지고 있으면 새로 꺼내지 않고
        {
            bullet = transform.GetChild(i);  //기존의 자식들을 가져다 쓴다.
        }
        else
        {
            bullet= GameManager.Instance.pool.Get(prefabId).transform;
            bullet.parent = transform;  //새로 가져오는 것들만 parent를 설정해주면 된다. 
                                        //- 기존 자식 오브젝트로 사용중이던 것은 이미 설정이 되어있음
        }

        bullet.localPosition = Vector3.zero; //bullet의 localPostion이 0으로 초기화 = 플레이어의 위치
        bullet.localRotation = Quaternion.identity; //Rotation값은 Quaternion형 값, 초기값은 identity

        Vector3 rotVec = Vector3.forward * 360 * i / count; //i번째 무기의 회전 각도를 계산
        bullet.Rotate(rotVec);                              //rotVec만큼 회전
        //Local 기준으로 방향이 위쪽으로 1.5만큼 이동 
        //이동 방향이 Space.self가 아니라 World인 이유는? 이미 회전 후 위쪽 방향으로 1.5만큼 이동시키는 것으로 했으므로 이동 방향은 월드를 기준으로 설정
        bullet.Translate(bullet.up * 1.5f, Space.World);    
        //Bullet의 Bullet 스크립트의 init 함수로 데미지 관통 초기화
        bullet.GetComponent<Bullet>().init(damage, -1); // -1 is Infinity Per. (근접공격은 무한 관통)
    }
}

결과물

profile
Be Honest, Be Harder, Be Stronger

0개의 댓글