Bakalım bir web / UI JS dev olarak anlamaya çalışarak yardımcı olabilir miyim. Ayrıca, dil agnostisizminde fazla ileri gitmeyin. Diğer dillerde kurulan birçok desen çalışmaya değer, ancak esnekliği nedeniyle JS'de çok farklı uygulanabilir veya dilin dövülebilir doğası nedeniyle gerçekten gerekli değildir. JS kod kodunuzu, daha klasik bir OOP-yönelimli dille aynı sınırlara sahip olarak yazarsanız, bazı fırsatlar yaratabilirsiniz.
Her şeyden önce, "OOP kullanma" faktörü üzerinde, JavaScript nesnelerinin diğer dillerle karşılaştırıldığında playdough gibi olduğunu ve JS sınıf olmadığı için basamaklı bir miras şeması kabusu oluşturmak için yolunuzdan çıkmanız gerektiğini unutmayın. temelli ve kompozisyon çok daha doğal gelir. JS'nize aptalca bir sınıf veya prototip teslim etme sistemi uyguluyorsanız, onu kaldırmayı düşünün. JS'de kapaklar, prototipler kullanıyoruz ve şekerleme gibi işlevlerden geçiyoruz. İğrenç ve pis ve yanlış ama aynı zamanda güçlü, özlü ve sevdiğimiz yol bu.
Kalıtım ağır yaklaşımları aslında Tasarım Desenlerinde bir anti-desen olarak ve iyi bir nedenden dolayı, bir yöntemin bozulan versiyonunun nerede olduğunu anlamak ve denemek için 15+ sınıf veya sınıf benzeri yapıları gözden geçiren herkes olarak iyi bir şekilde belirtilmiştir. sana geliyordu diyebilirim.
Neden bu kadar çok programcı bunu sevdiğini bilmiyorum (özellikle bir sebepten dolayı JavaScript yazan java çocuklar), ama aşırı kullanıldığında korkunç, okunaksız ve tamamen sürdürülemez. Kalıtım burada ve orada iyidir, ama JS'de gerçekten gerekli değildir. Daha cazip bir kısayol olduğu dillerde, bir BunnyRabbit içeren bir miras zinciri aracılığıyla bir zombi uygulamasını frankensteining gibi daha gerçek modelleme şemalarından ziyade daha soyut mimari kaygılar için ayrılmalıdır. Bu kodun tekrar kullanılması iyi değil. Bu bir bakım kabusu.
JS dev Entity / Component / System tabanlı motorlar olarak bana tasarım kaygılarını ayırmak için bir sistem / model olarak vururlar ve daha sonra nesneleri çok ayrıntılı bir düzeyde uygulama için birleştiririz. Başka bir deyişle, çocuk oyuncağı JavaScript gibi bir dilde. Ama önce bunu doğru bir şekilde sallıyor muyum bakayım.
Varlık - Tasarladığınız belirli şey. Daha çok özel isimler yönünde konuşuyoruz (ama aslında değil). 'Sahne' değil, 'IntroAreaLevelOne'. IntroAreaLevelOne, bir sahneEntity kutusunun içinde oturabilir, ancak diğer ilgili şeylerden farklı belirli bir şeye odaklanıyoruz. Kodda, bir varlık gerçekten yararlı olması için uygulanması veya kurulması (bileşenler) gereken bir grup şeyle bağlantılı bir addır.
Bileşenler - Bir işletmenin ihtiyaç duyduğu şey türleri. Bunlar genel isimler. WalkingAnimation gibi. WalkingAnimation içinde "Shambling" (zombiler ve bitki canavarları için iyi bir seçim) veya "ChickenWalker" (ters eklemli ed-209ish robot tipleri için harika) gibi daha spesifik olabiliriz. Not: Bunun böyle bir 3D modelin oluşturulmasından nasıl ayrılabileceğinden emin değilim - bu yüzden belki bir saçmalık örneği ama deneyimli bir oyun geliştiricisinden daha çok bir JS pro'suyum. JS'de eşleme mekanizmasını bileşenlerle aynı kutuya koyardım. Bileşenlerin kendi başlarına mantıklı olmaları ve sistemlerin hatta sistemlere ihtiyaç duyulursa ne uygulayacaklarını söyleyen bir yol haritasının daha hafif olması muhtemeldir (ECS denememde bazı bileşenler sadece özellik kümeleri koleksiyonlarıdır). Bir bileşen kurulduktan sonra, '
Sistemler - Gerçek programmey et burada. AI sistemleri inşa edilir ve bağlantılandırılır, Oluşturma gerçekleştirilir, animasyon dizileri oluşturulur, vb ... Bunları toparlayıp çoğunlukla hayal gücüne bırakıyorum, ancak System.AI'de bir grup özellik alır ve bir işlevi tükürür. sonuçta uygulamada kullanılan nesneye olay işleyicileri eklemek için kullanılır. System.AI ile ilgili en önemli şey, çoklu bileşen türlerini kapsamasıdır. Tüm AI öğelerini tek bir bileşenle sıralayabilirsiniz, ancak bunu yapmak, şeyleri ayrıntılı hale getirme noktasını yanlış anlamaktır.
Hedeflere Dikkat Edin: Tasarımcı olmayanlar için, bir paradigma içindeki bileşenleri maksimize ederek ve eşleştirerek farklı şeyleri kolayca değiştirmeleri için bir tür GUI arabirimini takmayı kolaylaştırmak istiyoruz ve uzak durmak istiyoruz. Yazmak, değiştirmek veya korumaktan çok daha kolay olan popüler rasgele kod şemaları.
Yani JS'de, belki böyle bir şey. Oyun geliştiricileri lütfen bana çok yanlış anladıysam söyle:
//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);
}
Artık bir NPC'ye ihtiyacınız olduğunda, npcBuilders.<npcName>();
Bir GUI npcEntities ve bileşenler nesnelerine takılabilir ve tasarımcıların eski varlıkları değiştirmesine veya bileşenleri basitçe karıştırarak ve eşleştirerek yeni varlıklar oluşturmasına izin verebilir (ancak varsayılan olmayan bileşenler için herhangi bir mekanizma olmasa da, özel bileşenler anında eklenebilir. bunun için tanımlanmış bir bileşen olduğu sürece kodlayın.