
오늘 학습 키워드
유니티 심화, 스탠다드 반 강의
오늘 학습 한 내용을 나만의 언어로 정리하기
유니티 심화 (2D)
오브젝트 풀링
- 오브젝트를 재사용
public interface IPoolable
{
void Initialize(Action<GameObject> returnAction);
void OnSpawn();
void OnDespawn();
}public class ObjectPoolManager : MonoBehaviour
{
public GameObject[] prefabs;
private Dictionary<int, Queue<GameObject>> pools = new Dictionary<int, Queue<GameObject>>();
public static ObjectPoolManager Instance { get; private set; }
private void Awake()
{
Instance = this;
for (int i = 0; i < prefabs.Length; i++)
{
pools[i] = new Queue<GameObject>();
}
}
public GameObject GetObject(int prefabIndex, Vector3 position, Quaternion rotation)
{
if (!pools.ContainsKey(prefabIndex))
{
Debug.Log($"프리팹 인덱스 {prefabIndex} 에 대한 풀이 존재하지 않습니다.");
return null;
}
GameObject obj;
if (pools[prefabIndex].Count > 0)
{
// 풀에서 꺼내기
obj = pools[prefabIndex].Dequeue();
}
else
{
// 새로 만들어서 풀에 집어넣기
obj = Instantiate(prefabs[prefabIndex]);
// 풀에 돌아갈 때 작동할 함수 넣어주기
obj.GetComponent<IPoolable>()?.Initialize(o => ReturnObject(prefabIndex, o));
}
obj.transform.SetPositionAndRotation(position, rotation);
obj.SetActive(true);
obj.GetComponent<IPoolable>().OnSpawn();
return obj;
}
public void ReturnObject(int prefabIndex, GameObject obj)
{
if (!pools.ContainsKey(prefabIndex))
{
Destroy(obj);
return;
}
obj.SetActive(false);
pools[prefabIndex].Enqueue(obj);
}
}확장 가능한 스탯 시스템
// 스탯 생성
public enum StatType
{
Health,
Speed,
ProjectileCount
}
[CreateAssetMenu(fileName = "New StatData", menuName = "Stats/Character Stats")]
public class StatData : ScriptableObject
{
public string CharacterName;
public List<StatEntry> stats;
}
[System.Serializable]
public class StatEntry
{
public StatType statType;
public float baseValue;
}// StatHandler 수정
public class StatHandler : MonoBehaviour
{
public StatData statData;
private Dictionary<StatType, float> currentStats = new Dictionary<StatType, float>();
private void Awake()
{
InitializeStats();
}
private void InitializeStats()
{
foreach (StatEntry entry in statData.stats)
{
currentStats[entry.statType] = entry.baseValue;
}
}
public float GetStat(StatType statType)
{
return currentStats.ContainsKey(statType) ? currentStats[statType] : 0;
}
public void ModifyStat(StatType statType, float amount, bool isPermanent = true, float duration = 0)
{
if (!currentStats.ContainsKey(statType)) return;
currentStats[statType] += amount;
if (!isPermanent)
{
StartCoroutine(RemoveStatAfterDuration(statType, amount, duration));
}
}
private IEnumerator RemoveStatAfterDuration(StatType statType, float amount, float duration)
{
yield return new WaitForSeconds(duration);
currentStats[statType] -= amount;
}
}아이템 생성과 사용 시스템 구현
public class ItemData : ScriptableObject
{
public string itemName;
public List<StatEntry> statModifiers;
public bool isTemporary;
public float duration;
}
public class ItemHandler : MonoBehaviour
{
[SerializeField] private ItemData itemData;
public ItemData ItemData => itemData;
}// EnemyManager
public void RemoveEnemyOnDeath(EnemyController enemy)
{
activeEnemies.Remove(enemy);
CreateRandomItem(enemy.transform.position);
// 소환도 다 했는데 활성화 된 적이 없다 == 다 잡았다!
if (enemySpawnComplite && activeEnemies.Count == 0)
{
gameManager.EndOfWave();
}
}
public void CreateRandomItem(Vector3 position)
{
GameObject item = Instantiate(itemPrefabs[Random.Range(0, itemPrefabs.Count)], position, Quaternion.identity);
}// PlayerController
public void UseItem(ItemData item)
{
foreach (StatEntry modifier in item.statModifiers)
{
statHandler.ModifyStat(modifier.statType, modifier.baseValue, !item.isTemporary, item.duration);
}
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Item"))
{
Debug.Log("Item과 부딛힘");
if (other.TryGetComponent<ItemHandler>(out ItemHandler handler))
{
if (handler.ItemData == null) return;
UseItem(handler.ItemData);
Destroy(handler.gameObject);
}
}
}유니티 심화 (3D)
URP
-
렌더링 파이프라인 종류
-
렌더링 : 화면에 오브젝트를 그리는 과정
-
렌더링 파이프라인 : 화면에 오브젝트를 그리는 과정을 제어하는 시스템
- Culling : 절두체에 걸리지 않는 건 그리지 않음.
- Rendering : 평면 화면에 그림
- Shader : 작은 단위의 처리. 각각의 색상을 계산하기 위한 수학적 계산 및 알고리즘 포함
- Mesh : 개체의 모양을 정의
- Texture : 비트맵 이미지.
- Material : 텍스쳐에 대한 참조, 타일링 정보, 색상 등등 표면을 렌더링하는 방법을 정의. 쉐이더에 따라 옵션이 정해짐
- Post-processing : 필터처럼 작동함.
-
Unity에서의 렌더링 파이프라인
- 빌트인 : 기본 렌더링 파이프라인. 커스텀 확장에 제한적
- SRP : 스크립트로 렌더링을 제어하고 커스터마이징 할 수 있음
- URP
- HDRP (하이엔드용)
-
URP : Universal Render Pipeline. 스크립트 가능한 렌더 파이프라인(SRP)임
- Cross-Platform Compatibility : Unity가 지원하는 모든 플랫폼에서 동작하도록 설계됨
- Performance and Scalability : 성능 / 확장성 굿
- Modern Rendering Features
- Customizability
- Graphics Quality
- Simplicity : 기존 렌더링 시스템에 비해 쓰기 쉬움. 셰이더 그래프, VFX 그래프 제공
FSM
-
FSM : Finite Sate Machine, 유한 상태 기계
- 유한한 갯수의 상태들로 구성된 기계 및 패턴
- 상태와 상태 간의 전환을 기반으로 동작하는 동작 기반 시스템
-
FSM의 구성 요소
- 상태 : 시스템이 취할 수 있는 상태
- 조건 : 상태 간 전환을 결정하는 조건
- 동작 : 상태에 따라 수행되는 동작 또는 로직
-
FSM의 동작 원리
- 초기 상태에서 시작해 입력 또는 조건에 따라 상태 전환을 수행함
- 상태 전환은 전환 조건을 충족할 때 발생
- 전환 조근언 입력, 시간, 조건 등으로 결정됨
- 상태 전환 시 이전 상태의 종료 동작과 새로운 상태의 진입 동작이 수행됨
-
FSM의 예시 : 플레이어 상태 관리
- 상태 : 정지, 이동, 점프
- 조건 : 이동 입력, 점프 입력, 충돌 등등
- 동작 : 이동 애니메이션 재생, 점프 처리, 이동 속도 조정 등
-
Switch-Case 문을 활용한 FSM의 단점
- 코드가 지나치게 길어짐
- 유지보수가 어려움
- 상태가 추가될 때마다 새로운 분기를 작성해야 하고, 중복 코드가 많아짐
-
State Pattern을 활용한 FSM의 장점
- 객체지향의 다향성을 활용
- 상태를 명확하게 정의, 상태 간 전환을 일관되게 관리 가능
- 복잡한 동작을 상태와 전환 조건으로 나누어 구현하기 때문에 코드 유지 보수가 용이함
- 다양한 동작을 유기적으로 조합해 원하는 동작을 구현할 수 있음
캐릭터 컨트롤러
- 유니티에서 캐릭터나 플레이어의 움직임과 충돌을 관리하기 위해 사용되는 컴포넌트
- 물리 엔진이 아닌 캐릭터의 움직임을 프레임 기반으로 처리하기 때문에, 3D 캐릭터를 제어하는 데 사용됨
- 수평 방향이 좀 더 중요하면 캐릭터 컨트롤러를 쓰는게
- 주요 특징
- 캐릭터 이동 : 단순 이동을 쉽게 구현할 수 있도록 메소드를 제공. 이동 방향 및 속력을 설정
- 중력 적용 : 캐릭터 컨트롤러는 Rigidbody 물리 도움을 못받음. 그래서 직접 만들어야함
- 충돌 처리 : 다른 콜라이더와의 충돌을 통제하고, 경사로와의 상호작용을 지원
- 바닥 검출 : 바닥 검출을 알아서 처리함.
- 움직임 제한 : 지정 영역 내에서만 움직이도록 하거나, 경사를 따라 이동할 수 있도록 설정 가능
스탠다드 반 강의 (주제 : 최적화 이론 및 이해)
최적화의 필요성
- 최대한 많은 경우의 수를 열어두어야 함.
- WebGL이 최적화 하기에 가장 까다로움
- 웹은 메모리가 한정적.
CPU 와 GPU 의 역할 차이
- CPU : 스크립트 실행
- GPU : 쉐이더, 파티클 효과 실행
FPS
- 1초 동안 화면이 갱신되는지를 나타내는 지표.
- 모바일, 웹 : 보통 30~60fps
- PC, 콘솔 : 60fps 이상
- Application.targetFrameRate로 동적 조정 가능
드로우콜
- GPU에게 이 오브젝트를 화면에 그리라고 지시하는 명령 1회
- 드로우콜이 많을 수록 안좋음 (CPU-GPU 간 명령 전달이 많아져 성능 저하가 심함)
- 배칭, 오큘루전 컬링, LOD, 텍스쳐 아틀라스를 사용해서 최적화 가능
배칭
- 여러 오브젝트의 드로우콜을 묶어서 한번에 함
- FPS와 배치는 반비례 관계.
- 배치는 줄이고, FPS는 높여야 함!
오큘루전 컬링
- 다른 오브젝트에 가려(오클루전된) 카메라에 보이지 않는 오브젝트의 렌더링을 비활성화 하는 기능
LOD (Level of Detail)
- 3D 모델 표현의 복잡도를 거리에 따라 차등을 두어 부하를 줄이는 기술
- 멀 수록 가벼운 재질로 씀
- Geometry LOD : 객체의 기하학적인 세부 수준을 조절. 면 수 or 정점 수 줄이기 등등
- Texture LOD : 객체에 적용되는 텍스쳐의 해상도를 조절
- Shader LOD : 쉐이더 프로그램을 조절해 시각적 효과를 조절. 빛 반사 or 그림자 등등
스크립트 최적화
- Find 하지마셈
- GetComponent 반복 사용 금지.
- 클래스 / 리스트 반복 재할당 금지.
- 특히 리스트는 Clear 쓰셈!!
- Awake, Start 가볍게 하셈
- 그리고 코루틴 yield return 할때 new 로 너무 자주 만들지마셈
정리
- 렌더링
- 오큘루전 컬링 했니?
- LOD 했니?
- 정적 배칭 했니?
- 빌드 설정
- 플랫폼 별 품질 설정 했니?
- 불필요한 에셋이 들어가진 않았니?
- 스크립트
- Update에서 불필요한거 뺐니?
- 오브젝트 풀링 했니?
- GC 과도하게 안나오지?
- 리소스 관리
- 텍스쳐 크기/포맷 최적화 했니?
- 어드레서블이나 에셋번들 썼니?
- 씬 로딩 전략 잘 짰니?
학습하며 겪었던 문제점 & 에러
문제 1
- 문제&에러에 대한 정의
갑자기 충돌 처리가 안됐음
- 해결 방법
리지드 바디를 넣었다가 뺐더니 됐음..