Biarkan saya melihat apakah dengan mencoba memahami sebagai web / UI JS dev, saya dapat membantu. Juga, jangan melangkah terlalu jauh dalam agnostisisme bahasa. Banyak pola yang dibuat dalam bahasa lain layak dipelajari tetapi dapat diterapkan sangat berbeda di JS karena fleksibilitasnya atau sebenarnya tidak diperlukan karena sifat bahasa yang mudah ditempa. Anda dapat menghancurkan beberapa peluang jika Anda menulis kode yang menganggap JS memiliki batas yang sama dengan bahasa berorientasi OOP yang lebih klasik.
Pertama-tama, pada faktor "jangan gunakan OOP", ingat bahwa objek JavaScript seperti playdough dibandingkan dengan bahasa lain dan Anda benar-benar harus pergi keluar dari cara Anda untuk membangun mimpi buruk skema warisan-cascading karena JS bukan kelas Berbasis dan pengomposisian datang secara alami. Jika Anda menerapkan beberapa kelas konyol atau prototipe sistem hand-down di JS Anda, pertimbangkan membuangnya. Di JS kami menggunakan penutupan, prototipe, dan kami melewati fungsi seperti permen. Itu menjijikkan dan kotor dan salah tetapi juga kuat, ringkas dan itulah cara kami menyukainya.
Pendekatan berat warisan sebenarnya dieja sebagai anti-pola dalam Pola Desain dan untuk alasan yang baik sebagai siapa saja yang telah berjalan 15+ level senilai kelas atau struktur kelas-seperti untuk mencoba dan mencari tahu di mana sih versi rusak dari suatu metode datang dari dapat memberitahu Anda.
Saya tidak tahu mengapa begitu banyak programmer suka melakukan ini (terutama orang-orang java menulis JavaScript untuk beberapa alasan), tapi itu mengerikan, tidak terbaca, dan benar-benar tidak dapat dipelihara bila digunakan secara berlebihan. Warisan baik-baik saja di sana-sini, tetapi tidak terlalu diperlukan di JS. Dalam bahasa yang merupakan jalan pintas yang lebih memikat, itu harus benar-benar dicadangkan untuk masalah arsitektur yang lebih abstrak daripada skema pemodelan yang lebih literal seperti frankensteining implementasi zombie melalui rantai pewarisan yang menyertakan BunnyRabbit karena kebetulan bekerja. Itu bukan kode yang baik digunakan kembali. Ini adalah mimpi buruk pemeliharaan.
Sebagai JS dev Entity / Component / System engine engine menyerang saya sebagai sistem / pola untuk decoupling masalah desain dan kemudian mengomposit objek untuk implementasi pada level yang sangat granular. Dengan kata lain, permainan anak dalam bahasa seperti JavaScript. Tapi biarkan saya melihat apakah saya grokking ini dengan benar terlebih dahulu.
Entity - Hal spesifik yang Anda rancang. Kita berbicara lebih banyak ke arah kata benda yang tepat (tapi tentu saja tidak sebenarnya). Bukan 'Scene', tapi 'IntroAreaLevelOne'. IntroAreaLevelOne mungkin duduk di dalam kotak sceneEntity dari beberapa jenis tetapi kami fokus pada sesuatu yang spesifik yang bervariasi dari hal-hal terkait lainnya. Dalam kode tersebut, suatu entitas benar-benar hanya sebuah nama (atau ID) yang diikat ke banyak hal yang harus diimplementasikan atau dibuat (komponen) agar bermanfaat.
Komponen - jenis hal yang dibutuhkan entitas. Ini adalah kata benda umum. Seperti WalkingAnimation. Dalam WalkingAnimation kita bisa mendapatkan yang lebih spesifik, seperti "Shambling" (pilihan yang baik untuk zombie dan monster tanaman), atau "ChickenWalker" (bagus untuk tipe robot reverse-joint ed-209ish). Catatan: Tidak yakin bagaimana itu bisa lepas dari rendering model 3D seperti itu - jadi mungkin contoh omong kosong tapi saya lebih dari pro JS daripada pengembang game yang berpengalaman. Di JS saya akan meletakkan mekanisme pemetaan dalam kotak yang sama dengan komponen. Komponen dalam hak mereka sendiri cenderung ringan pada logika dan lebih dari peta jalan memberitahu sistem Anda apa yang harus diterapkan jika sistem bahkan diperlukan (dalam upaya saya di ECS beberapa komponen hanya kumpulan set properti). Setelah komponen terbentuk, itu '
Sistem - Daging program yang sebenarnya ada di sini. Sistem AI dibangun dan dihubungkan, Rendering tercapai, sekuens animasi dibuat, dll ... Saya menghabiskan sebagian besar ini untuk imajinasi tetapi dalam contoh System.AI mengambil banyak properti dan mengeluarkan fungsi yang digunakan untuk menambahkan event handler ke objek yang akhirnya akan digunakan dalam implementasi. Kuncinya tentang System.AI adalah bahwa ia mencakup beberapa jenis komponen. Anda bisa memilah-milah semua barang AI dengan satu komponen tetapi untuk melakukannya adalah salah paham tentang titik pembuatan barang-barang granular.
Pikirkan Tujuan: Kami ingin membuatnya mudah untuk menyambungkan beberapa jenis antarmuka GUI untuk non-desainer untuk dengan mudah mengubah berbagai jenis barang dengan memaksimalkan dan mencocokkan komponen dalam paradigma yang masuk akal bagi mereka, dan kami ingin menjauh dari skema kode arbitrer populer yang jauh lebih mudah untuk ditulis daripada harus dimodifikasi atau dipelihara.
Jadi di JS, mungkin kira-kira seperti ini. Game devs, tolong beri tahu saya jika saya salah:
//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);
}
Sekarang kapan pun Anda membutuhkan NPC, Anda bisa membuatnya npcBuilders.<npcName>();
GUI dapat menyambung ke objek npcEntities dan komponen dan memungkinkan desainer untuk mengubah entitas lama atau hanya dengan mencampur dan mencocokkan komponen (meskipun tidak ada mekanisme di sana untuk komponen non-standar tetapi komponen khusus dapat ditambahkan dengan cepat di kode selama ada komponen yang ditentukan untuk itu.