O'Reilly logo

Programming Game AI by Example by Mat Buckland

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

z
TIP In addition to luabind::object, Luabind also provides luabind::func
-
tor
, which is a more lightweight object you can use if you only need to store
functions. See the Luabind documentation for more details.
Creating a Scripted Finite State Machine
To end the chapter I’m going to demonstrate how Lua, together with
Luabind, can be used to create a scripted finite state machine class. This
class is used in a similar way to the state machine class you’ve already seen
described in this book, except now the game agent’s state logic can be writ
-
ten in the Lua scripting language. Not only will this be a demonstration of
the power of scripting languages, but it will help consolidate everything
you have learned in this chapter.
As we have discussed, a scripted FSM has many advantages over a
hard-coded state machine. Because any new logic can immediately be
tested without recompiling the source, the frustration of the testing and
tweaking phases of an agent’s AI is reduced, resulting in faster develop-
ment cycles. Additionally, once the AI framework has been exposed to a
scripting language, you can hand your designer, artists, or whoever a copy
of the compiled game, together with a little documentation, and they can
fool around with the AI to their heart’s content without having to pester
you any further. Well, okay, you may have to work on the interface a little
until everyone is happy, but that’s about all. When the game is released you
can choose to either compile the script files, effectively encrypting them
from the prying eyes of the game player, or leave the scripts as they are,
provide a little documentation, and put the power of your engine into the
game players hands.
Ü
NOTE Luabind can be a great tool but, because it relies heavily on template
programming, the addition of it to a project will result in increased compile
times. This, alas, is the price we have to pay for its functionality.
How It Works
To be able to write state logic within a script file, the scripting language
must be able to access the interfaces of the relevant C++ objects. For this
example, I’m going to show you how the WestWorld demo from Chapter 1
can be converted to use a scripted state machine. As a result, the relevant
classes to expose to Lua will be
Miner and Entity. In addition, the methods
of the scripted state machine class itself must also be exposed to enable
state changes to be made from within a script.
The
StateMachine class used up to now has made use of the state design
pattern to implement its functionality. The
StateMachine class has a data
member of base class type
State, representing the current state of the
agent. This member variable can be exchanged at any time with any other
derived type of
State in order to change the functionality of the class. To
To Script, or Not to Script, That Is the Question | 285
Creating a Scripted Finite State Machine
provide similar behavior, the scripted state machine class has a member
variable of type
luabind::object, which represents the current state of the
agent. The states are created in Lua as Lua tables. Each table contains three
functions, providing the logic for the Enter, Execute, and Exit phases of the
state. This is easier to show than to describe. A Lua table providing similar
functionality to a C++
State class is created like this:
--create a table to represent the state
State_DoSomething = {}
--now create the Enter, Execute, and Exit methods
State_DoSomething[“Enter”] = function(pAgent)
--logic goes here
end
State_DoSomething[“Execute”] = function(pAgent)
--logic goes here
end
State_DoSomething[“Exit”] = function(pAgent)
--logic goes here
end
You’ll see some concrete examples of Miner states in a moment, but for
now it is enough to know that a Lua table like this can be assigned to a
luabind::object. Once assigned, it is a straightforward matter to call the
appropriate function using the
luabind::object::at() method.
Let’s take a look at the
ScriptedStateMachine class to see how these
ideas are put together. Check out the following code carefully. Notice how
the
m_CurrentState member variable acts as the holder for the current state
and how it is changed by passing a
luabind::object type to the
ChangeState method. Other than a few small alterations, the class looks
very similar to its C++
StateMachine cousin — and so it should because it
provides the same functionality.
template <class entity_type>
class ScriptedStateMachine
{
private:
//pointer to the agent that owns this instance
entity_type* m_pOwner;
//the current state is a Lua table of Lua functions. A table may be
//represented in C++ using a luabind::object
luabind::object m_CurrentState;
public:
ScriptedStateMachine(entity_type* owner):m_pOwner(owner){}
//this method assigns a state to the FSM
286 | Chapter 6
Creating a Scripted Finite State Machine

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required