Posted on October 29, 2009 by Doolwind

Why You Should Use C# For Your Scripting Language

I’ve used a lot of scripting languages over the years when developing games.  For my latest engine, I decided I’d use C# as the scripting language.  I’ve been amazed by how well C# works as a scripting language so I thought I’d share my experiences.  The technique I’m about to discuss gives game designers access to a fully-featured language and IDE with compile-time checking all while allowing run-time changes.  I use run-time compilation to achieve the flexibility of a scripting language combined with the power of a .NET language.

Advantages

There are a number of key advantages to using C# as a scripting language in your game engine.  I’ll list these first then describe how I achieve them.

  • Designers use Visual Studio. Most designers are stuck using notepad (or similar) when writing their scripts.  With my technique, designers can use Visual Studio, gaining the full power of the IDE.  Including:
    • Intellisense. Letting them know what functions exist on all of the objects you’ve exposed, as well as documentation you’ve provided in code.
    • Compilation. Designers can compile their scripts while making changes to catch any compile-time problems.
    • Full Debugging Support. Another area lacking in most scripting languages is the debugging support.  Designers have access to all of Visual Studios debugging functionality to help them find and fix issues with their code.
  • Run-Time Changes. Designers still have complete control to change their scripts at run-time.  Scripts can be reloaded automatically when files are changed, or (more likely) a “reset” button can be exposed in the game engine that allows them to reset the level and reload all scripts.
  • Compile scripts into the executable for release. You have the option to use the scripts compiled into the executable if you prefer, protecting your files.
  • Complete Access to game objects, or whatever you want designers to access. Designers are working in the same language you are developing in.  They have full access to the game objects or to a subset you decide on.  I use an interface and expose what I want them to use.
  • FAST Run-time performance. The C# code is compiled not interpreted meaning the run-time performance is as good as the rest of the engine.  The slight performance bottleneck of reflecting the function can be removed by compiling scripts into executable if required.
  • No Binding Required. Binding is automatic between the game engine and the script.  You don’t need to write any plumbing and exposing a new function is as easy as adding it to the engine.

How It Works

The setup is surprisingly easy, designers follow these steps and their up and running:

  1. Add the script file to the Visual Studio project (preferably in its own directory)
  2. Create an Init() function in the script class which will automatically be called by the engine
  3. Create callbacks for events and implement the game logic
  4. Run the game and make changes to scripts in real-time as needed

I use the run-time compilation functionality of the .NET framework to compile and run the code on- the-fly.  This has a small performance hit during compilation however the run-time performance is excellent.

Some Examples

Below are a few examples I put together today to show the power of the system.

The first script below creates an entity at the beginning of the game and then sets up a callback that will fire every 5 seconds.  The TestScript object’s lifetime is until the scripting system is reset allowing stateful object creation.

public class TestScript : BaseScript
{
    int team = 1;
    int xPos = 15;
    int yPos = 2;

    public void Init()
    {
        script.CreateEntity("Healer", team, xPos, yPos);
        script.CallEveryXSeconds(MyCallback, 5);
    }

    public void MyCallback()
    {
        script.CreateEntity("Tank", team, xPos, yPos++);
    }
}

The next script shows how simple customizing a game entity can be.  Whenever an effect is added or removed from a game entity, the following two functions are called allowing designers complete control over gameplay.  Notice the designer is given access only to what they need while still having complete control to change any settings or values of the entity.  An interface could be used to restrict what operations can be performed on entities as required.

class SpeedUpEffect
{
    public void AddEffect(GameEntity entity)
    {
        entity.Speed += 1;
    }

    public void RemoveEffect(GameEntity entity)
    {
        entity.Speed -= 1;
    }
}

Conclusion

As I said, I was amazed both at how simple all of this was to set up (only a few hours) and how powerful the system is.  If anyone is interested I’d be happy to share the source code for how I achieved this.  As I continue development of my engine I will be experimenting more with this scripting system and will release more information as I develop it.