Agent-based vs turn-based game objects

Started by
6 comments, last by Spoonbender 19 years, 3 months ago
This was part of another thread but I thought I'd clean it up and make it its own topic: It is regarding the difference between having your "game objects" act as either: 1) active 'agents' in your game, acting and responding to the world in a continuous, real time fashion or: 2) running a loop where you sequentially call an 'update' function on each object, allowing it to a) analyze changes in the game's state since its last turn, and b) act / change the game's state in a definite amount of time (ie no 'waiting' for something to happen) Note that by 'game object' i refer to things that exists in your game's world. Not neccesarily an instance of a class. For instance: a character, a sword, a planet, a box I was arguing that while the first approach may be more difficult to implement initially, it allows much greater flexibility when programming your objects, or adding a new object type into the game later. As an active agent, I can act whenever I feel like it, respond to events as they happen, or do a sleep(sleeptime) when I want to delay. I dont have to worry about how this affects the other objects at the code-level, so I can focus on what is happening around me in the game universe. I can start planning and speculating about the future, updating my plans in response to single events I receive as they happen, rather than having to re-analyze the entire state of the world and the multiple changes to it since my last turn. Of course the trick here is, how do you give EVERY object in the game the illusion that it is existing and acting in parallel/real-time with all others. -Multiple threads? -Something else? Or is there some way to alter the sequential/loop version to give that illusion as well? Thought it might be an interesting discussion :)
Advertisement
Micro threads might interest you, they provide a cheaper, and less difficult, way to run many threads, as opposed to OS threads. Using a more event driven approach to AI objects might work too.
Game Programming gems 2 has a couple of articles on microthreads and AI game programming wisdom has articles on a state machine language which is excellent.

The microthreads allow you to basically program each "agent" as though it's in it's own thread, with a bit of extra code, whilst the state machine language allows you to easily code up event driven AI.
What''s a cleaver?
Quote:Original post by Satook
Micro threads might interest you, they provide a cheaper, and less difficult, way to run many threads, as opposed to OS threads. Using a more event driven approach to AI objects might work too.
Game Programming gems 2 has a couple of articles on microthreads and AI game programming wisdom has articles on a state machine language which is excellent.

The microthreads allow you to basically program each "agent" as though it's in it's own thread, with a bit of extra code, whilst the state machine language allows you to easily code up event driven AI.




I wasnt aware of the concept of micro threading before but it definitely seems like a great way of approaching this issue. I think I may have been considering something similar, allowing multiple agents to share a thread.

is microthreading applicable to .NET ? I only see libraries/info related to C or java ie python
The basic problem is that all shared data must be accessed through mutexes and you run the risk of deadlock. Usually game objects are highly interactive and the result is that the code becomes more complicated, not less complicated.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:Original post by JohnBolton
The basic problem is that all shared data must be accessed through mutexes and you run the risk of deadlock. Usually game objects are highly interactive and the result is that the code becomes more complicated, not less complicated.


True, but lets assume that all accessing (or updating) of shared data is abstracted out into other sections of code which agents must use to retrieve info about the world (or change it). These sections take care of mutexes/locking/deadlocks. As you code the agent, you dont worry about it, as long as you are requesting information or changes in the 'legal' way
Quote:Original post by whitee115
Of course the trick here is, how do you give EVERY object in the game the illusion that it is existing and acting in parallel/real-time with all others.
-Multiple threads?
-Something else?
Or is there some way to alter the sequential/loop version to give that illusion as well?


Well, the best way I can think of to give that illusion would be to let every object get a chance to act based on the game state at the beginning of the turn, and then only apply all these actions at the end of the turn. That way, they'd all get to act "at the same time" (in the sense that nothing has changed in the game state since the the previous object acted)

Essentially it'd still just be a normal singlethreaded loop, except you don't update the game state until the end of the iteration.

The problem with threads is that you do have to, as you said, "re-analyze the entire state of the world and the multiple changes to it since my last turn.", because you never know how old your data is. If you need to collect two variables (say, your HP and your enemy's) and use them to decide on some action, then you'd prefer that they're both retrieved at the same time. But with threads, you might be paused after you've retrieved one of them, and then only get to run again much later. If one of the variables is 3 minutes old, and the other is up to date, your agent might decide on some very strange actions. ;)
if you want to avoid this, you'll have to write the mutexes into your agent as you code it, which of course makes it awfully complex. YOu can't really abstract those out into other sections of code.
Just to clarify, micro threads don't suffer from many of the concurrency problems of normal OS threads. This is because they are usually done as cooperative, not preemtive threads. The only thing you really need to be careful of is dangling pointers, but using handles and doing a test should fix this.

-- some pseudo microthread code
while(not at destination)
{
take one step towards destination

threadSwitch()
}

The threadSwitch function cycles to the next microthread and gives it the CPU. The threads allow you to code near-linearly, without semaphores/mutexes/etc, just make queeries atomic, don't switch threads during the function call. Some care is needed, but it's not like coding with preemtive, ie OS, threads.

As for a .net implementation, if you can use inline assembly and can find out which registers/etc need to be "saved" then it's entirely possible, just remember to save the stack as well. For CLI stuff, there might be differences if you're using other platforms, ie mac/linux.
What''s a cleaver?
But if the original purpose with using threads was to avoid having each agent holding up the rest of the game, then using microthreads that require you to explicitly switch threads sort of defeats that purpose... ;)

This topic is closed to new replies.

Advertisement