티스토리 뷰

💻 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;
}
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함