Problem
I am making a game for Android using Java and libGdx. I have an ArrayList
of enemies that are updated each frame. The update method for the enemy looks like this:
public void update(float delta){
if (waypointIndex < waypointCount){
waypoint = path.getPoints().get(waypointIndex);
distanceToWaypointX = waypoint.getX() - getCenterX();
distanceToWaypointY = waypoint.getY() - getCenterY();
directionToWaypoint = (float) Math.atan2(distanceToWaypointY, distanceToWaypointX);
setRotation((float) Math.toDegrees(directionToWaypoint) - 180);
translationX = (float) (Math.cos(directionToWaypoint) * getMovementSpeed() * delta);
translationY = (float) (Math.sin(directionToWaypoint) * getMovementSpeed() * delta);
distanceToWaypoint = (float) Math.hypot(distanceToWaypointX, distanceToWaypointY);
if (distanceToWaypoint <= 5){
waypointIndex++;
}
distanceTraveled += Math.sqrt(Math.pow(translationX, 2) + Math.pow(translationY, 2));
translate(translationX, translationY);
}
}
This works OK, but with 100 enemies the FPS starts to dip into the low 50’s. I would like to keep a consistent 60 FPS and I plan to have more than 100 enemies on the screen at a time. How can i improve this code to make it more efficient?
Solution
Some things you could try:
Unless the waypoints change position, the objects will continue in the same direction each frame. This means
- you don’t have to recalculate
directionToWaypoint
(unless you have imperfect rotation). This includescos(directionToWaypoint)
andsin(directionTpWaypoint)
translationX
andtranslationY
become simple multiplicationdistanceTravelled
seems too complicated. Why isn’t it just getMovementSpeed() * delta?
You could try not updating all your entities, on all your frames.
This is standard practice in games, and at high framerates, is unnoticable.
You would split entities in 10 buckets, and only update one bucket per frame, then cycle through.
Quick example using Guava’s MultiMap (I had to use current time to keep track of update intervals):
MultiMap<Integer, Entity> buckets = new MultiMap<>();
Map<Integer, Float> lastUpdate = new HashMap<>();
int currentBucket = 0;
public void update(float curTime){
float delta = curTime - lastUpdate.get(currentBucket);
buckets.get(currentBucket).forEach(ennemy -> update(ennemy, delta));
lastUpdate.put(currentBucket, curTime);
currentBucket = (currentBucket + 1) % buckets.size();
}
For rendering this is perfectly fine.
It can also be used for applying game logic, but it becomes more complex:
With Entities interacting with each other while belonging to different buckets, you’d have to force update both: When updating unitA, you encounter unitB which is in a different bucket, so you advance unitB to the current timeStep (if it encounters other units, repeat), then put unitB into unitA’s bucket, then proceed normally.
It might be good to split your units geographically to mimize this bucket-switching happening too much.