Hãy để tôi xem nếu bằng cách cố gắng hiểu như là một nhà phát triển web / UI JS, tôi có thể giúp được gì không. Ngoài ra, đừng đi quá xa trong thuyết bất khả tri ngôn ngữ. Rất nhiều mẫu được thiết lập bằng các ngôn ngữ khác đáng để nghiên cứu nhưng có thể được áp dụng rất khác nhau trong JS do tính linh hoạt của nó hoặc thực sự không cần thiết do tính chất dễ uốn của ngôn ngữ. Bạn có thể thổi bay một số cơ hội nếu bạn viết mã nghĩ về JS giống như có cùng một tập hợp các ranh giới mà một ngôn ngữ hướng OOP cổ điển hơn thực hiện.
Trước hết, về yếu tố "không sử dụng OOP", hãy nhớ rằng các đối tượng JavaScript giống như trò chơi so với các ngôn ngữ khác và bạn thực sự phải tránh khỏi cơn ác mộng để xây dựng một cơn ác mộng kế hoạch xếp tầng vì JS không phải là lớp dựa trên cơ sở và kết hợp đến với nó một cách tự nhiên hơn nhiều. Nếu bạn đang triển khai một số lớp ngớ ngẩn hoặc hệ thống xử lý nguyên mẫu trong JS của bạn, hãy xem xét bỏ nó. Trong JS, chúng tôi sử dụng các bao đóng, các nguyên mẫu và chúng tôi chuyển các hàm xung quanh như kẹo. Thật kinh tởm và bẩn thỉu và sai trái nhưng cũng mạnh mẽ, súc tích và đó là cách chúng ta thích nó.
Các cách tiếp cận nặng kế thừa thực sự được đánh vần là một kiểu chống mẫu trong Mẫu thiết kế và vì lý do chính đáng vì bất kỳ ai đã bỏ qua hơn 15 cấp độ cấu trúc giống như lớp hoặc lớp để thử và tìm ra phiên bản bị phá vỡ của phương thức đã đến từ có thể cho bạn biết.
Tôi không biết tại sao rất nhiều lập trình viên thích làm điều này (đặc biệt là những người java viết JavaScript vì một số lý do), nhưng nó thật tồi tệ, không thể đọc được và hoàn toàn không thể nhận ra khi sử dụng quá mức. Kế thừa là ổn ở đây và ở đó, nhưng không thực sự cần thiết trong JS. Trong các ngôn ngữ mà nó là một lối tắt hấp dẫn hơn, nó thực sự nên được dành riêng cho các mối quan tâm kiến trúc trừu tượng hơn là các sơ đồ mô hình hóa theo nghĩa đen như triển khai thực hiện zombie thông qua chuỗi thừa kế bao gồm BunnyRmus vì nó tình cờ hoạt động. Đó không phải là sử dụng lại mã tốt. Đó là một cơn ác mộng bảo trì.
Là một công cụ dựa trên cơ sở / Thành phần / Hệ thống của JS dev tấn công tôi như một hệ thống / mẫu để tách các mối quan tâm thiết kế và sau đó kết hợp các đối tượng để thực hiện ở mức độ chi tiết cao. Nói cách khác, trẻ chơi bằng một ngôn ngữ như JavaScript. Nhưng hãy để tôi xem nếu tôi đang mò mẫm điều này một cách chính xác trước.
Thực thể - Điều cụ thể bạn đang thiết kế. Chúng ta đang nói nhiều hơn theo hướng của danh từ thích hợp (nhưng thực tế không phải vậy). Không phải 'Cảnh', mà là 'IntroAreaLevelOne'. IntroAreaLevelOne có thể ngồi trong một hộp cảnhEntity thuộc loại nào đó nhưng chúng tôi đang tập trung vào một cái gì đó cụ thể khác với những thứ liên quan khác. Trong mã, một thực thể thực sự chỉ là một tên (hoặc ID) được gắn với một loạt các thứ mà nó cần phải thực hiện hoặc thiết lập (các thành phần) để có ích.
Thành phần - loại điều mà một thực thể cần. Đây là những danh từ chung. Giống như WalkingAnimation. Trong WalkingAnimation, chúng ta có thể biết cụ thể hơn, như "Shamble" (lựa chọn tốt cho zombie và quái vật thực vật) hoặc "ChickenWalker" (tuyệt vời cho các loại robot ed-209ish khớp ngược). Lưu ý: Không chắc chắn làm thế nào điều đó có thể tách rời khỏi kết xuất mô hình 3D như thế - vì vậy có thể là một ví dụ tào lao nhưng tôi là một người chuyên nghiệp hơn là một nhà phát triển trò chơi có kinh nghiệm. Trong JS tôi sẽ đặt cơ chế ánh xạ trong cùng một hộp với các thành phần. Các thành phần theo quyền riêng của chúng có thể sẽ nhẹ về logic và hơn nữa là một lộ trình cho hệ thống của bạn biết phải thực hiện những gì nếu hệ thống thậm chí cần thiết (trong nỗ lực của tôi tại ECS, một số thành phần chỉ là tập hợp các tập thuộc tính). Khi một thành phần được thiết lập, nó '
Hệ thống - Thịt chương trình thực sự ở đây. Các hệ thống AI được xây dựng và liên kết, Kết xuất được thực hiện, các chuỗi hoạt hình được thiết lập, v.v ... Tôi đang xử lý và để lại những thứ này chủ yếu cho trí tưởng tượng, nhưng trong ví dụ System.AI lấy một loạt các thuộc tính và tạo ra một chức năng được sử dụng để thêm các trình xử lý sự kiện cho đối tượng cuối cùng được sử dụng trong triển khai. Điều quan trọng về System.AI là nó bao gồm nhiều loại thành phần. Bạn có thể sắp xếp tất cả các công cụ AI với một thành phần nhưng để làm như vậy là hiểu sai quan điểm làm cho mọi thứ trở nên chi tiết.
Lưu ý các Mục tiêu: Chúng tôi muốn giúp dễ dàng cắm một số loại giao diện GUI cho những người không phải là nhà thiết kế để dễ dàng điều chỉnh các loại nội dung khác nhau bằng cách tối đa hóa và khớp các thành phần trong mô hình có ý nghĩa với họ và chúng tôi muốn tránh xa sơ đồ mã tùy ý phổ biến dễ viết hơn rất nhiều so với sửa đổi hoặc duy trì.
Vì vậy, trong JS, có thể một cái gì đó như thế này. Các nhà phát triển trò chơi vui lòng cho tôi biết nếu tôi hiểu sai về nó:
//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);
}
Bây giờ bất cứ khi nào bạn cần một NPC, bạn có thể xây dựng với npcBuilders.<npcName>();
GUI có thể cắm vào các đối tượng thành phần và thành phần npcEntities và cho phép các nhà thiết kế điều chỉnh các thực thể cũ hoặc tạo các thực thể mới bằng cách trộn và kết hợp các thành phần (mặc dù không có cơ chế nào trong đó cho các thành phần không mặc định nhưng có thể thêm các thành phần đặc biệt vào trong mã miễn là có một thành phần được xác định cho nó.