수색…
비고
- 가능한 경우 오브젝트에서 스크립트가 필요하지 않을 때 스크립트에서 사용 불가능하게하십시오. 예를 들어 플레이어를 대상으로 검색하고 발생시키는 적 개체에 스크립트가있는 경우 적이 플레이어와 너무 멀리 떨어져있을 때이 스크립트를 사용하지 않는 것이 좋습니다.
빠르고 효율적인 검사
가능한 한 어디서나 불필요한 작업 및 메서드 호출을 피하십시오. 특히 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
{
}