티스토리 뷰
💻 Unity
최적화
C# 코드 최적화
기법 | 설명 | 예시 |
1. GetComponent 캐싱 | GetComponent는 비용이 크므로 반복 호출하지 말고 변수에 저장 | private Rigidbody rb;\\nvoid Start() { rb = GetComponent<Rigidbody>(); } |
2. Update 최소화 | 매 프레임 실행되는 Update() 함수 내 로직은 최소화 | void Update() { if (needToUpdate) DoWork(); } |
3. Invoke/Coroutine 적절 활용 | 타이머나 반복 작업에 InvokeRepeating 또는 Coroutine 사용 | StartCoroutine(MyRoutine()); |
4. Find 함수 사용 지양 | GameObject.Find, FindObjectOfType는 느리므로 미리 참조 저장 | [SerializeField] GameObject target; |
5. LINQ 사용 자제 | LINQ는 할당과 GC 발생 가능성 있음 – 루프 사용 권장 | foreach (var item in list) { ... } |
6. foreach → for | foreach는 박싱/언박싱 이슈 가능. 특히 리스트에서는 for가 더 빠름 | for (int i = 0; i < list.Count; i++) { ... } |
7. 객체 풀링(Object Pooling) | 빈번한 생성/삭제 대신 미리 만든 오브젝트 재사용 | pool.GetObject(); |
8. Struct 사용 (필요시) | 값 타입은 힙 할당이 없으므로 메모리 효율적 (단, 박싱 주의) | struct MyData { public int id; } |
9. 불필요한 메모리 할당 방지 | Update 내 new 사용 금지. 리스트나 배열 재사용 | static List<int> cachedList = new List<int>(); |
10. 이벤트 기반 처리 | 매 프레임 상태 체크 대신 이벤트로 반응 처리 | OnTriggerEnter → HandleTrigger() |
11. Animator 파라미터 해시 사용 | 문자열 대신 Animator.StringToHash() 사용 | animator.SetBool(hashIsRunning, true); |
12. Script Execution Order 설정 | 특정 스크립트 실행 순서를 조정해 불필요한 호출 방지 | (Unity 메뉴: Project Settings → Script Execution Order) |
메모리 & GC 피하기
항목 | 설명 |
클로저, 람다 남용 금지 | GC 발생 원인이 될 수 있음. 매 프레임 람다 생성은 피하기. |
StringBuilder 사용 | 문자열 연산이 많다면 StringBuilder 사용해 GC 감소. |
배열 캐싱 | GetComponents, Physics.RaycastAll 등의 결과는 배열 재사용하기. |
Stats, Window > Anaylsis > Profler에서 CPU, GPU, 메모리 사용량 등 실시간 성능 모니터링을 할 수 있다
시야에 따라 객체 비활성화 (가장 중요한 최적화 방법)
- 프러스텀 컬링 : 카메라에 잡히지 않는 객체를 자동으로 비활성화
- 오클루전 컬링 : 다른 객체로 가려진 객체를 렌더링 하지 않음
기법 설명 예시
카메라 뷰 밖 오브젝트 비활성화 | 카메라에 보이지 않는 오브젝트는 SetActive(false) 또는 렌더링만 끄면 GPU/CPU 자원 절약 가능 | OnBecameInvisible() { gameObject.SetActive(false); } |
활성화 시점 제어 | 카메라에 다시 들어오면 SetActive(true)로 다시 활성화 | OnBecameVisible() { gameObject.SetActive(true); } |
메서드 설명
OnBecameVisible() | 카메라에 보이기 시작할 때 호출됨 |
OnBecameInvisible() | 카메라에 안 보이게 될 때 호출됨 |
Renderer.isVisible | 해당 오브젝트가 현재 카메라에 보이는지 여부 반환 |
주의사항
- OnBecameInvisible()은 카메라 기준이므로, 다수의 카메라나 씬뷰에서도 반응함.
- SetActive(false)는 오브젝트를 완전히 비활성화하므로, 스크립트나 트리거도 작동하지 않음. 필요에 따라 Renderer.enabled = false 정도만 끄는 것도 고려.
- 너무 자주 활성화/비활성화를 반복하면 오히려 성능에 좋지 않으므로, Pool 방식과 병행하거나 거리 기반으로 처리하는 게 좋음.
public class VisibilityCulling : MonoBehaviour
{
void OnBecameInvisible()
{
gameObject.SetActive(false);
}
void OnBecameVisible()
{
gameObject.SetActive(true);
}
}
🕹️ 실습 (3DCombat)
카메라 이동
// Controller.cs
[Header("Camera")]
public Transform camAxisCentral; // 카메라 축
public Transform cam; // 카메라
public float camSpeed; // 카메라 회전 속도
float mouseX;
float mouseY;
float mouseWheel;
void Start()
{
mouseWheel = -5;
mouseY = 3;
}
void Update()
{
CamMove();
Zoom();
}
void CamMove()
{
mouseX += Input.GetAxis("Mouse X");
mouseY += Input.GetAxis("Mouse Y") * -1;
if (mouseY > 10)
{
mouseY = 10;
}
if (mouseY < -5)
{
mouseY = -5;
}
camAxisCentral.rotation = Quaternion.Euler(
new Vector3(camAxisCentral.rotation.x + mouseY, camAxisCentral.rotation.y + mouseX, 0) * camSpeed); // 카메라 회전
}
void Zoom()
{
mouseWheel += Input.GetAxis("Mouse ScrollWheel") * 10;
if (mouseWheel >= -5)
{
mouseWheel = -5;
}
if (mouseWheel < -20)
{
mouseWheel = -20;
}
cam.localPosition = new Vector3(0, 0, mouseWheel); // 카메라 위치
}
플레이어 이동
// Controller.cs
[Header("Player")]
public Transform playerAxis; // 플레이어 축
public Transform player;
public float playerSpeed;
public Vector3 movement;
void PlayerMove()
{
movement = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
if (movement != Vector3.zero)
{
playerAxis.rotation = Quaternion.Euler(
new Vector3(0, camAxisCentral.rotation.y + mouseX, 0) * camSpeed); // 플레이어 회전
playerAxis.Translate(movement * playerSpeed * Time.deltaTime); // 플레이어 이동
player.localRotation = Quaternion.Slerp(player.localRotation, Quaternion.LookRotation(movement), 5 * Time.deltaTime); // 플레이어 회전
// TODO: 애니메이션
}
camAxisCentral.position = new Vector3(player.position.x, player.position.y + 3, player.position.z); // 카메라 위치
플레이어 애니메이션
- 플레이어 애니메이션들을 모두 Humanoid로 바꿔줌
- 플레이어 애니메이터에 아바타를 넣어줌, Root Motion은 체크하거나 안 하거나 맘대로
콤보
- 애니메이션 이벤트 순서대로 ComboPossible(), NextAttack() ,ResetCombo() 연결
// Combo.cs
Animator playerAnim;
bool comboPossible;
public int comboStep; // 콤보 단계
bool inputSmash;
public GameObject hitBox;
string combo1Str = "ARPG_Samurai_Attack_Combo2";
string combo2Str = "ARPG_Samurai_Attack_Combo3";
string combo3Str = "ARPG_Samurai_Attack_Combo4";
void Start()
{
playerAnim = GetComponent<Animator>();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
NormalAttack();
}
if (Input.GetMouseButtonDown(1))
{
SmashAttack();
}
if (Input.GetKeyDown(KeyCode.Space))
{
// 방어
}
}
void NormalAttack()
{
if (comboStep == 0)
{
playerAnim.Play(combo1Str);
comboStep = 1;
return;
}
else
{
if (comboPossible)
{
comboPossible = false;
comboStep += 1;
}
}
}
public void NextAttack()
{
if (!inputSmash) // 기본 공격
{
if (comboStep == 2)
{
playerAnim.Play(combo2Str);
}
else if (comboStep == 3)
{
playerAnim.Play(combo3Str);
}
}
else
{
if (comboStep == 1)
{
//playerAnim.Play("");
}
else if (comboStep == 2)
{
//playerAnim.Play("");
}
else if (comboStep == 3)
{
//playerAnim.Play("");
}
}
}
void SmashAttack()
{
if (comboPossible)
{
comboPossible = false;
inputSmash = true;
}
}
void ComboPossible()
{
comboPossible = true;
}
public void ResetCombo()
{
comboPossible = false;
comboStep = 0;
inputSmash = false;
}
void ChangeHitBoxTag(string t)
{
hitBox.tag = t;
}
'Unity > 멋쟁이사자처럼' 카테고리의 다른 글
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 250519 회고 (0) | 2025.05.19 |
---|---|
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 250516 회고 (0) | 2025.05.19 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 37일차 회고 (0) | 2025.04.17 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 36일차 회고 (0) | 2025.04.17 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 35일차 회고 (0) | 2025.04.17 |