エンティティコンポーネントシステムベースのエンジン


9

注:これはJavaScriptでプログラミングしていますが、ほとんどの場合、言語に依存しないはずです。

エンジンをECSベースのエンジンに変換することを考えています。

私は基本的なアイデアを得ます(注:これは間違っています、私の答えを参照してください):

エンティティはゲームオブジェクトです。
コンポーネントは、エンティティに「接着」できる機能(reactToInput())または状態(position)のビットです。
システムには、管理および更新するエンティティのリストがあります。

しかし、私は実装といくつかの詳細を取得することを完全に確信していません...

質問:システムはさまざまな種類のエンティティで動作できますか?私は通常Scene、エンジンで呼び出されるクラスの例を示しますが、これもこの目的に役立ちます。シーンは、レンダリング、更新、レンダリング(ライト)に影響を与える可能性のあるすべてのオブジェクトのコンテナであり、将来的には2DSoundEmitterオブジェクトも含まれる可能性があります。それは高レベルのインターフェースを持っているので、ユーザーは自分が使用しているオブジェクトのタイプscene.add()や、そのようなすべてのものについて心配する必要はありません。

私はそれがSceneシステムかもしれないことを理解しています。エンティティを取り込んで格納し、更新メソッドを呼び出して、状態を変更することもできます。しかし、問題があります。上で説明したように、にはさまざまなタイプのオブジェクトをScene供給することができます。たとえば、シーンにレンダリング可能なオブジェクト(「ドローアブル」)ライトの両方が含まれている状況ではどうすればよいですか?相互作用する前にエンティティを型チェックする必要がありますか?または、さらに低いレベルで解決する必要があります。任意のオブジェクトに追加できるコンポーネントを作成すれば、ライトはとコンポーネントを持つエンティティになります。それは受け入れられますか?LightSourceLightSourcePosition

また、従来の継承と従来のクラスを引き続き使用することは良い習慣ですか?たとえば、自分Rendererがどうなるかわからない!これはシステムではありません。唯一の機能は、カメラとシーンを取り込み、すべてをレンダリングして、エフェクト(シャドウなど)を適用することです。また、ゲームのコンテキスト、幅、高さを管理し、翻訳を行います...しかし、それはまだシステムではありません!

編集: ECSで見つけたリソースをリンクできますか?良いものを見つけるのに苦労しています。


2
このページに回答を再投稿するのではなく、このリンクを提供します。gamedev.stackexchange.com / questions / 23533 / エンティティは派生しないでください。エンティティ間の違いはコンポーネントを介して実現する必要があります。通常、主要なシステム(レンダリング、物理、ネットワーク、入力、オーディオなど)ごとにインターフェイスが必要です。レンダラーをセットアップする方法は、レンダリング可能なエンティティーについてシーンをクエリすることです。シーンマネージャーは、レンダーコンポーネントを持つ各エンティティーにレンダー情報を要求します。
ニック・フォスター

1
T = Machineブログのコンポーネントデザイン(良いものを求めたため)
John McDonald

エンティティフレームワークのコードとディスカッション:gamadu.com/artemis
Patrick Hughes

@JohnMcDonald、私はその記事にコメントを書きましたが、節度を待っています。あなたはそれをここで見ることができます:t-machine.org/index.php/2007/12/22/…。やんばねです。
jcora

また、ジョンがT = Machineでリンクした記事@NicFosterは、あなたの答えとは少し違う何かを説明しています...そのデイブにとって、エンティティにはコンポーネントのリストがなく、単なる名前です。「flsjn304」のように-それはエンティティです。「どこかに」格納されます。そして、私は彼が実際に内部の部品を保持した場合に理解するために事を再読み込みしなければならないシステム思われる、非常に私には奇妙な!
jcora

回答:


6

Web / UI JS開発者として理解しようとすることで、私が助けになるかどうかを確認してみましょう。また、言語にとらわれないでください。他の言語で確立された多くのパターンは学ぶ価値がありますが、その柔軟性のためにJSで非常に異なって適用することができます。JSをより古典的なOOP指向の言語と同じ境界のセットを持っていると考えてコードを書く場合、いくつかの機会を吹き飛ばす可能性があります。

まず第一に、「OOPを使用しない」という要素では、JavaScriptオブジェクトは他の言語と比較してplaydoughのようなものであり、JSはクラスではないため、実際にはカスケード継承スキームの悪夢を構築する必要があります。ベースの合成は、はるかに自然に行われます。JSに愚かなクラスまたはプロトタイプの手伝いシステムを実装している場合は、破棄することを検討してください。JSでは、クロージャーとプロトタイプを使用し、キャンディーのように関数を渡します。それは嫌で不潔で間違っているだけでなく、強力で簡潔でもあります。

継承を多用するアプローチは、実際にはデザインパターンのアンチパターンとして記述されています。正当な理由により、15レベル以上のクラスまたはクラスのような構造を歩いて、どこが破壊されたバージョンのメソッドなのかを理解しようとしているためです。から入ってくることができます。

なぜ多くのプログラマーがこれを行うのが好きなのか(特に、何らかの理由でJavaScriptを記述しているJavaの人たち)はわかりませんが、慣れすぎると、ひどく、判読できなくなり、完全にメンテナンスできなくなります。継承はあちこちで大丈夫ですが、JSでは実際には必要ありません。より魅力的なショートカットである言語では、たまたまBunnyRabbitが含まれている継承チェーンを通じてゾンビ実装をフランケンスタイン化するようなよりリテラルなモデリングスキームではなく、より抽象的なアーキテクチャの懸念のために予約する必要があります。これはコードの再利用には適していません。メンテナンスの悪夢です。

JS dev Entity / Component / Systemベースのエンジンが、設計上の懸念を切り離し、非常に細かいレベルで実装するためにオブジェクトを合成するためのシステム/パターンとして私を襲っています。言い換えれば、JavaScriptのような言語での子供の遊び。しかし、最初にこれを正しく処理しているかどうかを確認しましょう。

  • エンティティ-あなたが設計している特定のもの。私たちは固有名詞の方向により多く話します(もちろん、実際にはそうではありません)。「シーン」ではなく「IntroAreaLevelOne」。IntroAreaLevelOneはある種のsceneEntityボックス内にあるかもしれませんが、他の関連するものとは異なる特定の何かに焦点を合わせています。コードでは、エンティティは実際には、有用なものにするために実装または確立する必要がある一連の要素(コンポーネント)に関連付けられた単なる名前(またはID)です。

  • コンポーネント-エンティティが必要とするもののタイプ。これらは一般名詞です。ウォーキングアニメーションのように。WalkingAnimation内では、「シャンブリング」(ゾンビや植物モンスターに適した選択)や「チキンウォーカー」(リバースジョイントのed-209ishロボットタイプに最適)など、より具体的に取得できます。注:それがそのような3Dモデルのレンダリングからどのように分離されるかはわかりません-たぶんくだらない例ですが、私は経験豊富なゲーム開発者というよりはJSプロです。JSでは、マッピングメカニズムをコンポーネントと同じボックスに入れます。コンポーネント自体は、ロジックや、システムがさらに必要な場合に何を実装するかをシステムに指示するロードマップのように軽いものになる可能性があります(ECSでの私の試みでは、一部のコンポーネントは単なるプロパティセットのコレクションです)。コンポーネントが確立されると、

  • システム-実際のプログラムの肉はここにあります。AIシステムが構築され、リンクされ、レンダリングが達成され、アニメーションシーケンスが確立されるなど...私はこれらを主に想像に任せていますが、例ではSystem.AIが一連のプロパティを取り、関数を吐き出しています。実装で最終的に使用されるオブジェクトにイベントハンドラーを追加するために使用されます。System.AIの重要な点は、複数のコンポーネントタイプをカバーすることです。1つのコンポーネントですべてのAI要素を整理することはできますが、そうすることは、物事を細かくすることのポイントを誤解することです。

目標に留意する:設計者以外のユーザーが何らかの種類のGUIインターフェースを簡単にプラグインできるようにして、パラダイム内のコンポーネントを最大化して一致させることにより、さまざまな種類のものを簡単に微調整し、それらから離れたいと考えています。変更または維持するよりも書くのがはるかに簡単な人気のある任意のコード体系。

JSでは、おそらくこのようなものです。ゲーム開発者がひどく間違っているかどうか教えてください:

//I'm going with simple objects of flags over arrays of component names
//easier to read and can provide an opt-out default
//Assume a genre-bending stealth assassin game

//new (function etc... is a lazy way to define a constructor and auto-instantiate
var npcEntities = new (function NpcEntities(){

    //note: {} in JS is an object literal, a simple obj namespace (a dictionary)
    //plain ol' internal var in JS is akin to a private member
    var default={ //most NPCs are humanoids and critters - why repeat things?
        speedAttributes:true,
        maneuverAttributes:true,
        combatAttributes:true,
        walkingAnimation:true,
        runningAnimation:true,
        combatAnimation:true,
        aiOblivious:true,
        aiAggro:true,
        aiWary:true, //"I heard something!"
        aiFearful:true
    };

    //this. exposes as public

    this.zombie={ //zombies are slow, but keep on coming so don't need these
        runningAnimation:false,
        aiFearful:false
    };

    this.laserTurret={ //most defaults are pointless so ignore 'em
        ignoreDefault:true,
        combatAttributes:true,
        maneuverAttrubtes:true, //turning speed only
    };
    //also this.nerd, this.lawyer and on and on...

    //loop runs on instantiation which we're forcing on the spot

    //note: it would be silly to repeat this loop in other entity collections
    //but I'm spelling it out to keep things straight-forward.
    //Probably a good example of a place where one-level inheritance from
    //a more general entity class might make sense with hurting the pattern.
    //In JS, of course, that would be completely unnecessary. I'd just build a
    //constructor factory with a looping function new objects could access via
    //closure.

    for(var x in npcEntities){

        var thisEntity = npcEntities[x];

        if(!thisEntity.ignoreDefaults){

            thisEntity = someObjectXCopyFunction(defaults,thisEntity);
            //copies entity properties over defaults

        }
        else {
            //remove nonComponent property since we loop again later
            delete thisEntity.ignoreDefaults;
        }
    }
})() //end of entity instantiation

var npcComponents = {
    //all components should have public entityMap properties

    //No systems in use here. Just bundles of related attributes
    speedAttributes: new (function SpeedAttributes(){
        var shamblingBiped = {
            walkingAcceleration:1,
            topWalking:3
        },
        averageMan = {
            walkingAcceleration:3,
            runningAcceleration:4,
            topWalking: 4,
            topRunning: 6
        },
        programmer = {
            walkingAcceleration:1,
            runningAcceleration:100,
            topWalking:2
            topRunning:2000
        }; //end local/private vars

        //left is entity names | right is the component subcategory
        this.entityMap={
            zombie:shamblingBiped,
            lawyer:averageMan,
            nerd:programmer,
            gCostanza:programmer //makes a cameo during the fire-in-nursery stage
        }
    })(), //end speedAttributes

    //Now an example of an AI component - maps to function used to set eventHandlers
    //functions which, because JS is awesome we can pass around like candy
    //I'll just use some imaginary systems on this one

    aiFearful: new (function AiFearful(){
        var averageMan = Systems.AI({ //builds and returns eventSetting function
            fearThreshold:70, //%hitpoints remaining
            fleeFrom:'lastAttacker',
            tactic:'avoidIntercept',
            hazardAwareness:'distracted'
        }),
        programmer = Systems.AI({
            fearThreshold:95,
            fleeFrom:'anythingMoving',
            tactic:'beeline',
            hazardAwareness:'pantsCrappingPanic'
        });//end local vars/private members


         this.entityMap={
            lawyer:averageMan,
            nerd:averageMan, //nerds can run like programmers but are less cowardly
            gCostanza:programmer //makes a cameo during the fire-in-nursery stage
        }
    })(),//and more components...

    //Systems.AI is general and would get called for all the AI components.
    //It basically spits out functions used to set events on NPC objects that
    //determine their behavior. You could do it all in one shot but
    //the idea is to keep it granular enough for designers to actually tweak stuff
    //easily without tugging on developer pantlegs constantly.
    //e.g. SuperZombies, zombies, but slightly tougher, faster, smarter
}//end npcComponents

function createNPCConstructor(npcType){

    var components = npcEntities[npcType],

    //objConstructor is returned but components is still accessible via closure.

    objConstructor = function(){
        for(var x in components){
            //object iteration <property> in <object>

            var thisComponent = components[x];

            if(typeof thisComponent === 'function'){
                thisComponent.apply(this);
                //fires function as if it were a property of instance
                //would allow the function to add additional properties and set
                //event handlers via the 'this' keyword
            }
            else {
                objConstructor.prototype[x] = thisComponent;
                //public property accessed via reference to constructor prototype
                //good for low memory footprint among other things
            }
        }
    }
    return objConstructor;
}

var npcBuilders= {}; //empty object literal
for (var x in npcEntities){
    npcConstructors[x] = createNPCConstructor(x);
}

NPCが必要なときはいつでも、 npcBuilders.<npcName>();

GUIはnpcEntitiesとコンポーネントオブジェクトにプラグインでき、デザイナーがコンポーネントを単に組み合わせて一致させることにより、古いエンティティを微調整したり、新しいエンティティを作成したりできます(デフォルト以外のコンポーネント用のメカニズムはありませんが、特別なコンポーネントをその場で追加できます)定義されたコンポーネントがある限り、コード。


6年後のこの状況を見てみると、自分の答えがよくわかりません。これを改善できますか?
エリック・レッペン、

1

Entity Systemsについて、コメントで親切に提供してくれた記事を読みましたが、それでも疑問が残っていたため、別の質問をしました

まず、私の定義が間違っていました。エンティティコンポーネントは単なるデータホルダーですが、システムはすべてを提供します機能をます。

ここで私の質問のほとんどをカバーするのに十分な知識を得たので、それに答えます。

Scene私が話していたクラスは、システムであってはなりません。ただし、すべてのエンティティを保持し、メッセージを容易にし、システムを管理できる中央マネージャーである必要があります。また、エンティティーの一種のファクトリーとして機能することもできるので、そのように使用することにしました。それができエンティティの任意の型を取るが、(彼らだビット単位のない限り、パフォーマンス上の理由から、任意の型チェックを行うべきでない、)それが適切なシステムにそのエンティティを養う必要があります。

ESの実装中にOOPを使用しないでください。 示唆します Adamしますが、データホルダーだけでなく、エンティティとコンポーネントの両方のメソッドを持つオブジェクトを使用しない理由はありません。

Renderer単にシステムとして実装することができます。描画可能なオブジェクトのリストを維持し、それらのレンダリングコンポーネントのdraw()メソッドを16ミリ秒ごとに呼び出します。


1
「エンティティとコンポーネントは単なるデータホルダーですが、システムはすべての機能を提供します」「レンダーコンポーネントのdraw()メソッドを呼び出す」とはまだ混乱しています。また、シーングラフをレンダラーの一部にすることができない理由もわかりません。これは単なる便利なツールであり、「描画可能」コンポーネントをノードとしていつでも実装できます。シーングラフにシーン以上の責任を負わせることは不必要であり、デバッグするのは面倒なことになると私は確信しています。
dreta

@dreta、現在のレンダラー(エンジンの非ES実装)は変換、カメラの変更、アルファ関連を行い、将来的にはさまざまな効果、GUIおよびシャドウを描画します。グループ化するのは自然なことのように思われました。シーンは格納エンティティの作成に責任を持つべきではありませんか?または、何か他のものを保存する必要がありますか?作成の部分は、おそらくユーザーが提供したコンポーネントを数行に集約したものであり、実際には何も「作成」するのではなく、単にインスタンス化するだけです。
jcora

すべてのオブジェクトがレンダリング可能であるわけではありません。すべてのオブジェクトが衝突したり、音を出したりするわけではありません。極端な結合を行っているシーンオブジェクトを使用しているのはなぜですか。これは、作成とデバッグが面倒になるだけです。エンティティはオブジェクトの識別に使用され、コンポーネントはデータを保持し、システムはデータを操作します。RenderingSystemやSoundSystemなどの適切なシステムを用意する代わりに、これらすべてをまとめて、エンティティに必要なすべてのコンポーネントがある場合にのみそれらのシステムを悩ませるのはなぜですか。
dreta

1
シャドウキャスティングは通常、光源にアタッチされますが、コンポーネント「CastsShadow」を作成して、動的オブジェクトをレンダリングするときにそれを探すことができます。2Dを実行している場合、これは注文の基本的な問題にすぎません。単純なペインターのアルゴリズムがこの問題を解決します。TBHあなたは早すぎる心配です。あなたはそれをする時が来たときにこれを理解し、あなたはそれをあなたの頭に持っているだけです、今あなたはただ自分を混乱させています。初めてすべてが正しくなることを期待することはできません。それは起こりません。そこに着いたら橋を渡ります。
dreta

1
「エンティティとコンポーネントは単なるデータホルダーですが、システムはすべての機能を提供します。」必ずしも。彼らは一部の人々のアプローチにあります。しかし、他の人ではありません。Unityエンジンを見てください。すべての動作はコンポーネント内にあります。
カイロタン2012

-2

依存関係管理の概要101。

このコースは、依存関係の注入とリポジトリ設計の基本的な知識があることを前提としています。

依存性注入は、オブジェクトを直接結合せずに(メッセージ/シグナル/デリゲート/その他を介して)互いに対話するための優れた方法です。

それは、「new接着剤です」ます。

これをC#でデモします。

public interface IEntity
{
    int[] Position { get; }
    int[] Size { get; }
    bool Update();
    void Render();
}

public interface IRenderSystem
{
    void Draw(IEntity entity);
}

public interface IMovementSystem
{
    bool CanMoveLeft();
    void MoveLeft();
    bool CanMoveRight();
    void MoveRight();
    bool CanMoveUp();
    void MoveUp();
    bool CanMoveDown();
    void MoveDown();
    bool Moved();
    int[] Position { get; set; }
}

public interface IInputSystem
{
    string Direction { get; set; }
}

public class Player : IEntity
{
    private readonly IInputSystem _inputSystem;
    private readonly IMovementSystem _movementSystem;
    private readonly IRenderSystem _renderSystem;
    private readonly int[] _size = new[] { 10, 10 };

    public Player(IRenderSystem renderSystem, IMovementSystem movementSystem, IInputSystem inputSystem)
    {
        _renderSystem = renderSystem;
        _movementSystem = movementSystem;
        _inputSystem = inputSystem;
    }

    public bool Update()
    {
        if (_inputSystem.Direction == "Left" && _movementSystem.CanMoveLeft())
            _movementSystem.MoveLeft();
        if (_inputSystem.Direction == "Right" && _movementSystem.CanMoveRight())
            _movementSystem.MoveRight();
        if (_inputSystem.Direction == "Up" && _movementSystem.CanMoveUp())
            _movementSystem.MoveUp();
        if (_inputSystem.Direction == "Down" && _movementSystem.CanMoveDown())
            _movementSystem.MoveDown();

        return _movementSystem.Moved();
    }

    public void Render()
    {
        if (_movementSystem.Moved())
            _renderSystem.Draw(this);
    }

    public int[] Position
    {
        get { return _movementSystem.Position; }
    }

    public int[] Size
    {
        get { return _size; }
    }
}

これは、入力、動き、レンダリングの基本的なシステムです。のPlayerこのクラスは、この場合のエンティティであり、構成要素は、インタフェースです。Playerクラスはどのようにコンクリートの内部について何も知らないIRenderSystemIMovementSystemまたはIInputSystem仕事を。ただし、インターフェースは、Playerは、最終的な結果の達成方法に依存せずに、が信号を送信する方法(IRenderSystemでDrawを呼び出すなど)を提供します。

たとえば、IMovementSystemの実装を見てみましょう。

public interface IGameMap
{
    string LeftOf(int[] currentPosition);
    string RightOf(int[] currentPosition);
    string UpOf(int[] currentPosition);
    string DownOf(int[] currentPosition);
}

public class MovementSystem : IMovementSystem
{
    private readonly IGameMap _gameMap;
    private int[] _previousPosition;
    private readonly int[] _currentPosition;
    public MovementSystem(IGameMap gameMap, int[] initialPosition)
    {
        _gameMap = gameMap;
        _currentPosition = initialPosition;
        _previousPosition = initialPosition;
    }

    public bool CanMoveLeft()
    {
        return _gameMap.LeftOf(_currentPosition) == "Unoccupied";
    }

    public void MoveLeft()
    {
        _previousPosition = _currentPosition;
        _currentPosition[0]--;
    }

    public bool CanMoveRight()
    {
        return _gameMap.RightOf(_currentPosition) == "Unoccupied";
    }

    public void MoveRight()
    {
        _previousPosition = _currentPosition;
        _currentPosition[0]++;
    }

    public bool CanMoveUp()
    {
        return _gameMap.UpOf(_currentPosition) == "Unoccupied";
    }

    public void MoveUp()
    {
        _previousPosition = _currentPosition;
        _currentPosition[1]--;
    }

    public bool CanMoveDown()
    {
        return _gameMap.DownOf(_currentPosition) == "Unoccupied";
    }

    public void MoveDown()
    {
        _previousPosition = _currentPosition;
        _currentPosition[1]++;
    }

    public bool Moved()
    {
        return _previousPosition == _currentPosition;
    }

    public int[] Position
    {
        get { return _currentPosition; }
    }
}

MovementSystem 独自の依存関係と Player気にしません。インターフェイスを使用して、ゲームステートマシンを作成できます。

public class GameEngine
{
    private readonly List<IEntity> _entities;
    private List<IEntity> _renderQueue; 

    public GameEngine()
    {
        _entities = new List<IEntity>();
    }

    public void RegisterEntity(IEntity entity)
    {
        _entities.Add(entity);
    }

    public void Update()
    {
        _renderQueue = new List<IEntity>();
        foreach (var entity in _entities)
        {
            if(entity.Update())
                _renderQueue.Add(entity);
        }
        // Linq version for those interested
        //_renderQueue.AddRange(_entities.Where(e => e.Update()));
    }

    public void Render()
    {
        foreach (var entity in _renderQueue)
        {
            entity.Render();
        }
    }
}

そして、それは素敵なゲームの始まりです(これも単体テスト可能です)。

そして、いくつかの追加といくつかの多態性:

public interface IEntity
{
}

public interface IRenderableEntity : IEntity
{
    void Render();        
}

public interface IUpdateableEntity : IEntity
{
    void Update();
    bool Updated { get; }
}

public interface IRenderSystem
{
    void Draw(IRenderableEntity entity);
}

// new player class
public class Player : IRenderableEntity, IUpdateableEntity
{
    private readonly IInputSystem _inputSystem;
    private readonly IMovementSystem _movementSystem;
    private readonly IRenderSystem _renderSystem;
    private readonly int[] _size = new[] { 10, 10 };

    public Player(IRenderSystem renderSystem, IMovementSystem movementSystem, IInputSystem inputSystem)
    {
        _renderSystem = renderSystem;
        _movementSystem = movementSystem;
        _inputSystem = inputSystem;
    }

    public void Update()
    {
        if (_inputSystem.Direction == "Left" && _movementSystem.CanMoveLeft())
            _movementSystem.MoveLeft();
        if (_inputSystem.Direction == "Right" && _movementSystem.CanMoveRight())
            _movementSystem.MoveRight();
        if (_inputSystem.Direction == "Up" && _movementSystem.CanMoveUp())
            _movementSystem.MoveUp();
        if (_inputSystem.Direction == "Down" && _movementSystem.CanMoveDown())
            _movementSystem.MoveDown();
    }

    public bool Updated
    {
        get { return _movementSystem.Moved(); }
    }

    public void Render()
    {
        if (_movementSystem.Moved())
            _renderSystem.Draw(this);
    }

    public int[] Position
    {
        get { return _movementSystem.Position; }
    }

    public int[] Size
    {
        get { return _size; }
    }
}

public class GameEngine
{
    private readonly List<IEntity> _entities;
    private List<IRenderableEntity> _renderQueue; 

    public GameEngine()
    {
        _entities = new List<IEntity>();
    }

    public void RegisterEntity(IEntity entity)
    {
        _entities.Add(entity);
    }

    public void Update()
    {
        _renderQueue = new List<IRenderableEntity>();
        foreach (var entity in _entities)
        {
            if (entity is IUpdateableEntity)
            {
                var updateEntity = entity as IUpdateableEntity;
                updateEntity.Update();
            }

            if (entity is IRenderableEntity)
            {
                var renderEntity = entity as IRenderableEntity;
                _renderQueue.Add(renderEntity);
            }
        }
    }

    public void Render()
    {
        foreach (var entity in _renderQueue)
        {
            entity.Render();
        }
    }
}

これで、集約インターフェースと緩い継承に基づくプリミティブエンティティ/コンポーネントシステムができました。


1
これはコンポーネントの設計に反するものです:)あるプレーヤーに音を出させ、他のプレーヤーに音を出させないようにするにはどうしますか?
Kikaimaru

音声を再生しないISoundSystemの@Kikaimaru Pass。すなわち、何もしません。
ダスティンキンゲン

3
-1は、コードが悪いからではなく、コンポーネントベースのアーキテクチャにはまったく関係がないためです。実際、コンポーネントが回避しようとしているのは、このようなインターフェイスの急増です。
カイロタン2012

@キロタン私の理解は間違っているに違いないと思います。
ダスティンキンゲン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.