
오늘 학습 키워드
유니티 심화 팀 프로젝트, 스탠다드 반 강의
오늘 학습 한 내용을 나만의 언어로 정리하기
유니티 심화 팀 프로젝트
변경된 BT

-
Damaged Sequence 추가
-
Return Sequence의 위치 변경
-
Damaged 상태에서 isReturn = false 하고, Base위치를 맞았을 때의 위치로 변경할 예정
Return 부터 만들기
// ReturnCheckNode.cs
public class ReturnCheckNode : Node
{
private MonsterAI monsterAI;
public ReturnCheckNode(MonsterAI monsterAI)
{
this.monsterAI = monsterAI;
}
public override NodeState Evaluate()
{
if (monsterAI.isReturn)
{
state = NodeState.Success;
monsterAI.Destination = monsterAI.BasePosition;
}
else
{
state = NodeState.Failure;
}
return state;
}
}- Move 자체는 똑같아서 따로 안만듬
공격 만들기
- 애니메이션 프록시를 두고, 그게 부모에 있는 MonsterAI를 찾아서 Invoke하도록 함
- 공격 가능한지 여부는 Chase 가능한지랑 같기 때문에 패스
// AttackPlayerNode.cs
public class AttackPlayerNode : Node
{
private MonsterAI monsterAI;
private Transform player;
private float distance;
public AttackPlayerNode(MonsterAI monsterAI, Transform player)
{
this.monsterAI = monsterAI;
this.player = player;
}
public override NodeState Evaluate()
{
if (monsterAI.isAttackDone)
{
// 공격 끝난 경우
monsterAI.isAttackDone = false;
Debug.Log("Attack Done!");
state = NodeState.Success;
}
// 이미 공격 중인 경우
else if (monsterAI.Monster.Animator.animator.GetBool(monsterAI.Monster.Animator.data.AttackHash)
&& !monsterAI.isAttackDone)
{
state = NodeState.Running;
}
// 공격 중이지 않은 경우, 공격 시도
else
{
monsterAI.Attack();
monsterAI.isAttackDone = false;
state = NodeState.Running;
}
return state;
}
}- 프록시 받을 수 있도록 MonsterAI에다가 추상 함수 추가
// MonsterAI.cs
public abstract void HandleAnimationEvent(string eventName);// WarriorMonsterAI.cs
public override void HandleAnimationEvent(string eventName)
{
Debug.Log($"{eventName}");
Invoke(eventName, 0f);
}
public override void Attack()
{
// Debug.Log("Warrior Attack Start");
Monster.Animator.StartAnimation(Monster.Animator.data.AttackHash);
}
public void ParryingStart()
{
// Debug.Log("Parrying Start");
AttackCollider.gameObject.SetActive(true);
}
public void ParryingStop()
{
// Debug.Log("Parrying Stop");
AttackCollider.gameObject.SetActive(false);
}
public void StopAttack()
{
// Debug.Log("Attack Stop");
Monster.Animator.StopAnimation(Monster.Animator.data.AttackHash);
isAttackDone = true;
}스탠다드 반 강의 (주제 : 프레임워크)
매니저 종류
- GameManager, UIManager 등등..
- 특정 씬에서만 필요한게 아니고 전체적으로 필요한 애들은 싱글톤 매니저라고 함
// Singleton.cs
public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T _instance = null;
// 굳이 직접 안만들어도 호출 하는 순간 생성하기 때문에 유용함
public static T Instance
{
get
{
Create();
return _instance;
}
};
static void Create()
{
if(_instance == null)
{
// 타입이 T 인 오브젝트 찾기
T[] objects = FindObjectsByType<T>(FindObjectSortMode.None);
// 오브젝트를 찾았더니 있을 때
if(objects.Length > 0)
{
// 맨 앞에 있는 애가 Instance
_instance = objects[0];
for(int i = 1; i < objects.Length; i++)
DestroyImmediate(objects[i].gameObject);
}
// 오브젝트를 찾았더니 없을 때
else
{
// 새로 만들어주기
GameObjct go = new GameObject($"{typeof(T).Name}");
_instance = go.AddComponent<T>();
}
}
}
// 생명주기 함수 다 virtual 로 만들어두는게 좋음
protected virtual void Awake()
{
Create();
if(_instance != this)
{
Destroy(gameObject);
}
else
{
DontDestroyOnLoad(this);
}
}
protected virtual void OnEnable() { }
protected virtual void Start() { }
protected virtual void Update() { }
protected virtual void FixedUpdate() { }
protected virtual void LateUpdate() { }
protected virtual void OnDisable() { }
protected virtual void OnDestroy()
{
_instance = null;
}
}Resource Manager
public enum eAssetType
{
Prefab,
Thumbnail,
UI,
Data,
SO,
Sound
}
public enum eCategoryType
{
None,
Item,
NPC,
Stage,
Character,
Maps,
Model
}
public class ResourceManager : Singleton<ResourceManager>
{
// 오브젝트 풀
private Dictionary<string, object> assetPool = new();
public T LoadAsset<T>(string key, eAssetType assetType, eCategoryType categoryType = eCategoryType.None) where T : Object
{
// handle : 로드한 에셋을 받음
T handle = default;
var typeStr = $"{assetType}/{(categoryType == eCategoryType.None? "" : $"{categoryType}")}/{key}};
if(assetPool.ContainsKey(key + "_" + typeStr))
{
var obj = Resources.Load(typeStr, typeof(T));
if(obj == null) return default;
assetPool.Add(key + "_" + typeStr, obj);
}
handle = (T)assetPool[key + "_" + typeStr];
return handle;
}
// 참고 : 비동기로 하면 Object Pool에 데이터가 생성되기 전에 불러올 가능성 있음
public async Task<T> LoadAsyncAsset<T>(string key, eAssetType assetType, eCategoryType categoryType = eCategoryType.None) where T : Object
{
// handle : 로드한 에셋을 받음
T handle = default;
var typeStr = $"{assetType}/{(categoryType == eCategoryType.None? "" : $"{categoryType}")}/{key}};
if(assetPool.ContainsKey(key + "_" + typeStr))
{
var op = Resources.LoadAsync(typeStr, typeof(T));
while(!op.IsDone)
{
Debug.Log(key + "로드 중...");
await Task.Yield();
}
var obj = op.asset;
if(obj == null) return default;
assetPool.Add(key + "_" + typeStr, obj);
}
handle = (T)assetPool[key + "_" + typeStr];
return handle;
}
}Scene Load Manager
// SceneLoadManager.cs
public class SceneLoadManager : Singleton<SceneLoadManager>
{
public bool isManager = false;
public string NowSceneName = "";
protected override void Awake()
{
base.Awake();
NowSceneName = SceneManager.GetActiveScene().name;
}
//
public async void ChangeScene(string SceneName, Action callback == null, LoadSceneMode mode = LoadSceneMode.Single)
{
var op = SceneName.LoadSceneAsync(SceneName, mode);
while(!op.isDone)
{
Debug.Log("로딩창 띄우기");
await Task.Yield();
}
Debug.Log("로드 완료");
// 씬 전환 후에 할 콜백 실행
callback?.Invoke();
}
// 참고 : Addictive로 부르지 않은, Single로 부른 애를 Unlaod하면 오류남.
public async void UnLoadScene(string SceneName, Action callback = null)
{
var op = SceneManager.UnloadSceneAsync(SceneName);
while(!op.isDone)
{
await Task.Yield();
}
callback?.Invoke();
}
}Manager를 한 씬에 몰아넣기
- 매니저 씬을 따로 만들어서 그 씬을 Additive로 넣고, 좀 있다가 Unload를 하면 어차피 DontDestroy라서 계속 남아있음
// SceneBase.cs
public abstract class SceneBase : MonoBehaviour
{
protected virtual void Awake()
{
if(!SceneLoadManager.Instance.isManager)
{
SceneLoadManager.Instance.ChangeScene("Manager", () =>
{
SceneLoadManager.Instance.isManager = true;
SceneLoadManager.Instance.UnLoadScene("Manager");
}, LoadSceneMode.Additive);
}
}
}