コンポーネントベースのエンティティシステムでのスクリプト化された「ネイティブ」コンポーネントの処理


11

私は現在、コンポーネントベースのエンティティシステムを実装しようとしています。エンティティは基本的に単なるIDであり、いくつかのコンポーネントを束ねてゲームオブジェクトを形成するヘルパーメソッドです。そのいくつかの目標は次のとおりです。

  1. コンポーネントには状態のみが含まれます(例:位置、ヘルス、弾薬数)=>ロジックは「システム」に入り、これらのコンポーネントとその状態(例:PhysicsSystem、RenderSystemなど)を処理します
  2. 純粋なC#とスクリプト(Lua)の両方でコンポーネントとシステムを実装したいと思います。基本的に、C#ソースを再コンパイルすることなく、完全に新しいコンポーネントとシステムをLuaで直接定義できるようにしたいと考えています。

これを効率的に一貫して処理する方法についてのアイデアを探しているので、たとえば、C#コンポーネントやLuaコンポーネントにアクセスするために別の構文を使用する必要はありません。

私の現在のアプローチは、通常のパブリックプロパティを使用してC#コンポーネントを実装することです。おそらく、エディターにデフォルト値などを伝えるいくつかの属性で装飾されています。次に、C#クラス "ScriptComponent"を作成します。これは、Luaテーブルを内部でラップするだけで、このテーブルはスクリプトによって作成され、特定のコンポーネントタイプのすべての状態を保持します。コンパイル時にはわからないので、C#側からはその状態にあまりアクセスしたくないのですが、どのScriptComponentsにどのプロパティを使用できるかはわかりません。それでも、エディターはそれにアクセスする必要がありますが、次のような単純なインターフェースで十分です。

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

これは単にLuaテーブルにアクセスし、Luaからこれらのプロパティを設定および取得するだけで、純粋なC#コンポーネントにも簡単に含めることができます(ただし、通常のC#プロパティをリフレクションなどで使用します)。これはエディターでのみ使用され、通常のゲームコードでは使用されないため、ここではパフォーマンスはそれほど重要ではありません。特定のコンポーネントタイプが実際に提供するプロパティを文書化したコンポーネントの説明を生成または手書きする必要がありますが、それは大きな問題ではなく、十分に自動化できます。

しかし、Lua側からコンポーネントにアクセスする方法は?たとえばgetComponentsFromEntity(ENTITY_ID)、「ScriptComponent」を含むネイティブのC#コンポーネントの束をユーザーデータとして取得することになるでしょう。ラップされたLuaテーブルから値GetProperty<T>(..)にアクセスすると、他のC#コンポーネントのようにプロパティに直接アクセスする代わりに、メソッドが呼び出されます。

おそらくgetComponentsFromEntity()、Luaからのみ呼び出される特別なメソッドを記述します。これは、代わりにラップされたテーブルを返す「ScriptComponent」を除いて、すべてのネイティブC#コンポーネントをユーザーデータとして返します。しかし、他のコンポーネント関連のメソッドがあるので、C#コードまたはLuaスクリプトから呼び出されるために、これらのメソッドをすべて複製したくありません。

最終的な目標は、ネイティブコンポーネントとLuaコンポーネントを区別する特別なケース構文なしで、特にLua側から、すべてのタイプのコンポーネントを同じように処理することです。たとえば、次のようなLuaスクリプトを記述できるようにしたいと思います。

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

nativeComponent.NativeProperty = 5
scriptComponent.ScriptedProperty = 3

スクリプトは、実際に取得したコンポーネントの種類を気にする必要はありません。C#側から使用するのと同じメソッドを使用して、コンポーネントを取得、追加、または削除します。

たぶん、そのようなエンティティシステムとスクリプトを統合するいくつかのサンプル実装がありますか?


1
Luaの使用にこだわっていますか?実行時に解析される他のCLR言語(私の場合はBoo)を使用して、C#アプリケーションのスクリプトを作成しました。マネージド環境で既に高レベルのコードを実行していて、すべてが内部でILであるため、「スクリプト」側から既存のクラスをサブクラス化し、それらのクラスのインスタンスをホストアプリケーションに戻すのは簡単です。私の場合は、ロード速度を上げるためにスクリプトのコンパイル済みアセンブリをキャッシュし、スクリプトが変更されたときにそれを再構築します。
ジャスティニア

いいえ、Luaを使用しているのではありません。個人的には、そのシンプルさ、小さいサイズ、スピード、人気のために、それを使いたいと思っています(そのため、人々が既にそれに慣れている可能性が高いようです)。
マリオ

C#とスクリプト環境の間で同等の定義機能が必要な理由を尋ねてもいいですか?通常の設計とは、C#のコンポーネント定義であり、スクリプト言語の「オブジェクトアセンブリ」です。これが解決策であるという問題が発生したのではないでしょうか。
ジェームズ

1
目標は、コンポーネントシステムをC#ソースコードの外部から拡張可能にすることです。XMLまたはスクリプトファイルでプロパティ値を設定するだけでなく、まったく新しいプロパティを備えたまったく新しいコンポーネントを定義し、それらを処理する新しいシステムによって。これは、Dungeon Siegeのオブジェクトシステムに関するScott Bilasの説明に大きく影響を受けています。DungeonSiegeには、「ネイティブ」C ++コンポーネントと、それ自体がスクリプトのラッパーである「GoSkritComponent」が含まれていました:scottbilas.com/games/dungeon
マリオ

スクリプトまたはプラグインのインターフェースを複雑にして、元のツール(この場合はC#またはVisual Studio)の書き換えに近づくほどの罠に陥らないように注意してください。
3Dave 2012

回答:


6

FxIIIの答えの帰結は、統合ロジックが実際にModをプロビジョニングできるようにするために、スクリプト言語ですべて(Modd可能なもの)を最初に(または少なくともそのかなりの部分)設計する必要があるということです。スクリプト統合が多用途であることが確かな場合は、C#で必要なビットを書き換えます。

私はC#4.0のdynamic機能を個人的に嫌っています(私は静的言語のジャンキーです)。あなたのC#コンポーネントは単に強力な/エクスパンド行動オブジェクトです

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.

Luaコンポーネントは完全に動的です(上記とまったく同じ記事で、これを実装する方法のアイデアが得られるはずです)。例えば:

// 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.
}

今、あなたが使用しているためdynamic、彼らはC#での実際のクラスであるかのようにあなたはLuaのオブジェクトを使用することができます。

dynamic cSharpVillian = new Villian();
dynamic luaVillian = new LuaObject(_script["teh", "villian"]);
cSharpVillian.Sound();
luaVillian.Sound(); // This will call into the Lua function.

私は最初の段落に完全に同意します、私はしばしばこのように私の仕事をします。下位レベルの言語で再実装する必要があるかもしれないことがわかっているコードを書くことは、利子を支払う必要があることを知っているローンを取るようなものです。IT設計者は多くの場合、融資を受けます。これは、融資を行ったことがわかっていて、借金を自分で管理できる場合に適しています。
FxIII 2012年

2

私はむしろ反対のことをしたいと思います:動的言語を使用してすべてを記述し、ボトルネック(ある場合)を追跡します。いったん見つけたら、C#または(むしろ)C / C ++を使用して、関連する機能を選択的に再実装できます。

これは主に、システムの柔軟性をC#の部分に限定しようとしているためです。システムが複雑になると、C#の「エンジン」は動的インタープリターのように見え始めますが、おそらく最良のものではありません。問題は、あなたの時間のほとんどを、一般的なC#エンジンの作成に費やしたり、ゲームを拡張したりすることです。

私が信じているように、ターゲットが2番目のターゲットである場合は、おそらく、ゲームメカニクスに集中して時間を使い、経験豊富なインタープリターに動的コードを実行させることができます。


ええ、スクリプトを使用してすべてを実行すると、コアシステムの一部のパフォーマンスが低下するという漠然とした恐れがあります。その場合、その機能をC#(またはその他)に戻す必要があります...とにかく、C#側からコンポーネントシステムとインターフェイスするための良い方法が必要です。それがここでの中心的な問題です。C#とスクリプトの両方から同じように使用できるコンポーネントシステムを作成します。
マリオ

C#は、C ++やCなどの「加速されたスクリプトのような」環境の一種として使用されていると思いましたか?とにかく、最終的にどの言語を使用するかわからない場合は、LuaとC#でロジックを記述してみてください。結局のところ、高度なエディターツールとデバッグ機能のために、ほとんどの場合C#で高速になるでしょう。
Imi

4
+1私は別の理由でこれに同意します。ただし、ゲームを拡張可能にしたい場合は、独自のドッグフードを食べる必要があります。スクリプトエンジンの統合に着手して、潜在的なモッディングコミュニティが実際には何もできないことを発見する可能性がありますそれ。
ジョナサンディキンソン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.