we have a game that involves a small square scene in a courtyard. In the courtyard are four doors leading out. Through these doors we wish to have enemy NPC’s come and attack the main player while he tries to defend himself.
The script we are using is this:
private var enemy : Transform;
private var enemyController : CharacterController;
var playerVisible = false;
var player : Transform;
var speed : float;
var rotationSpeed : float;
var test : Transform;
function Start () {
var lastVisiblePlayerPosition = player.position;
enemy = GetComponent(Transform);
enemyController = GetComponent(CharacterController);
InvokeRepeating("Updateenemy", 0.8, 0.8);
}
function Update() {
var forward = enemy.transform.TransformDirection(Vector3.forward);
// Move the character
direction = forward * speed; // * speedModifier;
enemy Controller.SimpleMove(direction);
}
function Updateenemy() {
if (CanSeeTarget()) {
MoveTowards(player.position);
playerVisible = true;
}
else {
playerVisible = false;
}
}
function CanSeeTarget () : boolean
{
//var layerMask = 1 << 8;
//layerMask = ~layerMask;
var hit : RaycastHit;
if (Physics.Linecast (transform.position, player.position, hit))
return hit.transform == player;
return false;
}
function MoveTowards (position : Vector3) {
var direction = position - enemy.transform.position;
direction.y = 0;
if (direction.magnitude < 0.5) {
SendMessage("SetSpeed", 0.0, SendMessageOptions.DontRequireReceiver);
return;
}
enemy.animation.CrossFade("Run");
// Rotate towards the target
enemy.transform.rotation = Quaternion.Slerp (enemy.transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
enemyt.transform.eulerAngles = Vector3(0, enemy.transform.eulerAngles.y, 0);
}
The unfortunate thing is that it cripples the iphone cpu during play. Can anyone advise us how to make an efficient script in this vein?
Hmmm… going to look at that. ATM there are about 20 enemies onscreen at once. Is there a more efficient way to handle that many (a master controller???).
This is pure speculation, but if you’re instantiating all of the enemies (or some of them) at the same time, the invoke-repeated methods may all get called at approximately the same moment. So maybe you should build in a system that starts them at different intervals or something. Iif I’m not mistaking, the first parameter of InvokeRepeating is the amount of time before the method is called the first time, so maybe randomize this, or use a (static) counter to increase the interval a tiny bit for each enemy.
It’s just an idea, I have no idea whether it will actually work.
also use sqrMagnitude instead of magnitude. minor savings but its something. the number you get back is a little different so you will need to re-tune your script.
make sure you are not using rigidbody on your enemies. that will kill perf.
best case is to even get rid of a collision all together and just use the enemy location and fake when they get hit or run into things by checking their location in world space.
dont use a seperate sound node for each enemy. have them access a single sound node in the level or a few sound nodes and cycle threw them.
have as few children nodes in each enemy. the fewer there are the fewer items to deal with.
make sure they are batching the rendering correctlty. 20 enemys could cause 20 draw calls which would kill perf even if cpu was ok.
look into using particles instead. I am running into the same limit of about 10 to 20 and i have a slightly more complex AI than you do. one idea is that you might be able to run 100 particles and each particle could be an enemy etc etc. not easy to pull off but it might work.
i use a random number generated on start for each enemy to have a slight offset just in case they spawn close to each other. this makes my co-routines have a slightly different time setup so that they average out over gameplay instead of stack up and run everything at the same time.
make sure you xcode iphone setup is set to run at 60fps instead of the default 30. i found the iphone fluctuates way to much and doesnt really run at 30 with that setting, more like 27. but when set to 60 i normally get 30+.
if you are not using your accelerometer, turn it off or set it to 1 update per second in xcode. it will save you a few frames per second.
thanks for the reply. At the moment I have them all batching well. Each enemy is quite small in verts, so they dont really need to be particles.
As for the coroutines, we have already done random spawntimes, so that helped a little.
The things that I have noticed are taking the most CPU time are physics and mesh skinning. Is there any way to tone this down? Every enemy has a skinned mesh. Are there SM batching methods?
I use SM and it batches them… but I am not seeing better times than you are so I doubt you will see more than a few percent gain. What you want is like 200% gain or more.
The only way to improve your skinning performance is to use a lower influences per vert (1 bone per vert if at all possible) and just have fewer verts/bones.
What kind of physics do you have going on? Are your AI guys constantly bumping into each other and/or walls? That will kill performance pretty quickly. I found that rolling my own primitive collision detection for simple surfaces was radically faster than using colliders.
Or make them not rub against each other constantly… not sure how your AI behaves, but would it be possible to detect a collision between them and have one or both change direction, or otherwise create a gap between them? It’s probably just that they’re making prolonged contact, causing heavy calculations every frame.
I would recommend testing just how dramatic the colliders effect really is by setting their radius to 0 temporarily.
When I first jumped into Unity I started a cannon fodder clone, which ran at a very comfortable fps with even 15 enemies on screen… Until they got near other colliders…
Inevitably, I made my own obscenely primitive avoiding system based purely on distances.
However, removing colliders/rigid body also made ground detection substantially more difficult, so i worked around that by ray casting downward (the AI simply followed a waypoint at all times)… Every meter or so, they would be sent a new waypoint which would have the ground height…
I could also fine tune the distance/accuracy of how often I checked for the ground height. The reason was that sometimes I had flat ground, other times I had fairly bumpy terrain.
The project was abandoned for more profitable enterprises, because the new system also meant they could walk on top of anything (since as far as the ai was concerned, they just moved from one point in space to another), regardless of its height.
Could someone give me an example of how to distance the enemies from each other? I don’t use sqrMagnitude very much at all so I have no experience with it.
Start by subtracting present.position from player.position to get a vector:-
var offset: Vector3 = present.position - player.position;
Then, get the sqrMagnitude of the offset vector. This value will be equal to the square of the distance between the two points:-
var sqDist: float = offset.sqrMagnitude;
Now, instead of comparing the actual distance to the sight distance, compare the square of the actual to the square of sight:-
var sqSight: float;
sqSight = sight * sight; // In the Start function, say.
if (sqDist < sqSight) {
...
}
The significance of the squared distance is that this is involved in the calculation of the true distance - you have to take its square root and this is a time-consuming operation.
(I’d suggest you to use your own BoxCollider based collision manager (using onTrigger) instead of CharacterController. It is faster, as it’s only managing a simple collision state, leaving all the physics stuff away.)
You should definitely avoid using mesh colliders for your enemies if at all possible as they are very expensive. You should consider using box colliders and your own control scheme, rather than the character controller. The character controller is going to be too expensive with that number of combatants.