[Unity 강의] 뱀서라이크 강의 - 보스, AI
카테고리: Class VamSurLike
태그: C#, Unity, VamSurLike
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는 상태패턴이 더 간편하고 좋다.
댓글남기기