Managing Unit Actions
by (14 April 2003)
|Return to The Archives|
Have you ever played Starcraft ? Or M.A.X.? Or any other game where many tiny creatures roam the land,
run around and perform their tasks? This article tries to explain how to manage all of those independent entities, and it might be useful if you want
to implement something similar.|
Let's start with a simple concept of MVC. MVC stands for Model View Controller. This is not a game-industry-only term - most of you have probably heard of it before. Let's assume you have a system (I'm using 'system' here as a very general idea) which behaves according to certain rules. The Model part would represent your system, storing all information on system state, data, etc. However, sometimes you might want to keep parts of your system hidden, a kind of a 'black box' approach, making available only certain aspects of it. This is where the View comes into play. Let's think of 'View' as a specific representation of your system. And one last thing: your system probably does not remain static all the time. Its internal state/data can change when certain operations are performed on it. This is the Controller part - all those external factors that can change your system's state.
Let's put the MVC idea to work. Let's assume that you have in your game one of those tiny creatures I talked about earlier. We'll call it a drone. Our drone is a simple creature. It can only walk from one location to another. This is the data that describes our drone:
You can have more data describing your drone, like energy level or health or items being carried, but what we have now is enough for the purpose of this article. All that data is our system Model, drone being the system in this particular case.
Now let's talk about the Controller. Our drone can accept orders from two sources: one being a human player, the other being an AI (Artificial Intelligence, for the purpose of this article let's assume it's a set of rules our drone should follow). Both sources can use various means to pass information to the Controller. Those could be: keyboard events (local player), network events (in a multiplayer game, for example), or events generated by the AI. No matter how those orders arrive, they all have to be translated to common events, which can be understood by our drone. Our Controller is not the code that deals with keyboard or network traffic, it's the code that passes those final unified events to our drone's event queue. This way it can control the drone's behavior. I think it would be good to mention here that those events do not have to correspond to player orders only (like 'go to this location' or 'turn 90 degrees to the left'). They can also represent internal events (like 'blow up' or 'disappear'). Anything that modifies our drone's internal state can be represented as an event. And being blown up is definitely a state change.
OK, so where is the View? You have to be careful with the View idea. Intuitively someone might think of it as something that we can see, like graphical representation of the drone (a 2d sprite or a 3d model). But that's not it. The View here is all information available to the external user (any other code that needs to obtain information about our drone). For example, you might want to hide drone's velocity and eventQueue from other parts of your code, and add methods that provide information obtained by processing that data.
It's good to know the MVC approach since it helps in separating the code into specialized manageable pieces. For example, your Controller code might work in a separate thread (accepting events from the network), and you might have to add those events to the drone's event queue in a thread-safe manner. The same goes for the View methods. You shouldn't obtain information from your drone while it's being updated.
Keeping all this in mind, let's talk about how to deal with all that data. Remember, you might have many of such drones in your game world, and all of them could have different orders. I'd like to introduce a notion of high level events and low level events. High level events are of a general (high-level) nature. All those orders we talked about earlier ('go to this location' or 'turn 90 degrees to the left') are high-level events. They don't tell you how to perform a certain task exactly, they just provide the general information needed to accomplish the task. An example: let's say you have a map, where each location can be uniquely described using the 3d coordinates (x,y,z). You want the drone to go to one particular location. So you click on the map and you obtain the destination coordinates. Your path finding algorithm generates a list of motions your drone needs to perform to get to that location and places it in the drone's event queue. Those events could look like this:
All those events could be placed in the drone's event queue, or you could place a general order (move to final destination:(x,y,z)) and process it later, right now that doesn't matter. The important part is that you store information about the order issued to your drone as a high level event. Also it's important to notice that high level events do not store information about the entity (drone) they are affecting. They are only placed in the drone's event queue.
It's time to talk about low level events. Let's assume you have a bunch of drones that move around or simply do nothing. At any given time you have to be able to display some or all of your drones on the screen. This a global process - you have to gather the current data from all your drones and place it somewhere and then use that data to draw the current screen. Low level events represent the current state of any of your drones. They are placed in a global processing queue with every time slice (I'm assuming a non-turn-based approach here). They are derived from high level events and the current state of a particular drone. For example:
Let's assume that our drone event queue contains one moveTo:(x,y,z) event. We analyze the current drone position - if it's already there we remove the moveTo event from the queue. If it's not there yet, we create a low level event that will get us a little closer to the destination. We analyze the current drone position, we check the current drone velocity and direction, we do whatever needs to be done according to game world physics or rules, etc. If the drone graphics are animated, we advance animation to the next stage (for 2d, this could be the next sprite, for 3d we could rotate the drone turret or something), etc. At this point our drone's internal state is changed (updated) to reflect the next motion step. The we create a low level event(s) (moveTo:(xStep,yStep,zStep), turn:(aStep degrees)) and we place it on the global queue.
One important thing to mention here is that low level events contain information about the entity (drone) they pertain to, as opposed to high level events, which do not need to store this information. This is necessary since we have only one global queue and it stores events obtained from all our drones. Thus we need to be able to find the source of information, to get the visual representation for example.
After all low level events are placed on the global queue, we can process them all and draw everything to the screen.
There is one more aspect of using event queues in your drones. Queues can serve as memory of your drones. If your drone is performing certain actions, and you need to interrupt it and have it do something else, you insert the new event (order) at the top of the particular drone's event queue. Once that event is processed, the drone can continue to process the remaining events. Of course, this is optional - you might not want to retain previous orders once they have been cancelled.
This document does not describe the details of how to implement your game's physics or your game's AI, but hopefully it will help you to manage your in-game entities better. If you have other ideas/experiences on the subject let me know, also if you have any questions send email to: email@example.com. I'm working on a demo that would show several controllable drones in action, hopefully it will be done soon.