기록 보관소

[Unity/유니티] 기초-뱀서라이크: 회전하는 근접무기 구현[07] 본문

유니티 프로젝트/뱀서라이크

[Unity/유니티] 기초-뱀서라이크: 회전하는 근접무기 구현[07]

JongHoon 2023. 6. 1. 23:52

개요

유니티 독학을 위해 아래 링크의 골드메탈님의 영상들을 보고 직접 따라 해보면서 진행 상황을 쓰고 배웠던 점을 요약한다.

https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2 

 

📚유니티 기초 강좌

유니티 게임 개발을 배우고 싶은 분들을 위한 기초 강좌

www.youtube.com


뱀서라이크: 회전하는 근접무기 구현[07]

1. 프리펩 만들기

무기 스프라이트(Bullet0)를 장면에 배치
Bullet 태그 생성 후 오브젝트에 적용
Bullet 스크립트 생성 및 작성

//Bullet Script

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

public class Bullet : MonoBehaviour {
    public float damage;    //피해량 변수
    public int per;         //관통 변수

    //변수 초기화 함수
    public void Init(float damage, int per) {
        this.damage = damage;
        this.per = per;
    }
}

오브젝트에 Box Collider 2D와 Bullet 스크립트 컴포넌트로 추가
무기는 물리적 충돌 없이 영역으로 사용할 것이므로 Is Trigger 체크
Bullet 0 오브젝트를 Prefab으로 만들고 X, Y 위치를 0으로 초기화


2. 충돌 로직 작성

// Enemy Script

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

public class Enemy : MonoBehaviour {
    public RuntimeAnimatorController[] animCon;
    public float health;
    public float maxHealth;
    public float speed;
    public Rigidbody2D target;  //쫓아갈 타겟(플레이어)

    bool isLive;

    Animator anim;
    Rigidbody2D rigid;
    SpriteRenderer spriter;

    void Awake() {
        anim = GetComponent<Animator>();
        rigid = GetComponent<Rigidbody2D>();
        spriter = GetComponent<SpriteRenderer>();
    }

    void FixedUpdate() {
        if (!isLive)
            return;
        Vector2 dirVec = target.position - rigid.position;  // 방향 = 위치 차이의 정규화(Normalized). 위치 차이 = 타겟 위치 - 나의 위치.
        Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
        rigid.MovePosition(rigid.position + nextVec);   //플레이어의 키 입력값을 더한 이동 = 몬스터의 방향 값을 더한 이동
        rigid.velocity = Vector2.zero; //물리 속도가 이동에 영향을 주지 않도록 속도 제거
    }

    void LateUpdate() {
        if (!isLive)
            return;
        spriter.flipX = target.position.x < rigid.position.x;
    }

    void OnEnable() {
        target = GameManager.instance.player.GetComponent<Rigidbody2D>();   //플레이어 할당
        isLive = true;
        health = maxHealth;
    }

    //데이터를 가져오기 위한 초기화 함수
    public void Init(SpawnData data) {
        anim.runtimeAnimatorController = animCon[data.spriteType];
        speed = data.speed;
        maxHealth = data.health;
        health = data.health;
    }

    void OnTriggerEnter2D(Collider2D collision) {
        if (!collision.CompareTag("Bullet"))
            return;

        health -= collision.GetComponent<Bullet>().damage;  //총알 데미지만큼 Enemy 체력 감소

        if(health > 0) {
            // .. Live, Hit Action

        }
        else {
            // .. Die
            Dead();
        }
    }

    void Dead() {
        gameObject.SetActive(false);
    }
}

테스트용으로 무기를 여러개 만들어 플레이어 아래에 배치한다. 무기 스크립트의 damage 변수도 11로 변경하고 실행.
무기 피해량이 10보다 높아서 설정해서 좀비들은 닿자마자 바로 죽는다.


3. 근접 무기 배치

플레이어 아래에 무기를 배치할 빈 오브젝트 생성 후 이름 변경
Weapon 스크립트 생성 후 Weapon0 오브젝트에 추가
PoolManager Prefabs에 Bullet 0 설정

//Weapon Script

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

public class Weapon : MonoBehaviour {
    public int id;
    public int prefabId;
    public float damage;
    public int count;
    public float speed;

    void Start() {
        Init();
    }

    void Update() {
        
    }

    public void Init() {
        switch (id) {
            case 0:
                speed = -150;
                Batch();
                break;
            default:
                break;
        }
    }

    void Batch() {  //생성된 무기를 배치하는 함수
        for (int index = 0; index < count; index++) {
            Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
            bullet.parent = transform;
            bullet.GetComponent<Bullet>().Init(damage, -1); //-1 is Infinity Per. 무한 관통.

        }
    }
}

무기 관리 오브젝트 속성 수동 설정
테스트를 위해 실행해보면 Bullet 0이 생성된 것을 확인할 수 있다
튀어나온 무기에 몬스터가 닿으면
몬스터가 죽는다

//Weapon Script

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

public class Weapon : MonoBehaviour {
    public int id;
    public int prefabId;
    public float damage;
    public int count;
    public float speed;

    void Start() {
        Init();
    }

    void Update() {
        switch (id) {
            case 0:
                transform.Rotate(Vector3.back * speed * Time.deltaTime);
                break;
            default:
                break;
        }
    }

    public void Init() {
        switch (id) {
            case 0:
                speed = -150;
                Batch();
                break;
            default:
                break;
        }
    }

    void Batch() {  //생성된 무기를 배치하는 함수
        for (int index = 0; index < count; index++) {
            Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
            bullet.parent = transform;
            bullet.GetComponent<Bullet>().Init(damage, -1); //-1 is Infinity Per. 무한 관통.

        }
    }
}
  • 근접 무기가 빙글빙글 돌 수 있게 Update()를 수정

무기가 몬스터 위에 보이도록 Bullet 0의 Sprite Renderer -> Order in Layer를 3으로 수정

  • 이후 두 번째 테스트 플레이를 한다.

테스트 실행 후 무기가 Player 주변을 돌 수 있도록 Y축 값을 1로 변경. 변경하지 않으면 무기가 제자리에서 돌게 된다.
테스트 실행. 무기가 플레이어 주변을 돌면서 몬스터에게 닿으면 피해를 준다.


4. 근접 무기 배치

  • 근접 무기 개수가 늘어날 때마다 순서대로 360을 나눈 값을 Z축에 적용한다.
  • 무기의 위치는 회전한 상태에서 자신의 위쪽으로 이동한다.
//Weapon Script

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

public class Weapon : MonoBehaviour {
    public int id;
    public int prefabId;
    public float damage;
    public int count;
    public float speed;

    void Start() {
        Init();
    }

    void Update() {
        switch (id) {
            case 0:
                transform.Rotate(Vector3.back * speed * Time.deltaTime);
                break;
            default:
                break;
        }
    }

    public void Init() {
        switch (id) {
            case 0:
                speed = -150;
                Batch();
                break;
            default:
                break;
        }
    }

    void Batch() {  //생성된 무기를 배치하는 함수
        for (int index = 0; index < count; index++) {
            Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
            bullet.parent = transform;

            Vector3 rotVec = Vector3.forward * 360 * index / count; //개수에 따라 360도 나누기
            bullet.Rotate(rotVec);
            bullet.Translate(bullet.up * 1.5f, Space.World); //무기 위쪽으로 이동
            bullet.GetComponent<Bullet>().Init(damage, -1); //-1 is Infinity Per. 무한 관통.
        }
    }
}

테스트에 앞서 Count를 5로 변경
근접 무기 회전 테스트. 5개의 무기가 이쁘게 잘 배치되어 작동하고 있다.


5. 레벨에 따른 배치

//Weapon Script

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

public class Weapon : MonoBehaviour {
    public int id;
    public int prefabId;
    public float damage;
    public int count;
    public float speed;

    void Start() {
        Init();
    }

    void Update() {
        switch (id) {
            case 0:
                transform.Rotate(Vector3.back * speed * Time.deltaTime);
                break;
            default:
                break;
        }
        
		//Test Code
        if(Input.GetButtonDown("Jump")) {
            LevelUp(20, 5);
        }
    }

    public void LevelUp(float damage, int count) {
        this.damage = damage;
        this.count += count;

        if (id == 0)
            Batch();
    }

    public void Init() {
        switch (id) {
            case 0:
                speed = -150;
                Batch();
                break;
            default:
                break;
        }
    }

    void Batch() {  //생성된 무기를 배치하는 함수
        for (int index = 0; index < count; index++) {
            Transform bullet;

            if (index < transform.childCount) {
                bullet = transform.GetChild(index); //기존 오브젝트가 있으면 먼저 활용
            }
            else {
                bullet = GameManager.instance.pool.Get(prefabId).transform; //모자라면 풀링에서 가져옴
                bullet.parent = transform;
            }

            bullet.localPosition = Vector3.zero;        //무기 위치 초기화
            bullet.localRotation = Quaternion.identity; //무기 회전값 초기화

            Vector3 rotVec = Vector3.forward * 360 * index / count; //개수에 따라 360도 나누기
            bullet.Rotate(rotVec);
            bullet.Translate(bullet.up * 1.5f, Space.World); //무기 위쪽으로 이동
            bullet.GetComponent<Bullet>().Init(damage, -1); //-1 is Infinity Per. 무한 관통.
        }
    }
}
  • 테스트를 위해 스페이스바(점프키)를 누르면 레벨 업을 해서 데미지와 무기 개수를 늘어나게 했다.
  • Batch 함수를 수정해서 레벨 업시 무기가 늘어나서 위치가 엉키거나, 엉뚱한 위치에 생성되는 경우를 방지한다.

근접 무기 테스트. 스페이스바를 눌러 무기가 늘어났다. 계속 눌러도 문제 없이 무기가 늘어나고 잘 회전한다.