수색…


비고

  1. 가능한 경우 오브젝트에서 스크립트가 필요하지 않을 때 스크립트에서 사용 불가능하게하십시오. 예를 들어 플레이어를 대상으로 검색하고 발생시키는 적 개체에 스크립트가있는 경우 적이 플레이어와 너무 멀리 떨어져있을 때이 스크립트를 사용하지 않는 것이 좋습니다.

빠르고 효율적인 검사

가능한 한 어디서나 불필요한 작업 및 메서드 호출을 피하십시오. 특히 Update 와 같이 여러 번 호출되는 메서드에서 호출하는 것이 좋습니다.

거리 / 범위 검사

거리를 비교할 때 magnitude 대신에 sqrMagnitude 를 사용하십시오. 이렇게하면 불필요한 sqrt 조작을 피할 수 있습니다. sqrMagnitude 를 사용할 때는 오른쪽도 제곱해야합니다.

if ((target.position - transform.position).sqrMagnitude < minDistance * minDistance))

경계 체크

오브젝트 교집합은 Collider / Renderer 경계가 교차하는지 여부를 확인하여 확고하게 확인할 수 있습니다. Bounds 구조에는 두 경계가 교차하는지 여부를 결정하는 데 도움이되는 편리한 Intersects 방법이 있습니다.

Bounds 는 또한 물체 사이의 실제 (지표 간) 거리의 근사치를 계산하는데도 도움이됩니다 ( Bounds.SqrDistance 참조).

주의 사항

바운더리 검사는 볼록 오브젝트에 대해서는 잘 작동하지만 오목 오브젝트에 대한 바운드 검사는 오브젝트의 모양에 따라 훨씬 더 부정확 한 결과를 초래할 수 있습니다.

Mesh.bounds 는 로컬 공간 경계를 반환하므로 사용하지 않는 것이 좋습니다. 대신 MeshRenderer.bounds 를 사용하십시오.

코 루틴 전원

용법

스레드가 안전하지 않은 Unity API에 의존하는 장기 실행 작업을 수행하는 경우 Coroutines 를 사용하여 여러 프레임으로 분할하고 응용 프로그램을 응답 가능하게 유지하십시오.

코 루틴 은 또한 각 프레임에서 해당 작업을 실행하는 대신 n 번째 프레임마다 값 비싼 작업을 수행하는 데 도움이됩니다.

여러 프레임에서 장기 실행 루틴 분할

Coroutines는 여러 프레임에 걸쳐 장기 실행 작업을 분산하여 응용 프로그램의 프레임 속도를 유지하는 데 도움을줍니다.

순차적으로 지형을 페인트하거나 생성하거나 노이즈를 생성하는 루틴은 코 루틴 처리가 필요할 수있는 예입니다.

for (int y = 0; y < heightmap.Height; y++) 
{
    for (int x = 0; x < heightmap.Width; x++)
    {
        // Generate pixel at (x, y)
        // Assign pixel at (x, y)
        
        // Process only 32768 pixels each frame
        if ((y * heightmap.Height + x) % 32 * 1024) == 0)
            yield return null; // Wait for next frame
    }
}

위의 코드는 이해하기 쉬운 예제입니다. 프로덕션 코드에서는 yield return (아마 2-3 행마다)를 확인하고 사전 for 루프 길이를 미리 계산하는 픽셀 단위 검사를 피하는 것이 좋습니다.

보다 적게 비싼 행동 수행하기

코 루틴은 값 비싼 액션을 덜 빈번하게 수행하도록 도와주기 때문에 모든 프레임을 수행 할 때보 다 성능이 크게 떨어집니다.

매뉴얼 에서 직접 다음 예제를 얻습니다.

private void ProximityCheck() 
{
    for (int i = 0; i < enemies.Length; i++) 
    {
        if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance)
                return true;
    }
    return false;
}

private IEnumerator ProximityCheckCoroutine() 
{
    while(true) 
    {
        ProximityCheck();
        yield return new WaitForSeconds(.1f);
    }
}

근접 테스트는 CullingGroup API 를 사용하여 더욱 최적화 할 수 있습니다.

일반적인 함정

개발자가 공통적으로 범하는 실수는 코 루틴 밖에서 코 루틴의 결과 나 부작용에 액세스하는 것입니다. Coroutines는 yield return 문을 만나고 결과 또는 부작용이 아직 수행되지 않는 즉시 호출자에게 제어권을 yield return 합니다. 당신이 코 루틴 외부 결과 / 부작용을 사용해야하는 문제를 회피하기 위해, 확인 이 답변을 .

문자열

겸손한 줄보다 Unity에서 더 큰 자원량이 있다고 주장 할 수도 있지만, 초기에 해결하는 것이 쉬운 측면 중 하나입니다.

문자열 연산은 쓰레기를 만듭니다.

대부분의 문자열 연산은 작은 양의 가비지를 생성하지만 이러한 연산이 단일 업데이트 과정에서 여러 번 호출되면 스택됩니다. 시간이 지남에 따라 자동 가비지 수집이 실행되어 가시적 인 CPU 스파이크가 발생할 수 있습니다.

문자열 연산을 캐시하십시오.

다음 예제를 고려하십시오.

string[] StringKeys = new string[] {
    "Key0",
    "Key1",
    "Key2"
};

void Update()
{
    for (var i = 0; i < 3; i++)
    {
        // Cached, no garbage generated
        Debug.Log(StringKeys[i]);
    }

    for (var i = 0; i < 3; i++)
    {
        // Not cached, garbage every cycle
        Debug.Log("Key" + i);
    }

    // The most memory-efficient way is to not create a cache at all and use literals or constants.
    // However, it is not necessarily the most readable or beautiful way.
    Debug.Log("Key0");
    Debug.Log("Key1");
    Debug.Log("Key2");
}

그것은 어리석은 것처럼 보일지도 모릅니다. 그러나 셰이더로 작업한다면, 이런 상황에 처할 수도 있습니다. 키를 캐시하면 효과가 있습니다.

문자열 리터럴상수 는 프로그램 스택 공간에 정적으로 삽입되므로 가비지를 생성하지 않습니다. 당신은 런타임에서 문자열을 생성하는 동일한 문자열을 위의 예와 같이마다 발생하는 보장하는 경우, 캐싱은 확실히 도움이 될 것입니다.

생성 된 문자열이 매번 같지 않은 다른 경우에는 해당 문자열을 생성하는 다른 방법이 없습니다. 따라서 매번 수동으로 문자열을 생성하는 메모리 스파이크는 한 번에 수만 개의 문자열이 생성되지 않는 한 대개 무시할 수 있습니다.

대부분의 문자열 연산은 디버그 메시지입니다.

디버그 메시지에 대한 문자열 연산, 즉. Debug.Log("Object Name: " + obj.name) 는 괜찮 Debug.Log("Object Name: " + obj.name) 개발 중에는 피할 수 없습니다. 그러나 관련없는 디버그 메시지가 출시 된 제품에서 끝나지 않도록하는 것이 중요합니다.

한 가지 방법은 디버그 호출에서 Conditional 특성 을 사용하는 것입니다. 이것은 메소드 호출뿐만 아니라 그 안에 들어있는 모든 문자열 연산을 제거합니다.

using UnityEngine;
using System.Collections;

public class ConditionalDebugExample: MonoBehaviour
{
    IEnumerator Start()
    {
        while(true)
        {
            // This message will pop up in Editor but not in builds
            Log("Elapsed: " + Time.timeSinceLevelLoad);
            yield return new WaitForSeconds(1f);
        }
    }

    [System.Diagnostics.Conditional("UNITY_EDITOR")]
    void Log(string Message)
    {
        Debug.Log(Message);
    }
}

이것은 단순화 된 예입니다. 좀 더 완벽한 로깅 루틴을 설계하려면 시간을 투자 할 수 있습니다.

문자열 비교

이것은 사소한 최적화이지만 언급할만한 가치가 있습니다. 문자열 비교는 생각보다 약간 더 복잡합니다. 시스템은 기본적으로 문화적 차이를 고려하려고합니다. 대신 간단한 바이너리 비교를 사용하여 더 빨리 수행 할 수 있습니다.

// Faster string comparison
if (strA.Equals(strB, System.StringComparison.Ordinal)) {...}
// Compared to
if (strA == strB) {...}

// Less overhead
if (!string.IsNullOrEmpty(strA)) {...}
// Compared to
if (strA == "") {...}

// Faster lookups
Dictionary<string, int> myDic = new Dictionary<string, int>(System.StringComparer.Ordinal);
// Compared to
Dictionary<string, int> myDictionary = new Dictionary<string, int>();

참조 캐시

특히 update 함수에서 비싼 호출을 피하기 위해 참조를 캐시하십시오. 이것은 가능한 경우 시작시 이러한 참조를 캐싱하거나 사용 가능한 경우 null 참조를 다시 가져 오는 것을 피하기 위해 bool flat을 확인하여 수행 할 수 있습니다.

예 :

구성 요소 참조 캐시

변화

void Update()
{
    var renderer = GetComponent<Renderer>();
    renderer.material.SetColor("_Color", Color.green);
}

private Renderer myRenderer;
void Start()
{
    myRenderer = GetComponent<Renderer>();
}

void Update()
{
    myRenderer.material.SetColor("_Color", Color.green);
}

객체 참조 캐시하기

변화

void Update()
{
    var enemy = GameObject.Find("enemy");
    enemy.transform.LookAt(new Vector3(0,0,0));
}

private Transform enemy;

void Start()
{
    this.enemy = GameObject.Find("enemy").transform;
}

void Update()
{
    enemy.LookAt(new Vector3(0, 0, 0));
}

또한 가능한 경우 Mathf에 대한 호출과 같이 값 비싼 호출을 캐시하십시오.

문자열을 사용하여 메서드 호출하지 않기

메서드를 사용할 수있는 문자열을 사용하여 메서드를 호출하지 마십시오. 이 접근법은 특히 업데이트 기능에서 사용될 때 게임 속도를 늦출 수있는 리플렉션을 사용합니다.

예 :

    //Avoid StartCoroutine with method name
    this.StartCoroutine("SampleCoroutine");

    //Instead use the method directly
    this.StartCoroutine(this.SampleCoroutine());

    //Avoid send message
    var enemy = GameObject.Find("enemy");
    enemy.SendMessage("Die");

    //Instead make direct call
    var enemy = GameObject.Find("enemy") as Enemy;
    enemy.Die();

빈 단일성 방법을 피하십시오

빈 통일 방법을 피하십시오. 프로그래밍 스타일이 나쁜 것 외에도 런타임 스크립팅과 관련하여 매우 적은 오버 헤드가 있습니다. 많은 경우에, 이것은 빌드되어 성능에 영향을 줄 수 있습니다.

void Update
{
}

void FixedUpdate
{
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow