
오늘 학습 키워드
유니티 심화 팀 프로젝트, 스탠다드 반 강의
오늘 학습 한 내용을 나만의 언어로 정리하기
유니티 심화 팀 프로젝트
합치기
- 합치다보니 여러가지 문제가 발생했음
지정 위치로 못 가는 문제
- 가려는 위치로 못가고 maxMoveTime 이상 moveThreshold 만큼 움직이지 못한다면 막혔다고 판단
- 다음으로 갈 위치 고를 때 막힌 방향 제외하고 감
// MoveNode.cs
public class MoveNode : Node
{
private MonsterAI monsterAI;
private float startTime;
private float maxMoveTime;
private Vector3 lastPosition;
private bool checkMove;
private float moveThreshold;
public MoveNode(MonsterAI monsterAI, bool checkMove = false, float maxMoveTime = 0f, float moveThreshold = 0.05f)
{
this.monsterAI = monsterAI;
this.checkMove = checkMove;
this.maxMoveTime = maxMoveTime;
this.moveThreshold = moveThreshold;
}
public override NodeState Evaluate()
{
if (state != NodeState.Running)
{
startTime = Time.time;
lastPosition = monsterAI.transform.position;
}
if (monsterAI.MoveToDestination())
{
// Debug.Log("Move Done");
return EndMove();
}
monsterAI.Monster.Animator.StartAnimation(monsterAI.Monster.Animator.data.MoveHash);
if (checkMove && Time.time - startTime > maxMoveTime)
{
if (Vector3.Distance(monsterAI.transform.position, lastPosition) < moveThreshold)
{
// Debug.Log("No Move. Move Done");
monsterAI.isStucked = true;
monsterAI.stuckPosition = monsterAI.Monster.Animator.spriteRenderer.flipX ? -1 : 1;
monsterAI.isMoving = false;
return EndMove();
}
startTime = Time.time;
lastPosition = monsterAI.transform.position;
}
state = NodeState.Running;
return state;
}
NodeState EndMove()
{
monsterAI.Monster.Animator.StopAnimation(monsterAI.Monster.Animator.data.MoveHash);
monsterAI.isReturn = false;
state = NodeState.Success;
return state;
}
}// MonsterAI.cs
public void SetRandomDestination()
{
float range;
if (isStucked)
{
// Debug.Log("Stucked New Position");
if (stuckPosition < 0)
{
range = Random.Range(0, Monster.data.Patrol / 2f);
}
else
{
range = Random.Range(-(Monster.data.Patrol / 2f), 0);
}
isStucked = false;
stuckPosition = 0;
}
else
{
range = Random.Range(-(Monster.data.Patrol / 2f), Monster.data.Patrol / 2f);
}
float newPosX = Mathf.Clamp(transform.position.x + range, MinX, MaxX);
Destination = new Vector2(newPosX, transform.position.y);
}공격에 문제가 생김
- 깜빡하고 IsParried를 안끔
public class MeleeMonsterWeapon : MonsterWeapon
{
[field: SerializeField] public float ParryDelay { get; set; } = 0.01f;
public bool IsParried { get; set; }= false;
public override void Parry(int damage)
{
GetComponent<BoxCollider2D>().enabled = false;
Debug.Log("[Monster] Parried!");
monster.Parried(damage);
IsParried = true;
}
private void OnTriggerExit2D(Collider2D other)
{
Debug.Log($"[Test] Trigger Exit : {other}");
if (other.gameObject.CompareTag("Player") && other.gameObject.layer != LayerMask.NameToLayer("PlayerAttack"))
{
Debug.Log($"[Test] Player이면서 무기가 아님.");
if(other.gameObject.TryGetComponent(out Player player) && !IsParried)
{
Debug.Log("[Test] Attack!");
player.ReceiveMonsterAttack(monster.data.AttackDamage, monster.transform.position);
}
}
}
public override void EndParry()
{
IsParried = false;
}
}오디오를 계속 반복해서 살려놔야하는 애들이 있었음
- 그런 애들은 따로 딕셔너리에 보관하기로 함
// SoundManager.cs
public AudioSource PlayClip(AudioClip clip, bool loop, bool dontDestroy = false)
{
if (clip == null)
{
Debug.LogError("Clip is null");
return null;
}
GameObject obj = Instantiate(soundSourcePrefab);
SoundSource soundSource = obj.GetComponent<SoundSource>();
return soundSource.Play(clip, Instance.SoundEffectVolume, loop, dontDestroy);
}
public SoundSource GetPlayingClip(string key)
{
playingSounds.TryGetValue(key, out SoundSource clip);
if (clip != null) return clip.GetComponent<SoundSource>();
else return null;
}
public void StartClip(string key, AudioClip clip, bool loop = false)
{
if (GetPlayingClip(key) != null) return;
playingSounds.Add(key, PlayClip(clip, loop, true).GetComponent<SoundSource>());
}
public void StopClip(string key)
{
if (GetPlayingClip(key) == null) return;
Destroy(playingSounds[key].gameObject);
playingSounds.Remove(key);
}- 그리고 꺼져야 하는 특정 시점에 StopClip 해주기
스탠다드 반 강의 (주제: 프레임워크)
- UI Base를 상속하는 Popup UI Base
- UIManager는 필요한 UI를 꺼내줌
public class UIManager : Singleton<UIManager>
{
public static int ScreenWidth = 1920;
public static int ScreenHeight = 1080;
public Dictionary<string, UIBase> ui_List = new();
public T Show<T>(params object[] param) where T : UIBase
{
string uiName = typeof(T).ToString();
ui_List.TryGetValue(uiName, out UIBase ui);
if(ui == null)
{
ui = Load<T>(uiName);
// 켜져있는 목록 딕셔너리에 추가하기
ui_List.Add(uiName, ui);
ui.Opened(param);
}
ui.gameObject.SetActive(true);
return (T)ui;
}
public T Load<T>()
{
// 캔버스부터 만들기
var newCanvasObject = new GameObject(uiName + " Canvas");
// 캔버스 추가
var canvas = newCanvasObject.gameObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
// 캔버스에 스케일러 추가
var canvasScaler = newCanvasObject.gameObject.AddComponent<CanvasScaler>();
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
canvasScaler.referenceResolution = new Vector2(ScreenWidth, ScreenHeight);
// 캔버스에 레이캐스터 추가
newCanvasObject.gameObject.AddComponent<GraphicRaycaster>();
// 캔버스에 UI 붙이기
var prefab = ResourceManager.Instance.LoadAsset<GameObject>(uiName, eAssetType.UI);
var obj = Instantiate(prefab, newCanvasObject.transform);
obj.name = obj.name.Replace("(Clone)", "");
var result = obj.Getcomponent<T>();
result.canvas = canvas;
result.canvas.sortingOrder = ui_List.count;
return result;
}
public T Get<T>() where T : UIBase
{
string uiName = typeof(T).ToString();
ui_List.TryGetValue(uiName, out UIBase ui);
if(ui == null)
{
Debug.LogError($"{uiName} doesn't exist")
return Default;
}
return (T)ui;
}
public void Hide<T>()
{
string uiName = typeof(T).ToString();
Hide(uiName);
}
public void Hide(string uiName)
{
ui_List.TryGetValue(uiName, out UIBase ui);
if(ui == null) return;
DestroyImmediate(ui.canvas.gameObject);
ui_List.Remove(uiName);
}
}public class UIBase : MonoBehaviour
{
[HideInspector]
public Canvas canvas;
public virtual void Opened(params object[] param)
public virtual void Hide()
{
UIManager.Instance.Hide(gameObject.name);
}
}- 팝업에 따라 캔버스를 나눠주는게 최적화 부분에서 더 좋음.
- 왜냐면 한 캔버스 아래에 여러 UI가 있을 때, 하나라도 바뀌면 모든걸 다시 그리기 때문임
- 유니티 공식 문서에서도 권장하는 방법이라고 함
- 다른 캔버스의 경우, 그려지는 순서는 캔버스 안에 있는 Sort Order에 영향을 받음
- params : 가변 배열을 받겠다는 의미
학습하며 겪었던 문제점 & 에러
문제 1
- 문제&에러에 대한 정의
이펙트가 너무 뒤에 보였음.
- 해결 방법
소팅 오더를 크게 해줌
var psRenderer = go.GetComponent<ParticleSystemRenderer>();
psRenderer.sortingOrder = 200;문제 2
- 문제&에러에 대한 정의
이펙트를 좌우 반전 해야 했음.
- 해결 방법
X축 좌우 반전을 만들어줌
var psRenderer = go.GetComponent<ParticleSystemRenderer>();
psRenderer.flip = isLeft ? new Vector3(1f, 0f, 0f) : Vector3.zero;