JavaScript EditorFree JavaScript Editor     Ajax Editor 



Main Page
  Previous Section Next Section

Advanced AI Scripting

At this point, you should be quite the AI expert and things should be starting to gel. I've waited to talk about advanced AI scripting until now so you can see what I'm getting at with more of a foundation.

You already saw that a scripted instruction language can be used for AI when you learned about using a simple [OPCODE OPERAND] language and a virtual interpreter earlier in the chapter. This is a form of scripting, of course. Then I showed you yet another way to create decision trees with a scripted language based on logical productions and a set of inputs, operators, and action functions. This technology can be taken to any limits you want. QUAKE C is a good example, as well as UNREAL Script. Both of these actually allow you to program game code with a high-level English-like language that is processed by the engine.

Designing the Scripting Language

The design of the scripting language is based on the functionality that you want to give it. Here are some questions that you might ask yourself:

  • Will the scripting language be used for AI only, or will it be used for the entire game?

  • Will the scripting language be compiled or interpreted?

  • Will the scripting language be ultra-high-level, with an almost English-like syntax, or will it be a lower-level programming-like language with functions, variables, conditional logic, and so on?

  • Will all the gameplay be designed using the scripting language? That is, will the programmers do any hard-code game design, or will the entire game run with scripts?

  • What level of complexity and power do you want to give the scripters/game designers? Will they have access to system and engine variables?

  • What's the level of the game designers who will use the scripting language? Are they HTML coders, entry-level programmers, or professional software engineers?

These are the kinds of questions that you should think about before you start designing the language. Once you've answered these and any other questions, it's time to implement the language and really design the entire game.

This is a very important phase. If your game is going to be completely controlled by a scripting language, you'd better make it really open-ended, robust, extensible, and powerful. For example, the language should be able to model an airplane that flies by every now and then, as well as a monster that attacks you!

In any case, remember that the idea of a scripting language is to create a high-level interface to the engine so that low-level C/C++ code doesn't need to be programmed to control the objects in the game. Instead, an interpreted or compiled pseudo-English language can be used to describe actions in the game. This is shown in Figure 12.28.

Figure 12.28. The relationship between the engine and scripting language.

graphics/12fig28.gif

For example, here's a script for an imaginary scripting language to control a street light:

OBJECT: "Street Light", SL

VARIABLES:

    TIMER green_timer; // used to track time
// called when object of this type is created
ONCREATE()
BEGIN

// set animation to green
SL.ANIM = "GREEN"

END

// this is the main script
BEGINMAIN


// is anything near the streetlight
IF EVENT("near","Street Light") AND
   TIMER_STATE(green_timer) EQUAL OFF THEN
   BEGIN
   SL.ANIM = "RED"
   START_TIMER(green_timer,10)
   ENDIF

// has the timer expired yet
IF EVENT(green_timer,10) THEN
   BEGIN
   SL.ANIM = "GREEN"
   ENDIF

ENDMAIN

I just threw this together right now, so it may have some holes in it, but the point is that it's very high-level. There are a million little details about setting animations, checking proximity, and so forth, but with this language anyone can make a traffic light program.

The code starts up and sets the light to green with ONCREATE(), and then it tests if anything is close by with the EVENT() test and turns the light red. After the light has been red for awhile, it turns back. The language looks a little like C, BASIC, and Pascal all mixed together—yup!

This is the kind of language you need to design and implement to control a game—something that is neutral and knows how to operate on any object. For example, when you say BLOWUP("whatever"), the language processor better know how to make that work for any object in the game. Even though the call to blow up a blue monster might be TermBMs3(), and the call to blow up a wall might be PolyFractWallOneSide(), you just want to say BLOWUP("BLUE") and BLOWUP("wall"). Get the point?

You're probably wondering how to implement one of these scripting languages. It's not easy. You have to decide whether you want an interpreted or compiled language—is the language going to compile into straight code, be interpreted by an interpreter in the game engine, or something in between? Then you have to write the language, a parser, a code generator, and a P-code interpreter, or make the code generator create straight PentiumX machine code or maybe translate to C/C++. These are all compiler design issues and you're on your own here, but you've already written a couple of baby interpreters.

Some tools to help you are LEX and YACC, which stand for Lexical Analyzer and Yet Another Compiler Compiler. These are language parsing and definition tools to help you implement the recursive decent parser and complex state machines needed for a compiler or interpreter. I have a trick that you can use to get started without needing to write a full-blown language compiler/interpreter. Hold onto your hat!

Using the C/C++ Compiler

The nice thing about using an interpreted language is that the engine can read it and the game doesn't need to be recompiled. If you don't mind your game designers compiling (they should know how to anyway), you can use an old trick to make a crude game-scripting language: Use the C/C++ preprocessor to translate your scripting language for you. It takes nothing more than header files and the C/C++ source, which have nothing to do with compiler design.

The C/C++ preprocessor is really an amazing tool. It enables you to perform symbolic referencing, substitutions, comparisons, math, and a lot more. If you don't mind using C/C++ as the root language and compiling your scripts, you might be able to write your scripting language by means of a clever design, a lot of text substitutions, a lot of canned functions, identifiers to refer to objects, and a good object-oriented design.

Of course, under it is going to be real C/C++, but you don't have to tell your game designers that (if you have any). Or you can force them to use only the pseudo-language and not use all the real C/C++ functionality.

The best way to show you this is with a very simple example (that's all I have time for). First, the scripting language will be compiled and each script will be run whenever the object it refers to is created. The script will be terminated when the object it refers to dies. The scripting language you're going to write is based on C, so I'm not going to go over everything there. But I am going to use text substitutions for a lot of new keywords and data types.

A script consists of these parts:

Globals section— This is where any global variables that are used in the script are defined. There are only two types of data type: REAL and INTEGER. REAL holds real numbers like the C-type float, and INTEGER is similar to the C-type int.

Functions sectionThis section is composed of functions. Functions all have this syntax:

data_type FUNCNAME(data_type parm1, data_type parm2...)
BEGIN
// code
ENDFUNC

Main section— The main of the program is where execution begins, and it will continue to loop here until the object is dead:

BEGINMAIN
// code
ENDMAIN

As for variable assignment and comparison, only the following operators will be valid:

Assignment variable = expression;
Equality (expression EQUALS expression)
Not Equal (expression NOTEQUAL expression)

Comparisons—Greater than, less than, greater than or equal to, and less than or equal to all use the same C standard, which follows:

(expression > expression)
(expression < expression)
(expression >= expression)
(expression <= expression)

Conditionals—The form of conditional statements is the same as C, except that the code that executes when the statement is TRUE must be contained within a BEGIN ENDIF block. Look at the following example:

if (a EQUALS b)
   BEGIN
   // code
   ENDIF
else
   BEGIN
   // code
   ENDELSE

Similarly, the else block must be contained within a BEGIN ENDELSE block as well as the elseif.

There are no switch statements in this language, and there's only one kind of loop in the language, called a WHILE loop:

WHILE(condition)
     BEGIN
     // code
     ENDWHILE

Next, there's a GOTO keyword that jumps from one point in the code to another. The jump must be labeled with a name of the form:

LBL_NAME:

where NAME can be any character string up to 16 characters in length. See the following example:

LBL_DEAD:

if (a EQUALS b) BEGIN
GOTO LBL_DEAD;
ENDIF

You probably get the point by now. Of course, you'd want to add dozens or even hundreds of high-level helper functions that could perform tests on objects. For example, for objects that have a health or life state, you could have a function called HEALTH():

if (HEALTH("alien1") > 50)
    BEGIN
    // code
    ENDIF

Moreover, you might create events that could be tested with a text string parameter:

if (EVENT("player dead")
    BEGIN
    // code here
    ENDIF

All this magic is accomplished by using clever global state variables and making sure to expose enough generic events to the scripts, along with system state variables (via functions) and a lot of text substitution via the preprocessor. Leaving out some of those details to keep things simple, let's take a look at what you need for text substitutions thus far. Referring to Figure 12.29, each script that is compiled will be processed first through the C/C++ preprocessor.

Figure 12.29. Using the C/C++ preprocessor for a script language interpreter.

graphics/12fig29.gif

This is where you're going to make all those text substitutions and convert your little script language back to C/C++. To make it work, you tell the scripter to save all the files with the extension script .SCR or something, and when the file is imported into your main C/C++ file for compilation, you make sure to first include the script translation header. Here's the script translator for what you've so far:

SCRIPTTRANS.H

// variable translations
#define REAL static float
#define INTEGER static int

// comparisons
#define EQUALS   ==
#define NOTEQUAL !=
// block starts and ends
#define BEGIN {
#define END   }
#define BEGINMAIN {

#define ENDIF   }
#define ENDWHILE }
#define ENDELSE  }
#define ENDMAIN }

// looping
#define GOTO goto

Then you include the following in your game code:

#include "SCRIPTTRANS.H"

Then you include the actual script file somewhere in your game engine at the proper moment. You can do it at the beginning, or even in a function:

Main_Game_Loop()
{
#include "script1.scr"

// more code

} // end main game loop

This part is up to you. The point is that the code the scripter writes must be compiled somehow, and it must be able to access the globals, see events, and make calls to the function set you expose. For example, here's a crude script that fires an event (which I didn't define at the count of 10):

// the variables
INTEGER index;

index = 0;

// the main section
BEGINMAIN

LBL_START:

if (index EQUALS 10)
   BEGIN
   BLOWUP("self");
   ENDIF

if (index < 10)
   BEGIN
   index = index + 1;
   GOTO LBL_START;
   ENDIF

ENDMAIN

Obviously, you would have to define BLOWUP(), but you get the picture. This code would be translated by the preprocessor into the following:

{
static int index;
index = 0;

LBL_START:

if (index == 10)
   {
   BLOWUP("self");
   }

if (index < 10)
   {
   index = index + 1;
   goto LBL_START;
   }
}

Cool, huh? Of course, I'm leaving out a lot of details, like problems with variable names colliding, accessing globals, debugging, making sure the script doesn't sit in an endless loop, and so forth. However, I think that you have an idea about using the compiler as a building block of a scripting language.

TIP

You can tell the Visual C++ compiler to output the preprocessed C/C++ file with the compiler directive /P.


      Previous Section Next Section
    



    JavaScript EditorAjax Editor     JavaScript Editor