FPS Patrol and Updates on iPhone using SimpleMove

I have a number of GameObjects that have attached scripts that Patrol between WayPoints using SimpleMove. This works fine on my desktop. But when I run it on the iPhone, the GameObjects sometimes move in circles, and the movement is jerky. I am using the SpriteManager, so I don’t think the problem is too many DrawCalls.

The documentation on SimpleMove said that only one call per Frame should be made to SimpleMove. I figure I could make the call to SimpleMove in an Update function in the GameObject’s script, which isn’t currently there. I could put an Update function in the script attached to each GameObject, but I remember reading in the forums, that for iPhone, one wants to minimize the number of Update calls made.

So my question is: How should the Update calls for the scripts attached to the GameObjects be made? Should there be one Update function attached to the Camera, and if a script attached to a GameObject is ready to execute a SimpleMove, then the Update function attached to the Camera allows the SimpleMove attached to the GameObject to be executed? Or is there a better approach?

I have the following code to break the Patrol while(true) loop:

var getCompBoat : CharacterController = GetComponent(CharacterController);
getCompBoat.SimpleMove(direction);
yield;

Should I replace it with:

var getCompBoat : CharacterController = GetComponent(CharacterController);
getCompBoat.SimpleMove(direction);
yield new WaitForFixedUpdate();

so the SimpleMove only occur every FixedUpdate?

Does WaitForFixedUpdate wait for FixedUpdate in that script or waits for a FixedUpdate in any script?

Can you post the code that you have in the Update function? Unless the control code is putting a heavy load on the CPU, then you should be able to do some movement each frame without having to tinker with the framerate.

As regards moving in circles, do you mean that the patrolling object keeps “orbiting” around the last waypoint in the path? The cause of this behaviour is usually that the script turns to face the waypoint and also moves forward on each frame. This works OK when the object is far from the target, but as it gets closer, it can get into a state where it is moving and turning at its maximum rate. And if it is moving at constant speed and turning at constant speed, then it is going in a circle. The fix is to allow the object to slow down as it gets near the last waypoint, or else to stop before it gets close enough to start orbiting.

andeeee:

What I am seeing is that the “direction” values sent to SimpleMove on the desktop are small compared to the “direction” values on the iPhone. Which made me think that the time between calculating the “direction” values for SimpleMove were larger on the iPhone because the CPU was overloaded on the iPhone. So I removed all of the Finds and GetComponents from the Patrol portion of the script attached to the GameObject, and moved them to the Awake portion of that script. This sped things up a bit, but I still see the circling, which I don’t see on the desktop.

Each GameObject has a script attached to it which has the basic functionality below (which was lifted from the AI.js script in Lerp 3D FPS tutorial)

function Start(){
  Patrol();
}

function Patrol(){

  while(true) {
    if (Vector3.Distance(waypointPosition, transform.position) < pickNextWaypointDistance) {
      curWayPoint = PickNextWaypoint (curWayPoint);
    }
    MoveTowards(waypointPosition);
    yield;

  }

}

function MoveTowards (position: Vector3) {
   var direction = position - transform.position;
   direction.y = 0;
   transform.rotation = Quaternion.Slerp (transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
   transform.eulerAngles = Vector3(0, transform.eulerAngles.y, 0);

   var forward = transform.TransformDirection(Vector3.forward);
   var speedModifier = Vector3.Dot(forward, direction.normalized);
   speedModifier = Mathf.Clamp01(speedModifier);
   direction = forward * speed * speedModifier;

   GetComponent (CharacterController).SimpleMove(direction);

}   
   
}

On the desktop, the advances to the waypoint, as evidenced by the “direction” input to SimpleMove, start small and get bigger as the GameObject gets closer to the waypoint. But on the iPhone, the values of “direction” start off quite large, which causes the movement to overshoot the waypoint and oscillate and also spin.

On the desktop, when approaching a waypoint whose x position is at 340.0, the direction vector for the respective position vector are:

x-position       direction
----------------------------------
344.1            (-4.2, 0, 15.3)
344.0            (-14.2, 0. 25.5)
343.5            (-24.5, 0, 29.5)
342.6            (-34.1, 0, 29.7)
341.4            (-41.7, 0, 27.6)
339.9            (-12.6, 0, -6.2)

whereas on the iPhone it looks like

x-position       direction
----------------------------------
344.1            (-60.0, 0, 0.0)
324.0            (  52.8, 0. -18.3)
333.5            (  53.2, 0, 27.8)
344.4            (-60.0, 0, 0.0)
325.1            (   59.0, 0, -3.3)

Once the GameObject is within a distance of 2 of the waypoint, then I pick a new waypoint. But on the iPhone, the GameObject keeps overshooting the waypoint and thus never gets a new waypoint selected, and thus circles.

A few questions:

  1. The “yield” causes a break from the MoveTowards function. What causes re-entry into the MoveTowards function? Is it once all the other functions have been executed?

  2. There are two Update() functions, 1) on the MainCamera that counts time before more GameObjects should be added, and 2) one that monitors when the user has the touched the screen. Should I only have one Update() function?

  3. Do you have a good suggestion on how to slow down the GameObject as it approaches the waypoint?

As far as I can see, the Patrol function doesn’t get called again (unless you are calling it from somewhere else you haven’t shown). You could ensure it gets called periodically by changing the yield to “yield WaitForSeconds(delay)”, where delay is the time between direction updates.

You can have an Update function on any object that needs to be updated every frame.

You can use something like this:-

var slowDownDistance: float = xxx;

   ...

if (distance < slowDownDistance) {
    var slowDownFactor: float = distance / slowDownDistance;
    speedModifier *= slowDownFactor;
}

andeeee:

Thank you for your answers.

  1. Patrol doesn’t get called again, but inside Patrol there is a while(true) loop that runs continuously calling MoveTowards. The yield statement causes a break from the while(true) loop. Sometime later something causes re-entry into the while(true) loop… Does re-entry occur when all the other functions have been executed or after all the Updates have been executed?

  2. I wanted to minimize the number of Update() functions when running on the iPhone because I’ve read that having multiple Update() functions slows down performance on the iPhone. Is that true?

  3. I’ll try the slowDownFactor. Thanks!

andeeee;

  1. The slowDownFactor worked great. No more circling. Thank again!