- Home /
Does unity process "false" if statements?
Hi all, please bear with me on this, I tried searching for the answer, but struggling to phrase the question right. Here goes.....
I'm making a zombie game, and writing the AI for the zombies. There're a lot of them, and the AI script is quite long, so I want to cut down the processing spent on them when they're far from the player.
Do I have to make a separate script that measures distance between zombie and player, that turns the AI script on and off? I can do that, but that's an extra script per NPC, per frame, measuring distance.
However, I already do the measuring at the beginning of the existing AI script. My question is - if I put all the rest of the script in an "if" statement tied to distance, will all those lines of code be read and processed if the distance is too great (the statement is false)? If not, it's be cheaper to stick with the one script, with only a few lines being read. If the whole script is read and processed anyway, I'd be better off with two scripts, one turning the other on and off. Anyone able to help please? Sorry for the long winded explanation ;-) Regards Ian
Answer by Berenger · Jan 28, 2013 at 05:16 PM
Nothing is executed in the scope of a "if(false)". So that would solve your problem.
However, I'd like to make a suggestion. First, you should do an AI loop on a certain frequency, not each frame. So, coroutines. The closer the player is, the lower the frequency must be, obviously.
But instead of computing the distance, which can become quite heavy (even without the sqrt), you could put several game objects under your player, each one with a sphere collider. The first with a radius of five, that's very close from the player, then 20, then 50 etc. That way, your AI can adjust its frequency depending of which collider it enters, or leave. You could modify the complexity of the AI as well.
You mean the closer they are the higher the frequency right? :)
Answer by Bunny83 · Jan 28, 2013 at 05:16 PM
No, Unity or Mono / .NET doesn't execute the content of an if statement that is false. That's the point of an if statement ;)
If you worry about performance you might want to put your code in a CoUpdate function. A coroutine which just runs slower than Update.
function Start()
{
CoUpdate();
}
function CoUpdate()
{
while(true)
{
// Your code here
yield new WaitForSeconds(0.1);
}
}
This will make your code only being executed 10 times per sec.
You can also use InvokeRepeating(Fattie would suggest this first), but i don't like it ;)
I'd suggest InvokeRepeating too...not fond of the string, but it's more lightweight than a coroutine.
Answer by whydoidoit · Jan 28, 2013 at 08:09 PM
One more thought, based on the fact you need to do distance checks to work stuff out, a good way around that is to use a simplified 2D representation of the world to see if something is close to the player. For instance you could check if the enemy was within 40 and 80 world units of the player (in x and z) by dividing the world into 40 world unit squares and seeing if the player is in the current cell or any adjacent one. This doesn't require square roots and only requires that each item writes and removes itself from the grid as it moves. This is also a good technique for lots of things that involve proximity and the like. For instance I use it to keep 100s of characters apart without raycasts or built in collision detection.
I've built a simple(ish) class to support it.
SupportClasses.cs
public class DefaultDictionary< TK,TR > : Dictionary< TK,TR>
{
public new virtual TR this[TK index]
{
get
{
if (ContainsKey(index))
{
return base[index];
}
return default(TR);
}
set
{
base[index] = value;
}
}
public T Get< T >(TK index) where T : TR
{
return (T)this[index];
}
}
public class Index< TK,TR > : DefaultDictionary< TK,TR > where TR : class, new()
{
public event Action< TK, TR, TR> Setting = delegate { };
public event Action< TK, TR> Getting = delegate { };
public override TR this[TK index]
{
get
{
if (ContainsKey(index))
{
return base[index];
}
var ret = new TR();
if (ret is INeedParent)
{
(ret as INeedParent).SetParent(this, index);
}
base[index] = ret;
Getting(index, ret);
return ret;
}
set
{
if (Setting != null)
{
TR current = null;
if (base.ContainsKey(index))
{
current = base[index];
}
Setting(index, current, value);
}
base[index] = value;
}
}
}
CollisionGrid.cs
using UnityEngine;
using System.Linq;
using System.Collections.Generic;
using System;
public class GridCellPosition
{
public int z;
public int x;
static readonly Index< int, Index< int, GridCellPosition>> cache = new Index< int, Index< int, GridCellPosition>>();
public static GridCellPosition Get(int x, int z)
{
GridCellPosition cell;
if(!cache[x].TryGetValue(z, out cell))
{
cell = new GridCellPosition
{
x = x,
z = z
};
cache[x][z] = cell;
}
return cell;
}
public static implicit operator GridCellPosition(Vector3 position)
{
return Get(Mathf.FloorToInt(position.x),
Mathf.FloorToInt(position.z));
}
public GridCellPosition Offset(int offsetX, int offsetZ)
{
return Get(x + offsetX, z + offsetZ );
}
public static GridCellPosition operator / (GridCellPosition pos, int value)
{
return Get(pos.x / value, pos.z / value );
}
public static GridCellPosition operator * (GridCellPosition pos, int value)
{
return Get(pos.x * value, pos.z * value );
}
}
public class CollisionGrid< T>
{
public int size = 5;
public virtual bool Add(GridCellPosition position, T item) {return false; }
public virtual IEnumerable< T> Get(GridCellPosition position) { return null; }
public virtual IEnumerable< T> Get(GridCellPosition position, float width, float height) {return null;}
public IEnumerable< T> this[GridCellPosition position]
{
get
{
return Get(position);
}
}
public virtual bool CheckCell(GridCellPosition position) { return false; }
readonly List< T> _emptyList = new List<T>();
public IEnumerable< T> this[Component position]
{
get
{
return position == null ? _emptyList : Get(position.transform.position);
}
}
public virtual bool Add(T item)
{
if (item == null)
throw new Exception("Trying to add null to a grid");
if (item is Transform)
{
return Add((item as Transform).position, item);
}
if(item is Component)
{
return Add((item as Component).transform.position, item);
}
if(item is GameObject)
{
return Add((item as GameObject).transform.position, item);
}
throw(new Exception("Cannot get a position from " + item.GetType().FullName));
}
public virtual void Clear() {}
public virtual void Remove(T item) {}
public virtual void Remove(GridCellPosition position, T item) {}
}
public class SparseCollisionGrid< T> : CollisionGrid< T> {
readonly Index< GridCellPosition, HashSet<T>> _grid = new Index< GridCellPosition, HashSet< T>>();
public override bool Add(GridCellPosition position, T item)
{
position /= size;
var result = InternalCheck(position, item);
_grid[position].Add(item);
return result;
}
public override void Remove(GridCellPosition position, T item)
{
position /= size;
_grid[position].Remove(item);
}
public override void Remove(T item)
{
if (item is Transform)
{
Remove((item as Transform).position, item);
}
if(item is Component)
{
Remove((item as Component).transform.position, item);
}
else if(item is GameObject)
{
Remove((item as GameObject).transform.position, item);
}
else throw(new Exception("Cannot get a position from " + item.GetType().FullName));
}
public override IEnumerable< T> Get(GridCellPosition position)
{
position /= size;
//var list = Factory.New< List< T>>();
var list = new List< T>();
list.AddRange(_grid[position.Offset(0,0)]);
list.AddRange(_grid[position.Offset(0,1)]);
list.AddRange(_grid[position.Offset(1,0)]);
list.AddRange(_grid[position.Offset(1,1)]);
list.AddRange(_grid[position.Offset(-1,0)]);
list.AddRange(_grid[position.Offset(0,-1)]);
list.AddRange(_grid[position.Offset(1,-1)]);
list.AddRange(_grid[position.Offset(-1,1)]);
list.AddRange(_grid[position.Offset(-1,-1)]);
return list;
}
public override IEnumerable< T> Get (GridCellPosition position, float width, float height)
{
width = Mathf.FloorToInt(width/size)+1;
height = Mathf.FloorToInt(height/size)+1;
position /= size;
//var list = Factory.New< List< T>>();
var list = new List< T>();
for(var x = 0; x < width + 2; x++)
{
for(var z = 0; z < height+2; z++)
{
var result = _grid[position.Offset(x, -z)];
if(result!=null)
list.AddRange(result);
}
}
return list;
}
public override bool CheckCell(GridCellPosition position)
{
return _grid[position].Count > 0;
}
public override void Clear()
{
_grid.Clear();
}
}
Using it
You use it by defining a collision grid and giving it a size
public static CollisionGrid< AnyScriptOrComponent> collisionGrid = new SparseCollisionGrid< AnyScriptOrComponent>();
...
collisionGrid.size = 40;
Then thing that wants to be detected adds and removes itself as it moves (removing first really)
void Update()
{
collisionGrid.Remove(this);
...
transform.position += Vector3.right * 2;
...
collisionGrid.Add(this);
}
Then to get all of the items within adjacent cells (or the current cell)
foreach(var item in collisionGrid[this])
{
if(item == this) continue;
//Do something with the neighbour
}
If you were just looking for the player then you could not add the enemies at all and just have the player in there, then its a simple check
if(collision.CheckCell(transform.position))
{
//Player is close
}
else
{
//Not so much
}
The issue seems to be related to HT$$anonymous$$L tags, so as a work-around always uses spaces, such as foo< bar >
.
Thanks Eric, I was pulling my hair out - some were working (guess it knew they weren't tags) but it really didn't like < TR!!
Note this technique is very powerful when you need to test many things against many other things - it is certainly possible a distance check against a single point would be faster in circumstances that only require that. All depends really.