in GameDev, Tutorials

Embedding Tiny Scheme in a Game

I have recently started to develop a game from scratch. This game is about drone races. I will write about it in future posts.

I wanted to have a scripting language in order to be able to:

  1. configure game object properties,
  2. draw the entities,
  3. draw the levels,
  4. etc.

I started implementing my own parser but it rapidly became very complicated. So I’ve then looked for many embeddable scripting languages, such as lua, tcl, squirell and finally scheme.

The last one is very interesting because the Lisp family languages are very well suited to implement Domain Specific Languages (DSL).

This article compares the embeddable scheme implementations and shows how to embed TinyScheme.

The Embeddable Scheme Implementations

After reading Embedding Scheme for a game mission scripting DSL I started looking at the scheme implementations described and playing a little with them. My choices were between:

  • S7
  • TinyScheme
  • Scheme 9

The requirements were :

  1. Small: to be directly integrated in the code
  2. Readable: to be maintained and debugged
  3. Clean: no global variable, thread safe to be used simultaneously, etc.

S7

S7 is primally used in the snd sound editor. Its documentation is OK. But I have had problems to compile it and embed it in the main program.

Scheme 9

Scheme 9 comes with a very well done documentation about its implementation.

Its algorithmic implementation seems to be very well done because I was able to run a recursive factorial code where TinyScheme failed.

But the implementation is crapy, there is a lot of globals that make impossible to run several instances at the same time.

A cleaned version would be very cool to use.

TinyScheme

TinyScheme is the most used one, it is used in Gimp and less desirable software.

It comes with a Makefile, a standalone interpreter and a lot of compilation options. I recommend to check the Makefile, for example for MinGW options.

I highly recommend to use the last version (on their svn).

At the opposite of the others, the documentation is very sparse, there are two text files that explain how it works and how to compile it.

The implementation is clear and does not use global data.

Finally I have chosen TinyScheme because it is ready to use and easy to embed.

Embedding TinyScheme

I have chosen to directly integrate the source instead of using a library.

Integrating in the sources

I have tested two ways, Makefiles and CMake. I used very restrictive compilations flags in the project (-Wall -Werror –std=c99), so the compilation of TinyScheme failed.

Makefile

For the Makefile I simply overloaded the rule for the TinyScheme object file:

CMake

It is more complex in CMake because it is only possible to add flags, not to overload them, so I have added flags that remove the previous ones (-Wno-all -Wno-error):

Running a script from the game

To run a script, it is only necessary to call scheme_load_file, it is also recommended to load init.scm before in order to make available a lot of helpful functions.

Calling a C function from Scheme

To call a C function from scheme, I started by defining the function that will be called, then I registered this function to scheme in order to make it available to the script side.

Define the function to be called

The called function always takes the scheme environment and  a pointer to the args  as parameter and returns a pointer.

Scheme is a dynamically typed language, so I started checking the type of the args passed by using is_number here.

Then I retrieved the value of the number by using rvalue.

The function can return a computed value (real type here) by calling mk_real.

Register this function

Then I registered the function to scheme by passing a pointer to the function and the script side name.

Call from Scheme

Simply call the function as usual.

Full code from github:

Registering multiple functions

To register multiple foreign functions at the same time you have to define a list of foreign functions:

Then register them:

Bonus handle vectors

When the amount of data to pass is huge, it may be useful to use vectors:

It is very simple to get the values from the C side:

We can get the amount of data in the vector by using:

Then retrieve each data:

I don’t understand why we need to: