Script Optimization - zzragida/UnityDocs GitHub Wiki
๊ฒ์์ค๋ธ์ ํธ ๋ ํผ๋ฐ์ค ์บ์
- 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);
}