Your update loop sounds rather slow regardless of how many actors are on the level. From your description (correct me if I'm wrong) with 50 actors your update loop is 20 times a second. This is without rendering. As a rule of thumb I think you should be able to do twice that without much trouble.
What language are you using? What platform? What functions are taking the most time according to your profiler?
About the 50 actors -> 20 times a second, you are partially correct; it is the worst-case scenario. That is assuming every actor always takes a turn, after the player takes its turn. In practice, this varies a lot due to energy differences, since different actions consume different amounts of energy. Also, since this is a turn-based game, the game spends most of its time doing nothing, and checking the player's input. Are you saying 50 actors should be updating faster than 20 times a second?
This project is written in Java, and I'm using Eclipse. I have worked on the renderer for a long time, it pretty much works at optimal speed (this is a graphical roguelike). In those worst-case scenarios where every actor takes its turn after the player, this logic part definitely takes the most time.
Chances are it's not the loop itself that's the problem, but what's going on inside the loop. Maybe the actors or physics events are too involved, ie the AI is too complex or inefficient?
I forgot to mention, this test was made without using any actual physics events and with a stupid 'walk-to-a-random-next-square' AI, so this is definitely not the issue. Everything executed by this piece of code were empty placeholder events.
You may want to try a list that automatically sorts itself or try resorting the list in place rather than removing the actor then adding it again. Collections often have built in methods that have low level optimizations - especially in higher level languages. Try to stick to using those.
That's already in there. My actor list is a type of queue (Java's PriorityQueue), which is optimized to take only the head of the list, and is automatically sorted based on the actor's action energy.
and he is re-entered in the list.
What actually this means? And why there are two lists?
One list keeps the currently active physics events. The other (the PriorityQueue) keeps the actors that are currently in play. The idea is that there is always only one active event, since it's a turn based game; but the execution of this event can result in a few physics events, of which there can be more than one at the same time and should execute simultaneously.
Example: a wizards zaps a fireball in the direction of a group of other actors. The flying fireball is a single physics event (a projectile event). Once that event is complete (impact of the fireball), the explosion triggers knockback on the entire group of actors, thus a projectile event is thrown for every actor caught in the blast, throwing them all back at the same time.
I only know of three ways to improve performance:
- Do less work.
- Use a more appropriate algorithm.
- Use a more appropriate data structure.
The best option by far is to do less work. One thing I did for one of my games is to make creatures stay idle until the player sees them or they take damage (from traps, etc). That way most creatures aren't really involved in the update loop. Another thing is that you may be able to cut down on the amount of animations or use less granular time slices. It's hard to say without knowing how your physics events work but I'd guess that reducing the number of times you loop through before returning to the player will have the largest benefit by far.
And of course, profile everything and focus on what takes the most time.
Like I said, this was purely a test of the logic loop. Animations, physics, and AI calculations were taken out for this test. As far as I know, I have to agree with you, what I need is a better turn-based algorithm for my list, and/or a better data structure for my level. I'm just not sure what I need, that's what I'm here for.
1. Do you need to be updating guys that are off screen? How far off screen?
2. Is a bustling town going to have more than 50 moving actors on screen at once?
Others can surely help you speed this up with more efficient coding, but after that then one big work around I've used is to not compute things too far away from your character.
If you do need them to move around a bit to give the illusion of a living world then simplify their behavior off screen. Something like a drunken walk instead of a complex decision tree.
1. I'm not sure, actually. I definitely want guys that are kinda near the screen to be somewhat useful. One of my questions/ideas was using a chunk-like structure for very large levels, and like some of you said, limit the region that's being updated. I'm just not sure how this data structure is best implemented, that's what I'd like to know. One thing of note I forgot to mention is the fact that my level grid is contained in a much simplified version of a quad tree.
2. Well, if I had to take a guess, there would be very few, if no occasions where there actually are 50 actors on screen. 50 actors or more in an entire level however (which could possibly be divided into pieces given an appropriate data structure) would definitely be more frequent.