Menu

4 Kasım 2012 Pazar

Spikes Everywhere !

I'm sure when you start developing a new you are seeing those nasty spikes on your profiler.(If you are not using it, better start now :) )Those spikes are very important especially if you are developing for mobile.There are a lot of reasons for those spikes,unoptimized code,oversized textures etc.
I will try to cover solutions for those problems,

Cache,Cach,Cache
If you are a researcher type or read some artciles about optimization you already now that you should Cache everything you can.
So what does it mean caching anyways ?

using UnityEngine;
using UnityEngine;
using System.Collections;

public class example : MonoBehaviour 
{ 
    //Create the variable to hold our cached transform.
    private Transform _transform;
 
    void Awake() 
    {
        //Cache the Transform in the _transform variable.
        _transform = transform;
    }

    void Update()
    {
        _transform.position = Vector3.Zero;
    } 

} 

Lets interpret the code together.
First we have created a new variable with the type of Transform.Then at the Awake() method we  have cached our transform object into _transform variable.After that whenever we need to access the Transform object we can just use the _transform variable and save a lot of processing power.If you don't do this whenever you call transform component,Unity will have to make an expensive component lookup.

And another great thing,you can this for all the objects you will have to acces a lot of times.

Spike when using GetComponent Method
Whatever you do,try to avoid this and similiar methods that can cause Component Lookups.Everytime you use this methods Unity will have to search all the components to find what you are looking for.And it can be a really slow and expensive process.Try to cache that kind of items at the Awake() or your Start() methods which will be little bit slower to initiate but a lot more faster to use in game.

Instantiating Objects
Well instantiating objects can really be an expensive process.There are a couple ways to avoid this problem.One of them is using a Pool Manager.A Pool Manager basicly instantiates the objects that will be used in game at the loading screen and keeps them in memory for later use.And this saves us lot of cpu and gpu power when we actually need the game objects.We only have to enable and reposition the objects we have instantiated and kept in memory at the beginning.

Destroying Objects
Destroying objects are actually a hidden cost.Destroying a couple of objects usually won't create an instant spike.But everything starts when the Garbage Collector comes to clean around.This will create a spike if you have Destroyed a lot of objects.The reason behind this is Garbage Collector got called from time to time automatically to free up the unnessecary memory(Destroyed objects etc...).

There are 2 solutions i know for this problem.First one is triggering GC.Collect() method with short intervals to force Garbage Collector to clean memory more frequently which can make garbage collecting faster due to cleaning less memory each time.But i don't usually use this method since it can also create spikes sometimes.Another method is Object Pooling which i have explained before.When u are done with an object just disable it and wait for the time when u will need it.That way there is no objects destroyed and there wont be much job for garbage collector to do.And it also saves time when enabling that object instead of instantiating new one.

Big Spike On The Obcects First Appearance
This problem is one of the most common problems and it can be solved pretty easy with preloading objects.

Preloading is actually a simple yet very effective idea.The idea is showing each object to the camera at loading screen and let the camera render the each object.This way all your objcets will already be rendered and loaded into vram and thanks to that,Loading the texture etc. won't be necesary when the object is actually needed.

Note that preloading objects and Pool Manager methods are solutions for two different problems.Preloading is to save from first rendering time.Object Pooling wont make any visual preloading into memory.

Use Correct Shader
Shaders are very important for performance.And sometimes choosing wrong shader can cost you a lot of fps.First step is to check if you really need all the features of the shader you are using.If not find another shader which can fit better for your needs.For example if you are developing for Mobile you should always use shaders under the Mobile category in Unity.Those shaders are highly optimized for Mobile.But if you still have performance problems caused by shaders you even write your own to save even more performance.

Correct Compression Method
Choosing correct compression method for images and other media is also very important.For example using a 1024x1024 texture for an object that would look the same with 256x256 makes your game use a lot more memory then needed.And if you are using advanced mode for texture compression carefully decide which compression method you are using.Not all platforms support all of the compression methods.You will have to do a little research and experiment with compression methods the find the best fit for your needs.
Also for sounds and other media try to use ogv and ogg extensions which is natively supported by Unity.

Coroutines Are Your Friend
Using Corotuines for big loops or a method with lot of calculations etc. can be a great performance saver since it will not block other objects from doing their job.You can inspect the below example which is quite self explanatory.

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
    //This method will take 1000 frames to finish its job but will not block other scripts or methods from running.
    //And it will let other object to their job each frame due to yield method WaitForEndOfFrame().
    IEnumerator Routine() 
    {
 for(i = 0;i < 1000;i++)
 {
  Debug.Log(i);
  yield return new WaitForEndOfFrame();
 }
    }
    //This will take less time to finish but it will block everything else till it finishes its job.
    void Normal()
    {
 for(i = 0;i < 1000;i++)
 {
  Debug.Log(i);
 }
    }
    void Start() 
    {
        StartCorotuine(Routine());
 Normal();
    }
}

Stop Animating Invisible Objects
Animating an invisible object is not a very logical thing to do.Stopping animation and other complex calculations while the object is not visible can save a lot of performance.In below example the Animation is Stopped when the object became invisible and Played when the object is visible again.

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour{
    void OnBecameVisible() 
    {
        animation.Play();
    }
    void OnBecameInvisible() 
    {
        animation.Stop();
    }
} 

It's pretty much the basic and most important stuff for optimizing your game.I will try to keep you updated when i will have more time to share more with you !

If you have questions feel free to ask.I will try to answer them all.

Also check out those great pages by Unity team,
- www.docs.unity3d.com/Documentation/Manual/iphone-PracticalGuide.html
- www.blogs.unity3d.com/.../shadowgun-optimizing-for-mobile-sample-level/