私はXNAゲーム用の柔軟なパーティクルシステムを作成しようとしていますが、次のインターフェイスがあります。
_public interface IParticle : IUpdateable
{
bool Alive { get; }
float Percent { get; }
}
public interface IParticleEffect<T>
where T : IParticle
{
void Apply(GameTime time, ref T particle);
}
public interface IParticleEmitter<T> : IUpdateable
where T : IParticle
{
}
public interface IParticleRenderer<T> : IDrawable
where T : IParticle
{
}
_
このシステムの背後にある考え方は、クライアントコードはIParticle
から派生する必要があるだけであり、次にIParticleEmitter
とIParticleRenderer
から互換性のあるサブクラスを作成する必要があるということです。シーン。 (私は現在、すべてを書いている最中ですが、後者の2つは利用可能な抽象基本クラスを持っています。)
とにかく、一部のパーティクルシステムは、最適化のために可変構造体を使用することを好み、それは完全に合理的です。私のシステムはスケルトンのみを提供しており、クライアントが「ねえ、構造体は進むべき道だ!」とクライアントが決定した場合、システムはクライアントコードがスローするものをすべてサポートする必要があります。これが、私のIParticleEffect.Apply()
メソッドがrefによってパーティクルを取得する理由です-構造体を参照で渡す方が、コピーするよりも安価ですそれ。
残念ながら、コレクションが関係している場合は失敗します。foreachイテレータは、refまたはによって渡されたオブジェクトとうまく連携しないためです。 )out。 Eric Lippertが here の理由を説明します。
だから、今私は設計上の決定をします:
where T: class, IParticle
_に変更します。これにより、将来の最適化が損なわれる可能性がありますが、コレクションの操作がはるかに簡単になります。ICollection<T>
_または_IEnumerable<T>
_を使用するものを_IList<T>
_に変更して、インデクサーを介して手動でポーリングできるようにします。これにより、潜在的にはより強力になりますが、オブジェクトを格納するために、より深いインターフェイス(リスト)を使用するという犠牲が伴います。この質問があまりにも「依存する」ことではないことを願っていますが、ここで私が望む方法で機能させるためにどのような戦略を適用できるかについて興味があります。
[〜#〜] edit [〜#〜]:次のようなローカル変数を含めることもできることに気付きました:
_foreach (var particle in SomeParticleCollection)
{
var p = particle;
SomeEffect.Apply(ref p);
}
_
ただし、p
には、それをコピーすることによる最終的な効果があり、これも理想的ではありません。
時期尚早の最適化は悪です。
構造体をドロップします-柔軟な設計に進みます。最初の設計では、柔軟性/拡張性を考慮しているようです。パフォーマンスの問題が発生するまでは、柔軟なアーキテクチャを使用してください。構造体とオブジェクトを渡すことが、コードの最大のパフォーマンスの低下であると誰もが確信できるように、本格的なパフォーマンスプロファイリングを行う必要があります-レンダリング、更新ロジック、AIなどはどうですか?
これは、ゲームプログラミングアーキテクチャをパフォーマンスのために最適化するかどうか、またはどのように最適化するかについての記事です。 http://gameprogrammingpatterns.com/architecture-performance-and-games.html
あなたは本当にそのような種類の最適化(ref T
)が本当に必要ですか?類似のシステムを検討したり、自分のシステムをあざけたりしたことがありますか?時期尚早の最適化は、何もないよりも悪いです-せいぜい明白ではなく、最悪では理解できない決定で終わります。
ref T
は、参照された値が関数によって変更される可能性があることを示します。そして、それはIParticleEffect<T>
の正常な動作だとは思いません。奇妙に見えます。
独自のシステムを定義していて、無関係なパーティクルシステムは特別にそれに接続する必要があります。最悪の場合、構造をコピーするとパフォーマンスが大幅に低下する場合は、クラス変数にラップしてみてください。
public class ParticleStructWrapper<T> : IParticle
where T: struct, IParticle
{
T _Particle;
public ParticleStructWrapper(T particle) {...}
public Boolean IsAlive { get {return this._Particle.IsAlive;}}
...
}
それでもコンストラクターのコピーは発生しますが、それを複数回使用する場合、正味の効果は小さくなります。
ただし、元のパーティクルをIParticleEffectで変更する必要がある場合は、元のソリューションを使用するか(非常に単純ではないにもかかわらず)、より高度なプロキシを作成する必要があります。
[〜#〜]後[〜#〜]
その意図は、効果が粒子を修正することでした
構造体の背後にある主なアイデアは、値による単純な受け渡しを可能にすることです。このような受け渡しは、実際のディープコピーを作成するような構造体フィールドのみを持つ構造体の場合です。このような構造体を使用すると、元の構造はまったく影響を受けないため、誰かがメソッド内で変更することを恐れる必要はありません。小さな構造の場合、構造がスタック上にあるため、値によるコピーとその後の使用も高速です。
これらの外部パーティクルシステムを変更することはできませんが、開発者に以下を読むことをお勧めできます http://msdn.Microsoft.com/en-us/library/ms229017(v = vs.110).aspx =、 https://stackoverflow.com/questions/441309/why-are-mutable-structs-evil 。
PS:彼らがすでにクラスと構造体の間のすべての長所と短所を考慮している可能性は十分にあります。それらのソリューションが最もユーザーフレンドリーではないか、または粒子が不変であるように設計されています。
構造体を削除する提案でJBRWilkinsonを使用しています。
その答えに書かれていることを超えて、IUpdateable構造体を実装することは、構造体のニーズが変更可能でなければならないことを意味し、特定のクラスのバグを招く可能性があります。ヒープ上にある構造体は参照ではなくコピーによって割り当てられるため、コピーに加えられた変更はゲームオブジェクトのデータベースに反映されません。つまりあなたがのようなコードを書いた場合
foreach (var particle in SomeParticleCollection)
{
particle.EnabledChanged += myEvent;
}
構造体「particle」はコレクション内の構造体のコピーになるため、実際には何もしない場合があります。同様に、着信IParticleを変更するIParticleEffectを実装する人は、参照渡しされていても失望します。
(IUpdateableを実装するためにシステムが変更可能な構造体を使用する場合、実際にはほとんどの場合ボックス化されたままになっていると思います。何かが欠落していない限り、これは最適化の目的を損なうようです。)