There are many different forms of object behavior. Sometimes a behavior is as simple as an object rolling along a surface under its own momentum, or being affected by gravity. Other kinds of behavior might involve some actual decision making. How much behavior an object exhibits should generally equate to its nature to make your game seem more realistic. This page will suggest some challenges for you to help you realize what is involved in making an object behave and make decisions, and the importance of good behavior system design.
If you want to study some advanced behavior modelling for game objects, take a look at the book Game Programming Gems.
Write an application that features a harvester object that wanders, bouncing around a rectangular area. At random times, a grain object appears and starts bouncing around, too. When a grain object comes into a certain range of the harvester object, the harvester object will change its direction towards the grain until it is consumed, and then go back to wandering.
To do this, you will need a value attached to each object to
tell the program what it is, a "harvester" or "grain".
You will also have two behaviors for the harvester, "wander" and "harvest".
Use enumerated types for these values, and give them meaningful names, like OBJECT_HARVESTER and BEHAVIOR_WANDER.
You will also need a reference to the target grain for an object that is harvesting.
The object's current behavior dictates what it does that frame. A harvesting object continues towards it target. A wandering object bounces off the edges of the play area. The grain objects have no behavior per se, unless you want to treat them as permanently wandering.
Since objects will be created and removed, I recommend using a linked list that handles deleted nodes to represent the objects in the playfield.
Advanced: Allow more than one harvester object. It is possible that two harvester objects will try to consume the same grain. When the grain is consumed, both harvester objects should go back to wandering.
You can probably get away with using if-then-else blocks in
your code to handle this. Split the logic into function calls as much as you
can, just to gear up for what is to come.
This example is simple enough to illustrate the kind of things that are necessary to dictate simple behavior in game objects. Hold on to your seat, though, it gets more complicated at an astounding rate!
More complicated behavior systems
This section won't offer any real challenges - it's more of a discussion on the behavior system offered by Game Programming Gems.
Without giving too much away, the behavior system discussed in that book is introduced as a messaging system between objects. If a laser strikes a craft, it sends the message "I have struck you" to the craft, and the craft will receive the message and react to it, causing itself damage and possibly making a decision based on the damage taken or where the laser came from.
A single global routine routes the messages, allowing the programmer to log all messages sent to allow for easier debugging.
Sent messages isn't the only way that behaviorial decisions take place, however. A craft could be on patrol and notice a target. This doesn't require a message to be sent, it's just a check on the surrounding environment because the craft is on patrol. A potential target is noticed, so the craft reacts. It can send the message "I am targetting you" to the target, in case the target can detect being targetted and has a defense or countermeasure against it.
An object basically has a current behavioral "state", and the decisions that could be made at any time depend upon that state. A patrolling craft, for example, may decide to attack a target, where a craft that is purely on reconnaisance will not. However, in both states, the craft will defend itself if attacked.
It is also possible to have several levels of behavior "machines" going on an object at the same time. For example, if a craft is on patrol, and decides to attack a target, the craft will need to perform maneuvering to get the target within its sites, evade countermeasures, etc. But, in essence, the craft is still on patrol. So there is a primary state called "patrol", a secondary state including "attack" and "evade", and a tertiary behavior including "bank", "accelerate", "fire weapon", etc. all with their own systems of logic.
Some behavior systems are applied simply without any real decisions. For example, if you set a craft's target velocity, that craft can evaluate against its current velocity and accelerate to meet its goal, given the set acceleration capability of that object. Some other system has to decide what that target velocity is, however. Ultimately, the numeric attributes at the object level involving its maneuvering and weapons fire, on a frame by frame basis, are the visible result of the behavior system.
Modelling behavior systems one of the most complex tasks that takes place in game engine design. You have to get it close to right the first time, otherwise you'll find yourself at a dead end quickly, needing to redo your behavior design.
Consider the complexity in behavior involving a jet on patrol around a circular waypoint system that encounters an enemy missile tower. The jet has AI that will attempt to evade missiles fired at it, but also has the goal of destroying the missile tower. At the very lowest level, the jet is firing its weapon, banking, turning, and changing its velocity. At the next level up, the jet is either patrolling, attacking the missile tower, setting up for an attack run, evading a missile, or avoiding a collision. Finally, at the highest level, the jet is either on patrol or attacking a target. It sounded simple at first, didn't it? I thought so too. Even the behavior changes involved in banking and then pulling up hard to perform a hard turn is complicated enough to require a serious design consideration.
I'll come up with some more complicated examples, perhaps we can work towards a decent behavior system together! We'll have to go nice and slow with this one...
The Harvester with Wolves
This is a modification of the Harvester to add some complexity to the behavior model. There are now wolf objects that might appear randomly, and will seek to attack the harvesters when in range. When a harvester sees a wolf coming, it will run to the nearest harvester that is on patrol for help. The patrolling harvester will then move toward the wolf and destroy it when he touches it. All of these things happen within a certain range. The wolf attacks when in range of any harvester, a patrolling harvester attacks a wolf within a certain range, and a harvesting harvester runs for help within a certain range of a wolf that is attacking him.
In order to handle this, we could decide that there are two different kinds of harvesters, but what if in a game, we wanted sometimes for a harvester to go on patrol, and at other times to harvest?
Handling this now requires two state levels. One that handles "Harvesting" and "Patrolling", and another that depends on what the higher level is. For "Harvesting", the secondary level could be "Wandering", "Consuming Grain", and "Running for Help". For "Patrolling", the secondary level could be "Patrolling" or "Hunting Wolf".
Ok, I'll be honest, it doesn't really require two state levels. Some behavior systems could be handled by just changing the object type, forcing a different behavior system. In the case of the harvesters, our object types could be "Patrolling harvester" and "Harvesting harvester". However, as behavior complexities increase, you may want to get used to the idea of multi-tier behavior systems to keep the behaviors of object types more "contained". It may help avoid convoluting your behavior library with too large of a list.
Connecting collision, pathing and behavior together
Consider this. You tell an object to move to the opposite corner on the map. It plots its path, setting up waypoints to guide itself along. On the way, a falling rock knocks it off course.
In another discussion I mentioned that your goal should be to get your game to behave the same no matter what machine it is running on. That means basing everything off time, considering as many acceleration curves as possible.
I'm going to put some thought into this one. I'll post a walk through of the logic required to handle it, and hopefully that will make you aware of the kinds of problems you'll face programming this simple example in a way that allows every object to make the most efficient use of the time they are provided on a single game tick.
(More to follow!)
- Reaches a waypoint in mid frame, should use the rest of the frame to move towards the next waypoint
- Collision takes place mid frame, should use the rest of the frame to move its new direction
- Gains self-control after collision, should use the rest of the frame to re-plot its path to its next waypoint
The idea is that anything could happen within a frame, and the rest of the frame should be used to handle that. My goal is to classify the kinds of challenges that are faced, with collisions, behavior and movement.
Ultimately, this should become a template for game object behavior, similar to the primary game engine itself. Other things that can be automatic:
- If the object is just a sound, then when the sound ends, delete
- If the object is just an animation, then when the animation ends, delete the object
Events will fire on an object when certain things happen - animation ends, sound stops, etc. The object will process the event and based on the code for that object, it will behave in a certain way.