Suitable scripting language to control many game objects

Started by
9 comments, last by GregDude 19 years, 4 months ago
A couple of years ago I tried writing a 2D top-down tile-based game where every game object was scripted with a simple but effective scripting language. In the editor, you could pick a 32x32 icon to represent a game object, pick which of the three layers (ground layer, main layer and roof layer) it would be placed on and then assign a script to it that you had written (the same script could be used for multiple objects). There were no pre-written objects built into it, everything had to be scripted.

Only one object could be on a tile at a time and they couldn't sit between tiles. Each game step (about 3 every second), all the scripts would be executed, then all the objects which wanted to move would simulateously move by sliding from tile to tile in the animation step that followed. Some script commands would block the script execution whilst the action was carried out. For instance, if the object called the "move left" command, execution of that object script would pause for that game step, the object would slide left in the animation stage and the next game step would start after this.

The point of the project was to make every single object scripted in an easy to use language so that every game room could be packed with unique game objects that would be fun to play about with. For example, gambling machines, using buttons to control a robot, having a dog follow you when you feed it etc.

A basic enemy who will stagger around randomly and die when shot was scripted like this:

# script starts here when the game starts
created:
move any_free # move in any non-blocked direction 
wait 0..3     # wait randomly between 0 and 3 turns
goto created  # jump back to the start of the script

# script jumps here when sent a shot message
# from a bullet when shot by the player
shot:         
play scream   # play the screaming sound effect
shake         # make the tile shake for one turn
give score 5  # give the player 5 points
die           # destroy this object
It all worked pretty good until the inevitable stage where writing my own scripting language became too complex when I wanted to add features like multiple variable types, functions and parameter passing.

Does anybody know of a suitable scripting language which could be adapted for the role I have described above? The main features that are important are 1) every game object will be scripted and there could be many object on the screen at one time 2) scripts can be paused and resumed when certain commands are called 3) the scripting language can be adapted to have very simple syntax. The game was to be split up into many smallish rooms, where only the objects in the current room would be alive. Each object has to store it's current script position and variable values though, and there could be potentially hundreds or even thousands of objects in the game world so each object needs to have a very small memory footprint.

Does anyone have any recommendations to which scripting langage would best suited for this role?
Advertisement
*cough* Sounds like we have a ZZT fan! *cough*

I looked at Lua when I was considering writing a modern version of ZZT, and it had thread support that was just excellent, and you can easily bind C functions into the language.

You'd basically run the script, and for every function call that was an action you could call a "stop executing the script right now" function and it would drop back to where you started the script execution. Saving the state, so that next turn it would pick up right where it left off.

I never did it, though. :/ Ah, well. I can never motivate myself to write games.
Quote:Original post by C-Junkie
*cough* Sounds like we have a ZZT fan! *cough*


Hehe. :)

Quote:
I never did it, though. :/ Ah, well. I can never motivate myself to write games.


I actually got pretty far when I looked back at what I did. I used 32x32 tiles so I could just use free Windows icons for the graphics so I could concentrate on writing scripts. With just some basic moving commands, simple message passing and some generic animations for each tile (e.g. shaking, shrinking, fading), you could make some pretty fun rooms without much effort. It started getting messy though, because I needed commands for things like absolute/relative room coordinates and initalising object scripts with parameters (like setting the speed of a generic enemy). On one hand you want to stick to simple programming constructs to keep the scripts easy to write, but then there's this nagging feeling that it won't be powerful enough later.



Thanks for the suggestion. Any idea how much overhead each Lua script/thread would have?
Quote:Original post by seanw
Thanks for the suggestion. Any idea how much overhead each Lua script/thread would have?
I never got that far :).

My original thread
Quote:Original post by C-Junkie
I never got that far :).

My original thread


Thanks. I'm starting to think that something like Java/C# would be ideal for this but I'm a little nervous at the prospect of having, say, 1000 objects in seperate threads. It would make implementing a game like this really easy though. The syntax for Java/C# might be a little heavy and verbose for this task however. I suppose you could always define a customised language which is very similar (like removing semi-colons, optional function brackets), but is converted into Java/C# plain text code before it is compiled.
Java or C# are way too heavy for a scripting language IMHO.

Go with languages meant to be used as such: Python, Ruby, Lua, heck, even &#106avascript. (personal opinion: Ruby. I want to see more acceptance of this amazing language)
Ruby does look nice. It also looks a little OTT, and I don't understand the sand box and security issues, so I don't fully trust it.

Add gamemonkey to the list.
Well, I think the REAL question is: do you really need variables, arguments and so on in your scripting language?
For me, a scripting language must be very simple and can be used by a non-programmer.
I'm working on a demo of a 'point and click' game. Here is an example of a script (to open a door when the player use it):

Use
{
SetCollidable 0;
SetPickable 0;

RotateAxis "y" -90.0 1.2;

Return;
}

Another example (this is the script for a coin - it must be used with a JukeBox):

// Player take the coin: put it in inventory
Use
{
PutInInventory;
Return;
}

// Player look at the coin: some description
Look
{
Say "Woaaah! Some money!";
Return;
}

// Player use the coin with the juke box: remove it from game
JukeBox
{
PlaySFX "Action/use_coin.wav" 0;
RemoveFromGame;
Return;
}

As you see the scripting language is very simple but enough powerful (without variables, ...). It is very simple to parse and 'compile' such script files (it took 2 days to make the whole thing running).
Keep your scripting language as simple as you can. If you can't make something by scripting, you must do it in *real* language (C/C++, ...).

That was my 2 cents.

Quote:Original post by sumydi
Well, I think the REAL question is: do you really need variables, arguments and so on in your scripting language?
For me, a scripting language must be very simple and can be used by a non-programmer.


The thing is, if you don't include them, you'll never really know if your going to need them later. It took me a long time to write an ad-hoc compiler and byte-code interpreter myself, and adding advanced features does take a long time. You could still keep your language simple, but it's nice to have the advanced features there just in case you need them.

Quote:
As you see the scripting language is very simple but enough powerful (without variables, ...). It is very simple to parse and 'compile' such script files (it took 2 days to make the whole thing running).
Keep your scripting language as simple as you can. If you can't make something by scripting, you must do it in *real* language (C/C++, ...).


Thanks for the examples, it seems quite similar to what I was originally doing. It took me months to get my scripting language working and it was still pretty basic though. I had a few commands for handling variables and I had to start adding lots of little commands to do very specific things because I couldn't manipulate parameters properly. For example, getting a friendly dog to run after the player faster as the player goes further away requires some basic maths and it's messy when you are using assembly style instructions (i.e. store result in register, divide register, compare register).



Out of curiosity, I tried getting some basic scripting working in Java today and I'm seriously impressed by how much of my game I've managaged to replicate. In about 4 hours, I've got a grid-based map with sprites for the objects on them, the objects can move about, send messages to each other and I can already make them do quite cool dynamic things. For example, I've got an enemy spawner which spits out enemies every few seconds, the enemies walk about randomly, a player who is moved with the keyboard, can shoot bullets, the bullets can kill the enemies etc. This stage honestly took me months to get to when I tried making this game a couple of years ago in C++.



The Java sytax isn't ideal in that it isn't as simple as it can be, but the productivity increase I get by using it is insane. I've got all the advanced features of Java like inheritence and anonymous functions that could be used for advanced scripting. I've also got the in-built security mechasims and dynamic loading so I could do things like load new game objects on the fly from foreign sources and know they aren't going to wipe my hard-drive. Also, features like serialisation means I don't have to write my own custom loading/saving; it just works. All the level editor would have to do is bind a Java class file to each object on the map to script them.



Just to give an example, here's how a simple enemy is scripted:
class Enemy extends GameObject{  int health;  Enemy()  {    health = 3;  }  // Called by the game when the object is created  void created()  {    wander();   }  void wander()  {    while (true)    {      // Wait between 1 and 3 turns      pause(between(1, 3));      // Move in a random direction      moveRandomly();    }  }      // When sent a touched message (when something moves into the object)  void touched(GameObject sender)  {    // Bite the player if it was him who touched the enemy    if (sender instanceof Player)    {      playsound("bite");      ((Player)sender).damage(5);    }    // Wander again    wander();  }  // Sent when a bullet collides  void shot(GameObject sender)  {    // Scream and die if no more health left    if (--health <= 0)    {      playSound("scream");      die();    }    else    {      wander();    }  }}

There's more curly and rounded brackets than I would like, but the script is still very straight-forward to read. Note that when a message is send to the object, the current point of execution is stopped for good (the thread is killed) and a new thread starts running the function for the message sent. Multi-thread behaviour on a single object level is too complex for scripting I think, and this way makes it easy to write the kind of objects the game needs.



To make it simpler for modders though, you could wrap up common functionality in library functions (like wander and allowing pause(1, 3)). I suppose you could modify the Java code slighty by, for example, removing the curly brackets (using Python-style tabbing instead), removing the semi-colons (\n ends the current statement) etc. and then substituting these back in before the Java compiler sees it.
You could easily use Lua and use one coroutine per object. Each loop, you call coroutine.resume on each object's coroutine, which does a little work and then yields again.

Cooperative multitasking is generally easier and safer than using native threads. Also creating 1000 native threads could cause the OS some stress, whereas creating 1000 LUA coroutines only uses memory.

Every "thing" that you do, could be a routine which calls "yield"

More sensibly, rather than just yielding for one tick, you could setup some sort of wait lists so that the resume() only gets called when whatever the object is waiting for happens. That way you save time resuming threads which don't do anything much.

Mark

This topic is closed to new replies.

Advertisement