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

suhan0304·2023년 11월 6일
0

유니티 - Undead Survivor

목록 보기
10/21
post-thumbnail
post-custom-banner

Review

  • 근접무기 프리팹을 만든 후에 해당 프리팹을 오브젝트 풀링으로 불러와 여러개를 사용할 수 있도록 구현
  • 충돌 했을때 몬스터의 체력이 닳도록 OnTriggerEnter2D 함수를 이용해 충돌 로직 작성
  • 근접 무기가 플레이어 주변을 회전하도록 구현

강의영상 (8) - 자동 원거리 공격 구현


개발

몬스터 검색 구현

Layer을 이용해 몬스터를 Scan할 수 있도록 구현한다.

Layer : 물리, 시스템 상으로 구분짓기 위한 요소

Enemy 레이어 추가 후 프리팹에서 지정했다. 이후에 몬스터 스캔을 담당한 Scanner 스크립트를 생성했다.

    void FixedUpdate()
    {
        // 원형의 캐스트를 쏘고 모든 결과를 반환하는 함수 
        //우리의 목적? 플레이어를 중심으로 원으로 객체를 탐색
        targets = Physics2D.CircleCastAll(transform.position, scanRange, Vector2.zero, 0, targetLayer);
        //CircleCastAll     (1.캐스팅 시작위치, 
        //                   2. 원의 반지름, 
        //                   3. 캐스팅 방향,  => 지금 우리는 특정 방향으로 캐스트를 쏘는 것이 목적이 아니므로Vector2.zero 사용
        //                   4. 캐스팅 길이,  => 캐스팅을 쏘는 것이 목적이 아니라 길이가 0 
        //                   5. 대상 레이어)    
    }

CircleCastAll 함수로 특정 레이어에 스캔 범위 안에 있는 오브젝트를 탐색할 수 있다. CircleCastAll은 시작위치를 기준으로 원의 형태로 Cast에 맞은 물리적인 오브젝트를 모두 반환시켜준다.

CircleCastAll(시작위치, 반지름의 길이, 캐스팅 방향, 캐스팅 길이, 대상 레이어)

foreach를 통해 각각의 target을 모두 돌면서 가장 최소의 거리에 있는 타겟의 transform을 반환시키는 GetNearest 함수를 작성했다.

//가장 가까운 오브젝트의 Transform을 반환 시켜줄 함수
Transform GetNearest()
{
    Transform result = null;
    float diff = 100; //플레이어와의 거리

    //foreach 문으로 캐스팅 결와 오브젝트를 하나씩 방문
    foreach (RaycastHit2D target in targets) {  //CircleCastAll에 맞은 애들을 하나씩 접근
        Vector3 myPos = transform.position;             //플레이어 위치
        Vector3 targetPos = target.transform.position;  //타겟의 위치
        float curDiff = Vector3.Distance(myPos, targetPos); //거리를 구한다.

        if(curDiff < diff) //최소 거리를 저장
        {
            diff = curDiff;
            result = target.transform //결과는 가장 가까운 타겟의 transform으로 지정
        }

    }

    return result;
}

해당 함수를 FixedUpdate 안에서 호출해주면서 nearest 몬스터를 계속 탐색해준다.

nearest Target에 들어온 Enemy 오브젝트를 따라가보면 다음과 같이 제일 가까운 몬스터임을 알 수 있다.


프리팹 만들기

Bullet 0을 가져와서 Sprite Renderer의 Sprite를 Bullet3로 바꾸어준 후 Box Collider 2D 컴포넌트를 리셋시켜 스프라이트 크기에 콜라이더 크기를 맞추어야 한다.
(캡슐 콜라이더를 추가해줘도 무방함 - Is Trigge 꼭 체크해주기)

기존 컴포넌트를 삭제해도 삭제가 바로 안되는데 이는 프리팹과 연결되 오브젝트는 변경된 점을 계속 보여주기 때문이다.

이름을 Bullet 1로 설정후 프리팹 폴더에 드래그드랍한 후 original Prefab으로 하나 새로 만들어주었다.


총탄 생성하기

Weapon 0 오브젝트를 Weapon 1로 복사해 준 후 다음 사진과 같은 값들로 초기화해주었다.

시간 단위로 발사하기 위해 타이머를 위한 float Timer 변수를 추가한 후에 nearestTarget에 접근할 수 있도록 Player에 검색 클래스 타입 변수 선언 및 초기화를 진행해주었다

직접 만든 스크립트도 컴포넌트와 동일하게 취급되므로 선언후에 GetComponent로 가져올 수 있다.

void Fire() 
{
    if (!player.scanner.nearestTarget) //nearestTarget이 없다면 return
        return;

    Transform bullet = GameManager.Instance.pool.Get(prefabId).transform;
    bullet.position = transform.position;
}

스크립트 완성 후 실행하면 생성이 정상적으로 수행된다.
이제 총탄의 발사 로직을 구현하자.


총탄 발사하기

총탄은 속도가 필요하므로 Rigidbody 2D 컴포넌트를 추가해준다. 중력의 영향을 받지 않도록 Gravity Scale = 0으로 지정해준다.

Bullet.cs

총알 스크립트에서도 리지드바디 2D 변수를 선언해준후 초기화를 진행해야 사용가능하다. 또한 init 함수에서 방향 벡터 dir을 추가해서 관통이 -1 (무한)보다 큰 것에 대해서 속도를 적용시켜주었다. 기존 근접 무기의 init에서 오류가 생기는데 zero 벡터를 넣어줘서 오류를 해결한다. dir 벡터는 총탄에서만 사용하므로 근접 무기는 의미가 없다.

총탄은 관통이 아니기 때문에 OnTriggerEner2D함수를 bullet안에 추가로 작성해주었다.

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

    if(per > -1)
    {
        rigid.velocity = dir * 15f; //속도는 15로 사용 
    }
}

Weapon.cs

이제 Weapon 스크립트를 수정해주자. 가장 먼저 방향을 구하기 위해 목표 위치 - 나의 위치로 방향을 구한 후 크기를 1로 정규화 시켜준다. 그런 다음 회전을 위해 FromToRotation으로 회전시켜준다.

void Fire()
{
    if (!player.scanner.nearestTarget) //nearestTarget이 없다면 return
        return;

    Vector3 targetPos = player.scanner.nearestTarget.position;
    Vector3 dir = targetPos - transform.position; //크기가 포함된 방향 : 목표 위치 - 나의 위치
    dir = dir.normalized; //현재 벡터의 방향은 유지한체 크기만 1로 정규화 시켜준다.

    Transform bullet = GameManager.Instance.pool.Get(prefabId).transform;
    bullet.position = transform.position;
    //지정된 축을 중심으로 목표를 향해 회전하는 함수
    bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir);
    bullet.GetComponent<Bullet>().Init(damage, count, dir); //관통을 count로 지정
}

FromToRotation(축, 방향) : 지정된 축을 중심으로 목표를 향해 회전하는 함수
- Quaternion.FromToRotation(Vector3.up, dir); // up 방향 (0, 0, 1) = z 축을 중심으로 dir 방향을 향해 회전하겠다.

총탄 비활성화

다음과 같이 Bullet 스크립트를 수정해서 원거리 공격 무기가 일정거리 이상 멀어지면 게임 오브젝트 비활성화 시킬 수 있다.

void Update() //Dead를 프레임 단위로 수행
{
    Dead();
}

void Dead() //player와의 거리가 20보다 멀어지면 비활성화 되도록 설정
{
    Transform target = GameManager.Instance.player.transform;
    Vector3 targetPos = target.position;
    float dir = Vector3.Distance(targetPos, transform.position);
    if (dir > 20f)
    {
        rigid.velocity = Vector2.zero;//비활성화 이전에 재사용을 위해 미리 물리 속도 초기화
        this.gameObject.SetActive(false); //비활성화
    }
}

결과물

profile
Be Honest, Be Harder, Be Stronger
post-custom-banner

0개의 댓글