9
\$\begingroup\$

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?

\$\endgroup\$
  • \$\begingroup\$ Can anything other than this function change the position of the object or its next waypoint? If not, there are a lot of things you can cache from one frame to the next. \$\endgroup\$ – JS1 Feb 13 '17 at 5:01
  • \$\begingroup\$ @JS1 This should typically be the only method that changes the object's position, unless i decide to manually change it's position. \$\endgroup\$ – TychoTheTaco Feb 13 '17 at 5:05
  • \$\begingroup\$ @200_success The path is just an ArrayList of 2d points (so just an x and y value) and the enemy will move towards each waypoint one at a time. Once it reaches its target, it will move to the next. So in other words it is just following a path made up of points. Each point is about 10px apart from the next. \$\endgroup\$ – TychoTheTaco Feb 13 '17 at 5:08
  • \$\begingroup\$ Are the waypoints determined in advance, and if so, how? \$\endgroup\$ – 200_success Feb 13 '17 at 5:10
  • 1
    \$\begingroup\$ Are you sure the bottleneck is the update and not the draw? \$\endgroup\$ – Peter Taylor Feb 13 '17 at 11:10
6
\$\begingroup\$

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 includes cos(directionToWaypoint) and sin(directionTpWaypoint)
  • translationX and translationY become simple multiplication
  • distanceTravelled seems too complicated. Why isn't it just getMovementSpeed() * delta?
\$\endgroup\$
3
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.