Skip to content

Script Optimization

JungSu Kim edited this page Jan 27, 2016 · 1 revision

게임오브젝트 레퍼런스 캐시

  • Start/Awake 함수에서 오브젝트 정보를 설정한 후에 Update나 FixedUpdate 함수내에서 사용
// Reference cache
// Bad
void Update()
{
  GameObject player = GameObject.Find("Player");
  // something with player ...
}
 
// Good
void Start()
{
  player = GameObject.FindWithTag(Tags.Player);
}
  
void Update()
{
  // something with player ...
}

Tag 활용

  • Find 함수보다는 FindGameObjectWithTag나 FindObjectType 함수를 사용
  • GameObject compareTo 함수시에도 tag 활용
  • 하드코딩된 tag는 피하고 상수로 정의해서 활용
// Using tag
// Bad
if (gameobject.CompareTag("Player"))
{
  // something ...
}
 
var player = GameObject.FindObject("Player");
 
// Good
if (gameobject.CompreTag(Tags.Player))
{
  // something ...
}
 
var player = GameObject.FindObjectWithTag(Tags.Player);

invoke함수 대신에 코루틴을 사용하라(Use coroutines instead of Invoke())

  • Invoke 함수는 C#의 리플렉션 기능을 활용하기 때문에 성능이 좋지 않음. 꼭 필요한 경우가 아니면 Coroutine으로 대체해서 사용하자
// Use coroutines instead of Invoke
// Bad
public void Function()
{
  // something ...
}
Invoke("Function", 3.0f);
 
// Good
public IEnumerator Function(float delay)
{
  yield return new WaitForSeconds(delay);
  // something ...
}
StartCoroutine(Function(3.0f));

지속된 행동만 갱신하는 동작이라면 코루틴을 사용하라(Using coroutines for relaxed updates)

  • 지정된 시간에 계속해서 동작을 수행해야 하는 경우 활용하면 좋음
  • ex) 몬스터 스폰
// Interval with Start()
// Bad
void Update()
{
  // Perform an action every frame
}
 
// Good 
IEnumerator Start()
{
  while (true)
  {
    // Do something every quarter of second
    yield return new WaitForSeconds(0.25f);
  }
}

고정 업데이트 시간을 최소한으로 줄여놓아라(Reduce physics calculations by changing the fixed time step)

  • Edit => Project Settings => Time
  • TimeManager의 Fixed Timestep을 게임에 지장이 없는 수준까지 높게 설정하면 성능에 도움이 됨

아무런 동작을 하지않는 콜백함수는 제거하라(Remove empty callbacks)

  • 아무런 동작을 하지 않는 함수들은 제거 하자. 특히, Awake(), Start(), Update() ...

꼭 필요한 상황에서만 부하가 큰함수를 실행시켜라(Only execute expensive operations occasionally, e.g. Physics.Raycast)

  • Only execute expensive operations occasionally
// Bad
 // Bullet.js
 var speed = 5.0;
  
 function FixedUpdate () {
    var distanceThisFrame = speed * Time.fixedDeltaTime;
    var hit : RaycastHit;
  
    // every frame, we cast a ray forward from where we are to where we will be next frame
    if(Physics.Raycast(transform.position, transform.forward, hit, distanceThisFrame)) {
        // Do hit
    } else {
        transform.position += transform.forward * distanceThisFrame;
    }
 }
 
// Good
 // BulletOptimized.js
 var speed = 5.0;
 var interval = 0.4; // this is 'n', in seconds.
  
 private var begin : Vector3;
 private var timer = 0.0;
 private var hasHit = false;
 private var timeTillImpact = 0.0;
 private var hit : RaycastHit;
  
 // set up initial interval
 function Start () {
    begin = transform.position;
    timer = interval+1;
 }
  
 function Update () {
    // don't allow an interval smaller than the frame.
    var usedInterval = interval;
    if(Time.deltaTime > usedInterval) usedInterval = Time.deltaTime;
  
    // every interval, we cast a ray forward from where we were at the start of this interval
    // to where we will be at the start of the next interval
    if(!hasHit && timer >= usedInterval) {
        timer = 0;
        var distanceThisInterval = speed * usedInterval;
  
        if(Physics.Raycast(begin, transform.forward, hit, distanceThisInterval)) {
            hasHit = true;
            if(speed != 0) timeTillImpact = hit.distance / speed;
        }
  
        begin += transform.forward * distanceThisInterval;
    }
  
    timer += Time.deltaTime;
  
    // after the Raycast hit something, wait until the bullet has traveled
    // about as far as the ray traveled to do the actual hit
    if(hasHit && timer > timeTillImpact) {
        // Do hit
    } else {
        transform.position += transform.forward * speed * Time.deltaTime;
    }
 }

벡터의 크기를 비교할때는 sqrMagnitude 함수를 사용하라(Use sqrMagnitude for comparing vector magnitudes)

// Use sqrMagnitude for comparing vector magnitudes
// Bad
if (Vector3.Distance(transform.position, targetPos) < maxDistance)
{
  // Perform an action
}
 
// Vector3.magnitude property
if ((transform.position - targetPos).magnitude < maxDistance)
{
  // Perform an action
}
 
// Good
if ((transform.position - targetPos).sqrMagnitude < maxDistance * maxDistance)
{
  // Perform an action
}

오브젝트 풀을 사용하라(Use object pooling)

  • 유용한 에셋을 활용

박싱/언박싱을 피하라(Avoid boxing/unboxing)

  • Stack Area <=> Heap Area
  • 값(Value), 참조(Reference) 객체의 차이 이해

계산루틴 효율성

  • 나눗셈보다는 곱셈이 CPU부하가 작음
// Minimize callstack overhead in inner loops
// Bad
x = 100.0f / 2.0f;
 
// Good
x = 100.0f * 0.5f;

내부 루프안에서 함수호출을 최소화 하라(Minimize callstack overhead in inner loops)

// Minimize callstack overhead in inner loops
// Bad
for (;;) 
{
  x = Mathf.Abs(x);
}
 
// Good
for (;;)
{
  x = (x > 0 ? x : -x);
}

Clone this wiki locally