Chapter 11 - Game Events and Scripting Languages

This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

  • Chapter 11 - Game Events and Scripting Languages

    Here's the newest chapter - game events and scripting languages.

    If you haven't noticed - the chapters have been coming along fairly quickly...I'm about to put Ch 7 and Ch 10 through the 2nd draft too.

    I'm really happy with the feedback up here - I believe we're going to have a real winner here!
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • RE: Chapter 11 - Game Events and Scripting Languages

    "This might be the most important chapter in the whole book. "

    Yay!! (I'm such a friggin' dork)

    --

    "Another messy problem is how do you fine tune what a game object really does? In the case of our little explosive, we probably have a certain amount of time that it takes to activate, it might play a sound effect and run a "blinking red light" animation, and eventually explode after a certain amount of time. There's a lot of data there - the times for activation and explosion, the animation, the sound effect. It would be nice to keep all this data outside of the compiled code base, wouldn't it? Then you could even change this data and reload it each time you threw one of these devices, and get the whole thing working and fine tuned really quickly. The alternative of writing, changing, and tuning compiled and linked code or data is simply way too slow. "

    A potential (and possibly much more important) benefit: Using scripts can mean that non-programmers (or at least not the main programmers) can change/tweak settings without needing to know how to change it, recompile, etc. (which can be a learnign curve in itself). This means that the game designers or even the content, level, or story designers can edit it without knowing a thing about the code (and in fact, without knowing a thing about C++). In my opinion, this is pretty awesome, and worth mentioning.

    Also, you can obviously tweak a lot of the balancing and gameplay elements quickly, which is sort of the same as what I just said.

    Regardless, I think this paragraph is where this information should go (I haven't read the next section yet, so I don't if you included this information yet)

    The fact is (and I read a really cool website saying this, I'll find it if I can), if all you are using a scripting engine for is to avoid recompiling, then you might as well use Python (or similar). A designed scripting engine should NOT look like code, and should not require a programmer to write (and definately not the programmer who wrote the engine). My personal engine does look like code, but I designed it with non-programmers in mind (eh, complicated).

    --

    Well, time for bed. I'll give you more tomorrow.

    P.S. I can't tell if "If you haven't noticed - the chapters have been coming along fairly quickly" is you asking us to notice how awesome you are, or if you are warning us that you will crush us with your mighty pen :))
    -Larrik Jaerico

    www.LarrikJ.com
  • RE: Chapter 11 - Game Events and Scripting Languages

    It's funny that you said that game scripts should not look like code - I'm not sure I agree.

    Moving objects through 3d space, starting and stoping animations in a timed fashion, and manipulating game data structures IS a fundamentally programming-like job. The only way to make a game scripting language less code-like is to strip it of its functionality. This lesson was learned and re-learned on Ultima in every version.
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • RE: Chapter 11 - Game Events and Scripting Languages

    I know - they were renumbered mid-stream...
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • RE: Chapter 11 - Game Events and Scripting Languages

    Its an issue I've been debating in my head for a while. I really wish I can find that site, its a good read at the worst. Basically the idea is that scripts should be as high level as possible. Instead of trying to move one player to another by implementing some crazy path finding routine in the script, that should be handled by the game engine to the point that it isn't too much more complicated than playerA.walk(playerB)

    Of course, you would still need to handle failure states, etc, and this sort of simplicity is more of a goal than a rule.

    I always intended on having the scripts be functions, therefore you can have different levels of scripts, and scripts calling other scripts. So you can reap the benefits of avoiding recompiles in tweaking, but at the same time you can have lesser skilled programmers performing high level logic stuff.

    In the end, I don't have the experience to compare these theories to, so I just wrote this up so you can better understand what I meant.
    -Larrik Jaerico

    www.LarrikJ.com
  • The point it , each subsystem should be responsible for subscribing to and handling game events as they pass through the system.

    it -> is

    This is pretty useful, since virtual every system in your game will need access to the event manager object.

    virtual -> virtually

    ___
    Suggestion for class EventType. The default version of EventType is undefined... useless even. A good security measure would be to make the default constructor of EventType private. This would be generally good practice for all classes of this nature that do not want an undefined default behavior.
    ___

    A good rule of thumb - any listener that cares about messages important to AI characters should be tied to the AI subsystem, not a class representing one character.

    This is great advice! Since I am having trouble finding things to nit-pick in this chapter, I'll be a jerk and say that it would be good to illustrate why this is a good rule of thumb.... because you don't want a giant list of listeners to iterate through AND you want the AI system itself to be responsible for managing which AI's care instead of all those bunch of AI characters to manage whether they care or not... that way, you are not forced to use a list evaluation for the many AI chars and can instead use whatever evaluation the AI manager uses to check things for AI chars... such as N-Tree or whatever.

    The abortEvent method is a simple case of looking in the active queue for the event of a given type an erasing it. Note that this method can erase the first event in the queue of a given type or all events of a given type - depending on the value of the second parameter.

    Can you give an example of why someone would abort an event? I could imagine some people calling that "gold plating"... writing code that will never be or should never be used... unless they are convinced otherwise.

    ___
    About the method of adding a listener... it may be good to note that you should never add "this" inside the constructor of a listener, as the EventListener object is not fully formed. This forces you to either add a listener to an event manager after you instantiate the listener or in some post-coinstruction init function of the listener object.

    TO BE CONTINUED...

    The post was edited 1 time, last by Kain ().

  • Originally posted by Larrik
    Example? I'm thinking of stopping (defusing) a time-bomb maybe...or if an object has scheduled events still when the object is destroyed. I'm sure there's reasons.


    As am I. I just think it would be good to point it out in the chapter.

    So far, the best reason I can think of to Abort a queued event is in the code that culls out duplicate events, such as when there are two actor move events in the queue for the same actor.

    The post was edited 1 time, last by Kain ().

  • { TODO: Perhaps listing a few here would be ok??? }

    Would listing some anti-samples be helpful as well? Things that are not good candidates for events? Maybe that would be too much hand-holding. Just a thought.

    ___
    The scripting half of this chapter was very code-light, unlike the events portion of this chapter. How do you bridge the functionality of the scripting languages to your C++ code? How do script conditions get read by the C++ code? How do script actions get executed by the code? I think this chapter needs more cowbell. I mean... more details on bridging the scripting languages to the code. Perhaps you might come at this detail from a view of "what do all scripts have in common?" and from there you implement the things that your script would hook into so that the reader would have a feel for the code neccesary to allow non-coders to script events using the hooks you provided. I don't know if this is possible, but perhaps you could present a generic script reading framework? Some scripting examples I think of would be Ion Storm's somewhat robust system that allowed designers to specify conditions and actions in a trigger script down to the simple "have code manage this conditionless script and fire off this list of actions in order". You presented a lot of general information on the various scripting languages available and why scripting is important, but you don't quite show the user how to incorporate scripting into their game. If time permits, one thing you could do towards this is to show from start to finish how to allow a designer to do a super simple task like play a sound in 2D when a certain condition happens (collision event?)
  • RE: Chapter 11 - Game Events and Scripting Languages

    " // note: m_ident is stored as a void* not an int, so that in
    // the debugger it will show up as hex-values instead of
    // integer values. This is a bit more representative of what
    // we're doing here and makes it easy to allow external code
    // to assign event types as desired.
    "

    MY debugger lets you show any int in hex (VC++ 6, just right-click), and I don't think that this is a very good reason for this. Is there any other benefit?

    --

    I've been thinking about using hashes for a lot of things in my script project, so its very refreshing and helpful to see you handle similar issues with the same solution.

    --

    Operator overloading! How fancy and elegant! I love it!

    --

    "#define DO1(buf,i) {s1 += tolower(buf); s2 += s1;}
    #define DO2(buf,i) DO1(buf,i); DO1(buf,i+1);
    #define DO4(buf,i) DO2(buf,i); DO2(buf,i+2);
    #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4);
    #define DO16(buf) DO8(buf,0); DO8(buf,8);"


    ""#pragma warning(push)
    #pragma warning(disable : 4312)

    return reinterpret_cast<void *>( (s2 << 16) | s1 );

    #pragma warning(pop)
    #undef DO1
    #undef DO2
    #undef DO4
    #undef DO8
    #undef DO16"

    I think this all deserves explanation, at least in good comments.

    --

    "Anytime you compare strings in your game, don't make it case sensitive. Its simply too easy for a bleary eyed programmer to forget to capitalize in the right place, or remember to always type strings in lower case. In systems like this one, the string is turned into a hash, which is essentially impossible to match back to your original string - and therefore tough to debug if something is misstyped. You can head these problems off easilly by just deciding ahead of time that strings are always converted to lower case. The pain and suffering you save may be your own. }"

    Are you suggesting to convert strings to lower-case on input AND use case-insensitive comparisons? One or the other should be sufficient (in theory), I would think, and would save you some execution time (more so with the comparison).

    --

    " explicit Event( char const * const inEventTypeName,
    float inTime = 0.f,
    IEventDataPtr inData = IEventDataPtr((IEventData*)NULL) )
    : m_type( inEventTypeName ),
    m_time( inTime ),
    m_userData( inData )
    {}
    "

    I'm not familiar with using this notation outside of a derived class. You can just put a member data's name in and do that? That's really awesome (and insanely confusing). I am especially surprised it lets you do that before you actually declare them.

    And perhaps some justification is in order for the "explicit" keyword, if it hasn't already been justified in the book (I don't remember off-hand)

    --

    By the time I get to the heading called "The Event Listener," I find myself wondering multiple events of the same type are handled, and how much of a hashing function will be used. I had expected, to this point, that you would actually use a hash table, but now as I reflect and look back, it seems more like you are using the hashes only as identifiers. This is a bit surprising, and despite the fact that you pretty much moved into this, I as the reader felt a little swept away. It COULD be because I started reading this last night and am also coding in PHP between paragraphs, though.

    In that vein...I will get back to this later.
    -Larrik Jaerico

    www.LarrikJ.com
  • RE: Chapter 11 - Game Events and Scripting Languages

    " // compute the path to our current exe and use it as the
    // basis for the log file ...

    char fullPathName[MAX_PATH];
    memset( fullPathName, 0, sizeof(fullPathName) );
    GetModuleFileNameA( NULL, fullPathName, MAX_PATH );"

    I thought that simply not putting in a path would drop any new file into the current working directory, which is pretty reliable the executable's path?

    Also, is it necessary to use memset() here? Seems unnecessary, and potentially expensive.

    --

    "You now know how to create an event and how to write a class that listens for events "

    I do? From looking at code? That's a lot of faith in the reader...
    -Larrik Jaerico

    www.LarrikJ.com
  • RE: Chapter 11 - Game Events and Scripting Languages

    "" addListener - registers that a listener that cares about an event type"

    Hehe, that's jibberish. I assume you mean "registers a listener that cares about an event type".

    --

    I'm on page 15 in the document...I see "STL" a whole lot here, which worries me, but I assume you want brevity of code.

    Something's been bugging me that I haven't seen addressed so far in the reading. Why are you defining an interface for something when there will, by definition, only be a single instance of it? Is this to save on recompiles?

    --

    "Also, if this system were marshalling events across the internet, for a multiplayer game,"

    I happen to know what this means, but this at least needs to go into the glossary, and at most needs a full background (or change the wording). It's not difficult to figure out what you mean, and it is a pretty unimportant part of the book, but I said my piece anyway.

    --

    I like your event system, on the whole. I think it could look a little less like Win32, but it's great ;)

    --

    What is the viability of using something like Perl? From what I've seen, it has boatloads upon boatloads of features (my friend says you can even get it to act like Lisp)...s it just too big, or too slow? Or do you just not have enough Unix junkies proposing it?

    --

    "Gwenno cast the protection spell anyway and managed to protect the wall of wooden boxes instead of the Avatar."

    What's wrong with that? If the player doesn't WANT the spell, give it to the boxes!
    :P
    This bug IS pretty nitpicky, and I thought so when I read it last year, too.

    --

    "If only a small number of lines of code are executed within a large module, interpretation will perform better than compilation. "

    It isn't obvious that you are including the compile time in this statement. It almost sounds as if you are comparing actualy run-time efficiencies, which seems unlikely.

    --

    "A utility that generates C programs to be used in simple lexical analysis of text, or in plain language it matches input streams with a series of regular expressions."

    You call that plain language? Plenty of coders aren't familiar with regular expressions, it seems to be more of a scripter thing.

    --

    Maybe you should point out that learning to use Lex and Yacc isn't trivial (and it's sure as hell intimidating, which isn't uncommon in Unix-origin programs).

    --

    Ah, you help out with the regular expressions, awesome.

    --

    "Go take a look at Mastering Regular Expressions by Jeffrey E. F. Freidl (published by OReilly & Associates, Inc.)."

    I bet Paraglyph loves you mentioning O'Reilly everywhere.

    --

    The Lex and Yacc stuff seems the same as before, so far. I think this is pretty cryptic, and nearly useless to people who haven't read it already, but I can't imagine it being any better with such a small space, and hopefully it will get those readers to research on their own.

    --

    "If you wanted a scripting language that looks a lot like PHP"

    PHP? I think its a poor choice, since PHP is designed to be able to look like any number of languages, depending on who's coding it. Pick a stricter language.

    --

    I like ths chapter, a lot better than its 1st edition equivalent. The Grammars are extremely important and extremely hard, and you probably do want somebody awesome doing that. For my script engine, I stumbled into what seems to be an air-tight grammar so far, but I used heavy use of "end" statements (which is sort of like cheating), and borrowed the rest from C. I only learned about real grammars this past semester, actually.
    -Larrik Jaerico

    www.LarrikJ.com
  • RE: Chapter 11 - Game Events and Scripting Languages

    I totally understand what you mean - and to be honest there's no right and wrong answer here - just style.

    Over the years Ultima's scripting languages went from a simple script just as you describe to a complicated language that has every bit of functionality as Lua or Python.

    There were always complaints from designers - either not enough functionality was given or the language was too complicated. A more complicated system can easily support simple scripts, where a simple one will always remain, well, simple. If your game can use a complicated scripting language like Lua or Python - I'd say use it, and you can still write simple scripts.
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • Brilliant! This is critical for queued events - but only if you extended the class to have a "send an event five seconds in the future" or something like that. The current queue mechanism sends the events in the next game loop, so you wouldn't really have a good reason to use it for the reason you described....
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • I was hoping for more time to show how to integrate Lua into the code base, and use it to create game initialization scripts or user interface layout.

    Unfortunately, I ran out of time.

    :(
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • RE: Chapter 11 - Game Events and Scripting Languages

    You can never be too sure when opening files what the current working directory is - and since opening a log file is a rare thing we don't care about performance as much as robustness of the code.
    Mr.Mike
    Author, Programmer, Brewer, Patriot