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