Scripting for Games

by Cameron Albert 26. August 2008 01:02

Scripting in games is not a new concept, most games do this on some level. Scripting allows game objects to execute code that is not compiled as part of the object. In the realm of roe playing games scripting gives objects the ability to react to the world around them. For instance, an object could execute script whenever a player gets near it, allowing a vender to hawk their wares, creatures to attack, etc. There are a variety of scripting languages currently being implement in games such as Lua and Python. Recently I have been researching these two for possible addition to Perenthia to provide me the ability to add custom behavior to objects and NPCs.

Edit: Thanks to some helpful insight and information from Michael Foord I was able to get an IronPython sample working. The sample adds scripting support to an object; the code is very basic but accomplishes the task. Thanks Michael!

Here is a snippet of a console app using the latest IronPython Beta4:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Scripting;

using IronPython;
using IronPython.Hosting;
using IronPython.Runtime;
using IronPython.Runtime.Exceptions;

using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;

namespace IronPythonGameScripting
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Actor player = new Actor("Cam");
                Actor mob = new Actor("Mob");
                mob.Script = @"class Mob:
    def onEncounter(self, *value):
        value[0].Name = ""Mr Groovy""";

                Console.WriteLine("Player name was '{0}'", player.Name);

                mob.Execute(Actor.EVENT_ON_ENCOUNTER, player);

                Console.WriteLine("Player name is now '{0}'", player.Name);

                mob.Execute("Test", player);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                Console.WriteLine("Press any key to exit...");
                Console.ReadKey();
            }
        }
    }

    public static class Engine
    {
        private static ScriptEngine _engine;
        private static ScriptScope _scope;

        static Engine()
        {
            _engine = PythonEngine.CurrentEngine;
            _scope = _engine.CreateScope();
        }

        public static void Initialize()
        {
        }

        public static void Execute(string script)
        {
            ScriptSource source = _engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);
            source.Execute(_scope);
        }

        public static void Execute(string script, string className, string methodName, params object[] args)
        {
            ObjectOperations ops = _engine.Operations;
            ScriptSource source = _engine.CreateScriptSourceFromString(script, SourceCodeKind.Statements);
            source.Execute(_scope);

            if (_scope.ContainsVariable(className))
            {
                object @class = _scope.GetVariable(className);
                object instance = ops.Call(@class);
                if (instance != null)
                {
                    if (ops.ContainsMember(instance, methodName))
                    {
                        object method = ops.GetMember(instance, methodName);
                        ops.Call(method, args);
                    }
                }
            }
        }
    }

    public class Actor
    {
        public const string EVENT_ON_ENCOUNTER = "onEncounter";

        public string Name { get; set; }
        public string Script { get; set; }

        public Actor(string name)
        {
            this.Name = name;
        }

        public void Execute(string methodName, params object[] args)
        {
            Engine.Execute(this.Script, "Mob", methodName, args);
        }
    }
}

 

As it turns out I was attempting to use the IronPython libraries compiled against the Silverlight framework rather than the libraries compiled against the normal .NET framework. I was also using an older beta 1 version of the IronPython library which may have attributed to the exceptions I was seeing. Got to make sure you reference the correct assemblies when working in both .NET and Silverlight. Surprised this is the first time I've done this...

I reviewed IronPython because it has Silverlight support with the DLR but it is really designed to be used instead of C# not as a scripting language executed from within C#. With Lua though I may be able to get it working although it still seems like everything was designed to simply execute the game in lua scripts using C# as the core instead of augmenting C# classes.

I really want to just be able to raise events on the mobile objects and have instances handle those events in a custom way. I certainly don't want to compile C# on the fly either as that has its own set of issues. Compiling C# on the fly is like running an exe for each script that is executed and would be way too costly in both execution time to compile and memory consumption.

Also, since both the client and server will need to execute scripts the scripting language needs to work in both Silverlight and ASP.NET.

Perenthia currently handles commands using a Dictionary<int, Command> where the int is command value, from a list of constants, and the Command is a method pointer (delegate). When the game loads up the first time the list of commands is constructed and the methods pointers are set to methods that related to the command. An example would be a chat command. When the command comes in and is validated the method pointer is found using this dictionary and then executed in the context of the connected player. I was thinking of doing the same thing with mobiles and the actions they can take and respond to based on the world around them. It is a bit more complex with mobiles than commands since commands do a specific thing whereas every mobile could execute a different block of code for a given event. I still have some work to do on it but this will probably be what I use in place of scripting. I loose the flexibility of writing quick scripts, changing them on the fly, etc. but gain the ability to have it work within my code framework, rely on only .NET and work in both Silverlight and ASP.NET.

Tags:

Game Development | General | Perenthia PBBG | Silverlight 2 Development | Silverlight Games

Comments

8/26/2008 3:06:04 PM #

Michael Foord

I reviewed IronPython because it has Silverlight support with the DLR but it is really designed to be used instead of C# not as a scripting language executed from within C#.

What brings you to that conclusion? IronPython (and the Dynamic Language Runtime) was designed with embedding in mind from the very beginning. The hosting API is very flexible and can be used from inside Silverlight.

Michael Foord United Kingdom

8/26/2008 4:48:36 PM #

calbert

It works in Silverlight but not on the server side in a library compiled against the normal framework. If you have it working that way I would love to see some samples.

calbert United States

8/26/2008 4:56:26 PM #

calbert

Alright, I just looked at your website, guess I was wrong. It wasn't working for me when I compiled against the normal framework, got all kinds of errors trying to call out to IP rather than building everything in IP an calling back into C#. I will take a look at your book, maybe I can find some answers there.

calbert United States

8/26/2008 5:47:05 PM #

Michael Foord

Cool. Calling into IronPython from C# is relatively easy, with all sorts of things you can do. There is a whole chapter in my book on it (chapter 15). If you visit the IronPython-URLs blog or the IronPython Cookbook you will find all sorts of interesting examples of embedding IronPython in C#.

* http://www.ironpython.info/
* http://ironpython-urls.blogspot.com/

Michael Foord United Kingdom

8/27/2008 12:16:45 AM #

calbert

Thanks Michael, it's probably just a series of ID10T errors. I think I was going about it the wrong way. It would be awesome to use IronPython, relatively easy language to write and understand.

calbert United States

8/27/2008 4:35:07 AM #

pingback

Pingback from geekswithblogs.net

Silverlight Cream for August 26, 2008 -- #356

geekswithblogs.net

Powered by BlogEngine.NET 1.5.0.7
Modified Theme by Mads Kristensen

About the Author

CameronAlbert.com I am Senior Software Development Consultant specializing in Silverlight, WPF and the Microsoft .NET Framework. 

I have released an iPhone game called the Adventures of Puppyman that was built using ExEn and am currently working on a WP7 and iPhone version of Perenthia soon to be released.

View Cameron Albert's profile on LinkedIn
See how we're connected

Follow cameronalbert on Twitter

 

Recommended Books

Silverlight 4 Business Application Development - Beginner's Guide:

http://www.packtpub.com/microsoft-silverlight-4-business-application-development-beginners-guide/book

Microsoft Silverlight 4 Business Application Development: Beginner’s Guide