- Home /
Optimizing enemy AI and a question about speed differences between C# and Javascript
Heys guys! I am working on a doom-eque style game, and I have a few turrets scattered throughout the level. After adding more turrets around the map, bringing the total to seven, I found to my dismay that my fps dropped from 120 with two turrets to just bellow 40.
The turret prefab is set up as follows:
--botGameObject
----Turret - Model and Collider, with script attached.
------Turret Spitfire - Spawn point for projectiles.
------Turret Spot - Shadow enabled light.
Here is the code:
using UnityEngine; using System.Collections;
public class BotWall : MonoBehaviour {
private Transform trget;
private Transform meTransform;
public int maxSeeDistance;
private GameObject alarmLight;
public int rotationSpeed;
private Transform botBullet;
private Transform botSpitfire;
public Transform bulletModel;
public int botBulletSpeed;
public int botBulletInterval;
public int wallBotHealth;
private int bulletTime;
void Awake () {
}
// Use this for initialization
void Start () {
meTransform = transform;
GameObject go = GameObject.FindGameObjectWithTag("Player");
trget = go.transform;
transform.Find("wallBot_spot").light.intensity = 0;
botSpitfire = transform.Find("wallBot_spitfire");
}
void OnTriggerEnter (Collider coll)
{
if(coll.tag == "Bullets") {
wallBotHealth --;
Debug.Log("Wall bot health: " +wallBotHealth);
if (wallBotHealth <= 0)
{
Destroy(gameObject);
}
}
}
// Update is called once per frame
void Update () {
//also check if object rotation doesnt intersect walls of device
if ((Vector3.Distance(trget.position, meTransform.position)) < maxSeeDistance && !Physics.Linecast(meTransform.position, trget.position))
{
//Debug.Log("Clear line of sight");
meTransform.rotation = Quaternion.Slerp(meTransform.rotation, Quaternion.LookRotation(trget.position - meTransform.position), rotationSpeed * Time.deltaTime);
bulletTime ++;
//bullet counter
if (bulletTime > botBulletInterval)
{
Transform botBullet=(Transform)Instantiate(bulletModel, botSpitfire.position, Quaternion.identity);
botBullet.rigidbody.AddForce(transform.forward*botBulletSpeed + new Vector3(0f,Random.Range(-15, 15),Random.Range(-15, 15)));
//reset counter
bulletTime = 0;
}
}
}
}
My main concern lies in optimization, any ideas? Another thing I wanted to ask is whether there is a discernable difference between Javascript and C# in Unity games(Dont think so but worth an ask heh). Thanks a lot guys!
Ow, if anyone could let me know why my spotlight isn't working it would be terrific!
Best case scenario: UnityScript is just as fast than C#. Sometimes, one of them is a nanosecond faster than the other, but values will usually fluctuate. Worst case scenario: C# is twice as fast as UnityScript.
Single biggest optimization is using coroutines to run the "AI" part not every frame. Linecast might run 4 times/sec and set/unset canSeePlayer
. Slerp would still need to be run every frame, behind if(canseePlayer)
. The bullet code could also be in a coroutine, delayed to the bullets' fire rate, with a check on canSeePlayer
(Unity can run the bullet timer easier than you can.) As a bonus, turrets seem more real: a "random" lag before they "see" you and they sometimes shoot one extra at a wall you duck behind.
Answer by Skjalg · Jun 29, 2011 at 09:59 PM
There are a lot of small optimizing gems hidden around on the unity forums and the unity script wiki and the like:
And judging from your script there;
You need to stop using "transform.Find" immediately. Its a VERY resource heavy method. Work around the issue by having the "wallBot_spot" gameObject as a public variable in your script.
Doing meTransform = transform; in the Start method is good for optimizing, so you are doing that part right.
Declare "Bullets" as a const string in your script somewhere instead of recreating the string every time OnTriggerEnter happens (wont really save you that much, but while you are optimizing... you should ;)).
Remove all Debug. functions, logging writes to disk and slugs down any machine.
Use the Physics. methods sparsely, they are usually very costly.
Last thing; when you spawn bullets. Are the bullets always destroyed after a set time? (This is probably what is slowing everything down). Just try to play for a few minutes in the editor and then click pause and check if there are a million bullets in your scene. If it is, you should probably attach a script to your bullet prefab that destroys the bullet after some seconds.
Also on the topic of c# vs javascript, the only performance difference is when you dont use explicit types in javascript, then javascript is a little slower (miniscule, or so I've heard). And you can avoid this by not using the "var" everywhere, but actually using the real Type of the variables.
public class SelfDestruct : MonoBehaviour
{
public float selfDestructTime = -1.0f;
public void Start()
{
if (selfDestructTime >= 0)
{
Destroy(gameObject, selfDestructTime);
}
}
}
Hope this helps you in your optimizing :)
#1 He is calling Transform.Find from the Start() function and storing it, that is correct. The function exists for a reason and that is the correct usage. '$$anonymous$$ake objects public' is bad advice.
#3 is wrong, string literals are a special type stored in the metadata.
#5 pooling those objects would increase efficieny quite a bit check out Prime31's tutorial: http://www.youtube.com/watch?v=IX041ZvgQ$$anonymous$$E
I disagree with your asssesmet on #1, Petroz, calling Find in Start might be the correct usage (if you are going to use Find), but it will still make your game stutter on start.
I had no idea that happened with #3, but even so, for code readability it is good to declare it as a const.
and as for number 5, That is a very good tutorial :)
Yeah I agree that Find should be used sparingly, but for purposes of encapsulation it can be good.
Yeah Prime31 is a boss!
Answer by _Petroz · Jun 29, 2011 at 11:49 PM
The most important thing to do when optimizing is to profile your code. If you have Unity Pro you can use Unity's in built profiler which is really easy to use. If you do not have Pro then there are still methods of profiling. There is nothing worse than blindly optimizing, in most cases the performance increase will be negligable and it will make your code less readable and harder to maintain.
The quickest way to get results is to remove code. If you comment out a line of code and it speeds up your game a lot, then that is the line that is slowing you down.
At a glance it appears this line is the biggest issue:
if ((Vector3.Distance(trget.position, meTransform.position)) < maxSeeDistance
&& !Physics.Linecast(meTransform.position, trget.position))
Vector3.Distance will perform a square root which you don't need. The formula for distance is: d = sqrt(x*x + y*y + z*z)
if the only thing you do with that value is compare it you can remove the square root, consider this:
sqrt(x*x + y*y + z*z) < maxSeeDistance
is the same as
(x*x + y*y + z*z) < (maxSeeDistance * maxSeeDistance)
That is a lot cheaper. You can store (maxSeeDistance * maxSeeDistance) in a variable and save even more:
(x*x + y*y + z*z) < maxSeeDistanceSqrd
The code change would look something like this:
(trget.position - meTransform.position).sqrMagnitude < maxSeeDistanceSqrd
The other expensive function Physics.Linecast(meTransform.position, trget.position)
I guess this is a line of sight test, if so it would probably be reasonable to time slice it not do it every frame. So maybe only 5 times per second do a line of sight test that would reduce the load considerably.
Your answer
