Cameron Albert

Ramblings of software development, games and technology.

Archive for August 2008

Scripting for Games

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.

Written by Cameron

August 26, 2008 at 1:02 am

Silverlight Game Contest

I thought I would take a little break from working on Perenthia and take part in a Silverlight Game Contest for a sports theme game. This will push out the alpha release date a little but I need a little change of pace.

I am a little late in deciding to do this but figured I would give it a try and see if I can come up with something fun by September 8th. I have an idea for a simple game, hopefully I can make it fun to play because I know it will be fun to build.

Once the game is finished I will also post the source code.

Written by Cameron

August 23, 2008 at 12:01 am

Silverlight 2 Farseer Physics Geom Visual Helper Method

I ported some code from the Farseer Physics Engine XNA demos that displayed  a debug view of the geom vertices used with the Physics Simulator. This is useful if you want to see where your geoms are being rendered to the screen to make sure you have everything in the right place. I know I need this a lot, helps me to understand where I am placing body geoms.

Anyway, here is the method, the _debug variable is just a Canvas I added at the top of the UI. Keep in mind that this method will slow your game WAY down so be sure and use it to test where geoms are positioned and if everything is moving properly. I use a static variable for the PhysicsSimulator, hence the cleverly named Physics.Simulator.

        private void DrawVertices()
        {
            _debug.Children.Clear();
            int verticeCount = 0;
            for (int i = 0; i < Physics.Simulator.GeomList.Count; i++)
            {
                verticeCount = Physics.Simulator.GeomList[i].LocalVertices.Count;
                for (int j = 0; j < verticeCount; j++)
                {
                    Line line = new Line();
                    line.Fill = new SolidColorBrush(Colors.Transparent);
                    line.Stroke = new SolidColorBrush(Colors.Magenta);
                    line.StrokeThickness = 1;
                    if (j < verticeCount - 1)
                    {
                        line.X1 = Physics.Simulator.GeomList[i].WorldVertices[j].X;
                        line.Y1 = Physics.Simulator.GeomList[i].WorldVertices[j].Y;
                        line.X2 = Physics.Simulator.GeomList[i].WorldVertices[j + 1].X;
                        line.Y2 = Physics.Simulator.GeomList[i].WorldVertices[j + 1].Y;
                    }
                    else
                    {
                        line.X1 = Physics.Simulator.GeomList[i].WorldVertices[j].X;
                        line.Y1 = Physics.Simulator.GeomList[i].WorldVertices[j].Y;
                        line.X2 = Physics.Simulator.GeomList[i].WorldVertices[0].X;
                        line.Y2 = Physics.Simulator.GeomList[i].WorldVertices[0].Y;
                    }
                    _debug.Children.Add(line);
                }
            }
        }

Written by Cameron

August 21, 2008 at 9:43 pm

More Perenthia Screen Shots

Here are a few more screen shots of the Perenthia UI. The three screen shots are dialog windows that popup as a result of clicking on icons or buttons in UI. They are use a reusable control I created that has the title bar and X button and then just has a ContentControl for the guts of the dialog. The dialog window is draggable and does the hide and show thing. I need to do some cleanup on it and then I will post the source. I will also post the source for the stat bars shown on the skills dialog screen shot.

Also, I am hoping to have some time to blog about some of what I am doing with the overall game engine. I wanted to provide a development update but haven’t moved the development along all that much due to working out some networking issues and trying to clean up some of the code.

The goal for Alpha is to get a working zone complete with quests, monsters, npcs, etc. However, I may do some smaller tests with the alpha registrants before all of this is complete. I was thinking of setting up a tiny zone just for testing where players could login, create a Character and accept a quest, which would be to enter a room where enemies spawn randomly and in various numbers. This would allow me to test a lot of things in one place; Character Creation, Quests, Spawning, Combat and NPC interaction.

Anyway, here are the screen shots; this first one is the Character Sheet that will display your character stats, equipped items and skills. The empty box will show the skills once I bind them to it. 🙂

This next screen is the actual skills screen where you can see you skill rank and all available skills. If you have Skill Points you can increase or learn new skills from this window.

This last image is just the backpack dialog, all I have is a starting candle right now. 🙂

Written by Cameron

August 9, 2008 at 1:36 am