[Unity] Monster Base 구현

MonsterBase의 구현

프로젝트에서 몬스터를 구현을 담당하게 되어서 몬스터 스크립트의 베이스가 되는 MonsterBase를 만들어봤다.

 

필요한 변수들의 선언

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

public class MonsterBase : MonoBehaviour
{
    public enum MonsterType
    {
        M_V1,
        M_V2,
        M_V3,
        M_CardPack,
        M_VE_1,
        M_VE_2,
        M_SpiderCardPack,
        Red_Spider,
        White_Spider,
        Boss_Mouse
    }

    public static Dictionary<MonsterType, string> MonsterNameDict = new Dictionary<MonsterType, string>
    {
        { MonsterType.M_V1, "M_V1의 별명" },
        { MonsterType.M_V2, "M_V2의 별명" },
        { MonsterType.M_V3, "M_V3의 별명" },
        { MonsterType.M_CardPack, "M_CardPack의 별명" },
        { MonsterType.M_VE_1, "M_VE_1의 별명" },
        { MonsterType.M_VE_2, "M_VE_2의 별명" },
        { MonsterType.M_SpiderCardPack, "M_SpiderCardPack의 별명" },
        { MonsterType.Red_Spider, "Red_Spider의 별명" },
        { MonsterType.White_Spider, "White_Spider의 별명" },
        { MonsterType.Boss_Mouse, "Boss_Mouse의 별명" }
    };

    // Monster Base Info
    public MonsterType monsterType;
    public float MoveSpeed;
    public float AttackPower;
    public float HP;
    public float DEF; // 방어력
    public float DetectingAreaR;
    protected bool isMoving = true;

    // Target Info
    protected Transform player; // 플레이어의 위치
    protected Vector3 TargetPosition; // 탐지된 플레이어의 위치 저장
    protected bool DetectionSuccess = false; // 탐지 성공 여부

    protected GameManager GameManager;
    protected StatusManager statusManager;
    protected SpriteRenderer SpriteRenderer;
    protected Animator MAnimator;
    protected Rigidbody2D rb;

    ...
}

간단하게 중요한 변수들에 대해 설명하자면

  • MonsterType
    • 몬스터의 타입 목록
  • MonsterNameDict
    • 사망 시 몬스터 이름이 나오도록 string을 저장해 두는 Dictionary
  • DetectingAreaR
    • 플레이어 추격 시 탐색 범위 계산용 변수
    • 원의 반지름

 

기본 함수(Start, Update)

기본 함수는 MonsterBase 스크립트를 상속받은 자식이 재정의할 수 있도록 virtual 키워드를 사용하여 만들었다.

Start 함수에서는 변수 초기화를 해주었고, Update 함수는 사용하지 않았다.

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

public class MonsterBase : MonoBehaviour
{
	...
    
    // Start is called before the first frame update
    protected virtual void Start()
    {
        GameManager = GameManager.Instance;
        statusManager = StatusManager.Instance;
        SpriteRenderer = GetComponent<SpriteRenderer>();
        MAnimator = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {

    }
    
    ...
}

 

코루틴 관련 함수

몬스터들은 정해진 주기에 따라 행동을 반복하고, 일정한 패턴 이후 딜레이가 필요하므로 코루틴을 사용하도록 함수들을 정의하였다. 

이때 몬스터별로 패턴이 다르므로 함수만 만들고 세부 내용은 자식 스크립트에서 재정의하도록 virtual 키워드를 사용하여 정의하였고, 로그를 이용해 정의가 필요하다는 것을 표기해 줬다.

원래는 abstract 키워드를 사용하여 순수 가상 함수 사용하려 했지만, 어째서인지 IEnumerator를 반환하는 함수는 순수 가상 함수로 만들 수 없어서 virtual로 정의하였다.

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

public class MonsterBase : MonoBehaviour
{
    ...

    public virtual IEnumerator MonsterRoutine()
    {
        Debug.Log("\"MonsterRoutine \"함수를 재정의하지 않음.");
        yield return null;
    }

    public virtual IEnumerator AttackPreparation()
    {
        Debug.Log("\"AttackPreparation \"함수를 재정의하지 않음.");
        yield return null;
    }

    public virtual IEnumerator RandomMoveAfterSearchFail()
    {
        Debug.Log("\"RandomMoveAfterSearchFail \"함수를 재정의하지 않음.");
        yield return null;
    }
    
    ...
}

 

충돌, 사망 처리 함수들

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

public class MonsterBase : MonoBehaviour
{
	...

    // Player Collision
    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.CompareTag("Player"))
        {
            if (statusManager != null)
            {
                statusManager.TakeDamage(AttackPower, monsterType);
            }
            else
            {
                Debug.Log("Monster OnTriggerEnter2D : Player Not Found");
            }
            
            ...
            
        }

        if (collision.gameObject.CompareTag("Bullet"))
        {
            Debug.Log("Monster Take Damage");
            this.HP -= statusManager.AttackPower;
            Destroy(collision.gameObject);

            if (HP < 0)
            {
                Die();
            }
        }
    }

    private void Die()
    {
        Destroy(this.gameObject);
    }
    
    ...
    
}

플레이어와 캐릭터의 총알을 Tag를 이용하여 감지하도록 만들고, 사망 시 파괴되도록 설정했다.

 

기타 함수

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

public class MonsterBase : MonoBehaviour
{
    ...
    
    // Return RandomPosition
    protected virtual Vector3 GetRanomPositionAround()
    {
        float randomAngle = Random.Range(0f, 360f);
        float randomDistance = Random.Range(0f, DetectingAreaR);

        float x = transform.position.x + randomDistance * Mathf.Cos(randomAngle * Mathf.Deg2Rad);
        float y = transform.position.y + randomDistance * Mathf.Sin(randomAngle * Mathf.Deg2Rad);

        return new Vector3(x, y, 0);
    }

    // Sprite Flip Setting
    protected void SpriteFlipSetting()
    {
        // 방향 설정
        if (TargetPosition.x > transform.position.x)
        {
            SpriteRenderer.flipX = true;   // 왼쪽을 바라봄 (Flip X 활성화)
        }
        else if (TargetPosition.x < transform.position.x)
        {
            SpriteRenderer.flipX = false;  // 오른쪽을 바라봄 (Flip X 비활성화)
        }
    }

    public bool DetectionPlayerPosition()
    {
        Collider2D[] detectedObjects = Physics2D.OverlapCircleAll(transform.position, DetectingAreaR);
        rb.bodyType = RigidbodyType2D.Dynamic;

        foreach (Collider2D obj in detectedObjects)
        {
            if (obj.CompareTag("Player"))
            {
                player = obj.transform;

                TargetPosition = player.position;
                return true;
            }
        }

        return false;
    }

    // ========== 탐색범위 표시용 ==========
    // 탐지 범위를 시각적으로 표시 (에디터 전용)
    protected void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, DetectingAreaR);
    }
}

스프라이트의 방향 처리, 플레이어 탐지, 랜덤 이동 시 목표 지점을 설정하는 함수를 정의하였고,

에디터에서 탐지 범위를 시각적으로 표현하기 위한 함수도 하나 만들어줬다.

 

참고 사항

Coroutine

https://gdoo.tistory.com/20

 

[Unity] Unity에서 딜레이를 주는 방법

Unity에서 딜레이를 주는 방법현재 프로젝트에서 딜레이를 줘야 하는 상황이 많아 자세하게 공부했던 내용을 정리하고자 한다.유니티에서 딜레이를 주는 방법은 크게 5가지가 있다.Coroutine을

gdoo.tistory.com

 

마무리하며..

원래는 하나의 파일에 몬스터 루틴 및 각종 함수들을 몬스터 타입별로 case문을 만들어 정의해 뒀었다.

몬스터의 종류가 다양해지고 많은 변경점이 생기자 유지 보수를 위해 코드를 분리하였고, 중복적인 코드를 피하기 위해 인터페이스 역할을 하는 MonsterBase C# 파일을 만들게 되었다.

 

 

'Unity > Hackers Window' 카테고리의 다른 글

[Unity] Monster 구현 : M_VE - 1  (1) 2024.11.16
[Unity] Monster 구현 : M_V3  (0) 2024.11.15
[Unity] Monster 구현 : M_V2  (0) 2024.11.14
[Unity] Monster 구현 : M_V1  (0) 2024.11.13
[Unity] Hackers Window  (0) 2024.11.10