11
\$\begingroup\$

I'm currently trying to implement a component-based entity system, where an entity is basically just an ID and some helper methods tying a bunch of components together to form a game object. Some goals of that include:

  1. Components only contain state (e.g. position, health, ammo count) => logic goes into "systems", which process these components and their state (e.g. PhysicsSystem, RenderSystem, etc.)
  2. I want to implement components and systems both in pure C# and through scripting (Lua). Basically I want to be able to define completely new components and systems directly in Lua without having to recompile my C# source.

Now I'm looking for ideas on how to handle this efficiently und consistently, so I don't need to use different syntax to access C# components or Lua components, for example.

My current approach would be to implement C# components using regular, public properties, probably decorated with some attributes telling the editor about default values and stuff. Then I'd have a C# class "ScriptComponent", which just wraps a Lua table internally with this table being created by a script and holding all the state of that particular component type. I don't really want to access that state much from the C# side, as I wouldn't know at compile time, what ScriptComponents with what properties would be available to me. Still, the editor will need to access that, but a simple interface like the following should suffice:

public ScriptComponent : IComponent
{
    public T GetProperty<T>(string propertyName) {..}
    public void SetProperty<T>(string propertyName, T value) {..}
}

This would simply access the Lua table and set and retrieve those properties from Lua and it could easily be included in the pure C# components as well (but use the regular C# properties then, through reflection or something). This would only be used in the editor, not by regular game code, so performance isn't as important here. It would make it necessary to generate or handwrite some component descriptions documenting what properties a certain component type actually offers, but that wouldn't be a huge issue and could be sufficiently automated.

However, how to access components from the Lua side? If I make a call to something like getComponentsFromEntity(ENTITY_ID) I'll probably just get a bunch of native C# components, including "ScriptComponent", as userdata. Accessing the values from the wrapped Lua table would result in me calling the GetProperty<T>(..) method instead of accessing the properties directly, like with the other C# components.

Maybe write a special getComponentsFromEntity() method only to be called from Lua, which returns all the native C# components as userdata, except for "ScriptComponent", where it'll return the wrapped table instead. But there'll be other component-related methods and I don't really want to duplicate all these methods both for being called from C# code or from Lua script.

The ultimate goal would be to handle all types of components the same, with no special case syntax differentiating between native components and Lua components - especially from the Lua side. E.g. I'd like to be able to write a Lua script like that:

entity = getEntity(1);
nativeComponent = getComponent(entity, "SomeNativeComponent")
scriptComponent = getComponent(entity, "SomeScriptComponent")

nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3

The script shouldn't care what kind of component it actually got and I'd like to use the same methods I'd use from the C# side to retrieve, add or remove components.

Maybe there are some sample implementations of integrating scripting with entity systems like that?

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Are you stuck on using Lua? I've done similar things scripting a C# application with other CLR languages being parsed at runtime (Boo, in my case). Because you're already running high-level code in a managed environment and it's all IL under the hood, it's easy to subclass existing classes from the "scripting" side and pass instances of those classes back to the host application. In my case, I would even cache a compiled assembly of scripts for increased loading speed, and just rebuild it when the scripts changed. \$\endgroup\$
    – justinian
    Commented Feb 12, 2012 at 21:11
  • \$\begingroup\$ No, I'm not stuck using Lua. Personally I'd very much like to use it because of its simplicity, small size, speed and popularity (so chances of people already being familiar with it seems higher), but I'm open to alternatives. \$\endgroup\$
    – Mario
    Commented Feb 12, 2012 at 22:13
  • \$\begingroup\$ Might I ask why you desire to have equal definition capabilities between C# and your script environment? Normal design is component definition in C# and then 'object assembly' in the scripting language. I am wondering what issue has arisen that this is the solution for? \$\endgroup\$
    – James
    Commented Feb 13, 2012 at 8:10
  • 1
    \$\begingroup\$ The goal is to make the component system extensible from outside the C# source code. Not just through setting property values in XML or script files, but by defining whole new components with whole new properties and new systems processing them. This is largely inspired by Scott Bilas' descriptions of the object system in Dungeon Siege, where they had "native" C++ components as well as a "GoSkritComponent", which is itself just a wrapper for a script: scottbilas.com/games/dungeon-siege \$\endgroup\$
    – Mario
    Commented Feb 13, 2012 at 15:31
  • \$\begingroup\$ Beware of falling into the trap of building a scripting or plugin interface so complex that it approaches a rewrite of the original tool (C# or Visual Studio in this case). \$\endgroup\$
    – 3Dave
    Commented Jul 29, 2012 at 17:34

2 Answers 2

6
\$\begingroup\$

A corollary of FxIII's answer is that you should design everything (that would be moddable) in the scripting language first (or at least a very decent portion of it) to ensure that your integration logic actually provisions for modding. When you are certain that your scripting integration is versatile enough rewrite the required bits in C#.

I personally hate the dynamic feature in C# 4.0 (I am a static language junkie) - but this is without doubt a scenario where it should be used and is where it really shines. Your C# components would be simply be strong/expando behaviorial objects.

public class Villian : ExpandoComponent
{
    public void Sound() { Console.WriteLine("Cackle!"); }
}

//...

dynamic villian = new Villian();
villian.Position = new Vector2(10, 10); // Add a property.
villian.Sound(); // Use a method that was defined in a strong context.

Your Lua components would be completely dynamic (the very same article above should give you an idea of how to implement this). For example:

// LuaSharp is my weapon of choice.
public class LuaObject : DynamicObject
{
    private LuaTable _table;

    public LuaObject(LuaTable table)
    {
        _table = table;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        var methodName = binder.Name;
        var function = (LuaFunction)_table[methodName];
        if (function == null)
        {
            return base.TryInvokeMember(binder, args, out result);
        }
        else
        {
            var resultArray = function.Call(args);
            if (resultArray == null)
                result = null;
            else if (resultArray.Length == 1)
                result = resultArray[0];
            else
                result = resultArray;
            return true;
        }
    }

    // Other methods for properties etc.
}

Now because you are using dynamic you can use Lua objects as though they were real classes in C#.

dynamic cSharpVillian = new Villian();
dynamic luaVillian = new LuaObject(_script["teh", "villian"]);
cSharpVillian.Sound();
luaVillian.Sound(); // This will call into the Lua function.
\$\endgroup\$
1
  • \$\begingroup\$ I totally agree with the first paragraph, I often do my job in this way. Writing code you know you may have to reimplement it in lower level language, is like to take a loan KNOWING that you will have to pay for interests. IT designers often take loans: this is good when they know that they did it and they keep their debts under control. \$\endgroup\$
    – FxIII
    Commented Mar 30, 2012 at 11:08
2
\$\begingroup\$

I'd rather do the opposite: write all using a dynamic language and then track down the bottleneks (if any). Once you find one you may selectively reimplement the functionalities involved using a C# or (rather) C/C++.

I tell you this mainly because what are trying to do is to confinate the flexibilities of your system in the C# part; as the system becomes complex your C# "engine" will start to look like a dynamic interpreter, probably not the best one. The question is: are you willing to invest the most of your time into writing a generic C# engine or to extend your game?

If your target is the second one, as I belive, you will probably use your time best focusing on you game mechanics, letting a seasoned interpreter to run the dynamic code.

\$\endgroup\$
3
  • \$\begingroup\$ Yeah, there's still this vague fear of bad performance for some of the core systems, if I'll do everything through scripting. In that case I'd have to move that functionality back to C# (or whatever)... so I'd need a good way to interface with the component system from the C# side, anyway. That's the core problem here: creating a component system I can use equally fine from both C# and script. \$\endgroup\$
    – Mario
    Commented Feb 13, 2012 at 15:42
  • \$\begingroup\$ I thought that C# is used as some kind of "accelerated scripting-like" environment for stuff like C++ or C? Anyway, if you are not sure in which language you'll end up, just start to try to write some logic in Lua and C#. I bet in the end you will end up being faster in C# most of the time, because of advanced editor tooling and debugging features. \$\endgroup\$
    – Imi
    Commented Feb 18, 2012 at 17:22
  • 4
    \$\begingroup\$ +1 I agree with this for another reason though - if you want your game to be extensible you should be eating your own dog food: you might land up integrating a script engine only to find your potential modding community can't actually make anything with it. \$\endgroup\$ Commented Mar 14, 2012 at 8:57

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .