[Unity 강의] 뱀서라이크 강의 - 보스, AI

업데이트:

카테고리:

태그: , ,




1. 보스

✔ 보스 리소스 Sprite 가져오기
✔ 애니메이션 만들기
✔ 애니메이터에 애니메이션 추가하기.





2. 수정 코드

🔹 GameScene.cs => 조건(목표 킬 달성) -> 필드 내 몬스터 디스폰, 일반 몹 스폰 x, 보스 생성
🔹 ObjectManager => 몬스터 소환(일반 몹 ~ 보스 몹) 수정, DespawnAllMonsters() 추가
🔹 MonsterController => 상태 패턴 추가.
🔹 BossController => 상태로 보스 관리 (Idle, Moving, Skill, Dead)

GameScene.cs

HandleOnkillCountChanged()
✔ 킬 수 체크, 5킬 -> 스테이지 타입으로 스폰 조절하기, 모든 몬스터 디스폰, 보스 생성.
✔ SpawningPool - bool Stopped으로 스폰 정지

GameScene
public class GameScene : MonoBehaviour
{
    SpawningPool _spawningPool;

    StageType _stageType;
    public StageType StageType 
    {
        get { return _stageType; }
        set 
        {
            _stageType = value;
            if (_spawningPool !=null)
            {
                switch (value) 
                {
                    case StageType.Normal:
                        _spawningPool.Stopped = false;
                        break;
                    case StageType.Boss:
                        _spawningPool.Stopped = true;
                        break;

                }
            }
        }
    }

    void StartLoaded()
    {
        Managers.Game.OnkillCountChanged -= HandleOnkillCountChanged;
        Managers.Game.OnkillCountChanged += HandleOnkillCountChanged;

    }

    public void HandleOnkillCountChanged(int killCount)
    {
        Managers.UI.GetSceneUI<UI_GameScene>().SetKillCount(killCount);
        if (killCount==5)
        {
            StageType = StageType.Boss;
            Managers.Object.DespawnAllMonsters();
            Vector2 spawnPos = Utils.GenerateMonsterSpawnPosition(Managers.Game.Player.transform.position, 5, 10);
            Managers.Object.Spawn<MonsterController>(spawnPos, MonsterID.BOSS_ID);
        }
    }
}


ObjectManager

✔ 몬스터 스폰(일반 몹 ~ 보스 몹) 수정, DespawnAllMonsters() 추가

ObjectManager
public class ObjectManager 
{
    public T Spawn<T>(Vector3 position, int  templateID =0) where T : BaseController 
    {
        System.Type type = typeof(T);

        else if(type == typeof(MonsterController))
        {
            string name = "";
            switch (templateID) 
            {
                case MonsterID.GOBLIN_ID:
                    name = PrefabsName.Goblin;
                    break;
                case MonsterID.SNAKE_ID:
                    name = PrefabsName.Snake;
                    break;
                case MonsterID.BOSS_ID:
                    name = PrefabsName.Boss;
                    
                    break;
            }
            GameObject go = Managers.Resource.Instantiate(name, pooling : true);
            go.transform.position = position;

            MonsterController mc = go.GetOrAddComponent<MonsterController>();
            Monster.Add(mc);
            mc.Init();

            return mc as T;
        }

    public void DespawnAllMonsters() 
    {
        var mosters = Monster.ToList();

        foreach (var monster in mosters)
        {
            Despawn<MonsterController>(monster);
        }
    }
}


MonsterController

✔ bool _attack = false; bool _chase = false;
✔ bool로 상태를 관리하면 나중에 많은 경우들이 발생해서 복잡해진다.
✔ 대부분 상태 기반(State)으로 가능하다.(FSM)
✔ 복잡한 경우 Behaviour Tree 사용
✔ MonsterController - 상태 기반 사용.

MonsterController
public class MonsterController : CreatureController
{

    #region State Pattern

    CreatureState _creatureState = CreatureState.Moving;
    public virtual CreatureState CreatureState 
    {
        get { return _creatureState; }
        set 
        {
            _creatureState = value;
            UpdateAnimation();
        }
    }

    protected Animator _animator;

    public virtual void UpdateAnimation() { }

    public override void UpdateController()
    {
        base.UpdateController();

        switch (CreatureState) 
        {
            case CreatureState.Idle:
                UpdateIdle();
                break;
            case CreatureState.Skill:
                UpdateSkill();
                break;
            case CreatureState.Moving:
                UpdateMoving();
                break;
            case CreatureState.Dead:
                UpdateDead();
                break;
        }
    }

    protected virtual void UpdateIdle() { }
    protected virtual void UpdateSkill() { }
    protected virtual void UpdateMoving() { }
    protected virtual void UpdateDead() { }
    #endregion


BossController

✔ 일반적인 경우 - 데이터 시트를 통해 보스, 몬스터 상관없이 하나의 컨트롤러로 가능하다.
✔ 프로젝트 - BossController를 통해 보스 컨트롤
✔ 상태로 보스 관리 (Idle, Moving, Skill, Dead)
✔ 공격 등 애니메이션 시간 - 코루틴으로 관리

BossController
public class BossController : MonsterController
{
    public override bool Init()
    {
        base.Init();

        _animator = GetComponent<Animator>();
        CreatureState = CreatureState.Moving;
        Hp = 100000;

        return true;
    }

    public override void UpdateAnimation()
    {
        switch (CreatureState) 
        {
            case CreatureState.Idle:
                _animator.Play("Idle");
                break;
            case CreatureState.Moving:
                _animator.Play("Moving");
                break;
            case CreatureState.Skill:
                _animator.Play("Attack");
                break;
            case CreatureState.Dead:
                _animator.Play("Death");
                break;
        }
    }

    float _range = 2.0f;
    protected override void UpdateMoving()
    {
        PlayerController pc = Managers.Game.Player;
        if (pc.IsValid() == false)
            return;

        Vector3 dir = pc.transform.position - transform.position;

        if (dir.magnitude <_range)
        {
            CreatureState = CreatureState.Skill;

            //float animLength = _animator.runtimeAnimatorController.animationClips.Length;
            float animLength = 0.41f;
            Wait(animLength);
        }
    }

    protected override void UpdateSkill()
    {
        if (_coWait ==null)
            CreatureState = CreatureState.Moving;

    }

    protected override void UpdateDead()
    {
        if (_coWait == null)
            Managers.Object.Despawn(this);
    }

    #region Wait Coroutine

    Coroutine _coWait;

    void Wait(float waitSecond) 
    {
        if (_coWait != null)
            StopCoroutine(_coWait);
        _coWait = StartCoroutine(CoStartWait(waitSecond));

    }

    IEnumerator CoStartWait(float waitSecond) 
    {
        yield return new WaitForSeconds(waitSecond);
        _coWait = null;
    }

    #endregion

    public override void OnDamaged(BaseController attacker, int damage)
    {
        base.OnDamaged(attacker, damage);
    }

    protected override void OnDead()
    {
        CreatureState = CreatureState.Dead;
        Wait(2.0f);
    }

}


DEFINE

DEFINE
public struct MonsterID
{
    public const int GOBLIN_ID = 1;
    public const int SNAKE_ID = 2;
    public const int BOSS_ID = 3;
}

public struct PrefabsName
{
    public const string Goblin = "Goblin_01.prefab";
    public const string Snake = "Snake_01.prefab";
    public const string Player = "Slime_01.prefab";
    public const string Boss = "Boss_01.prefab";
}

public enum StageType
{
    Normal,
    Boss,
}

public enum CreatureState
{
    Idle,
    Moving,
    Skill,
    Dead,
}





이것저것 메모

애니메이션 생성

애니메이션 - Sprite들을 여러 개 드래그해서 Hierarchy에 올리면 자동으로 만들어진다.

어드레서블

보스 어드레서블 등록하면 포함된 애니메이션도 포함되어 등록된다.





잡담, 일기?

상태패턴 구조 파악하기, 많이 써보기
Behaviour Tree 공부하기
간단한 AI는 상태패턴이 더 간편하고 좋다.




📔

댓글남기기