웹 / UI JS 개발자로 이해하려고 노력하면 도움이 될 수 있습니다. 또한 언어 불가지론으로 너무 멀리 가지 마십시오. 다른 언어로 설정된 많은 패턴은 공부할 가치가 있지만 유연성으로 인해 JS에서 매우 다르게 적용될 수 있거나 언어의 가변성 때문에 실제로 필요하지 않습니다. JS에 대한 코드 생각을 좀 더 고전적인 OOP 지향 언어와 동일한 경계를 갖는 것으로 작성하면 약간의 기회를 날릴 수 있습니다.
우선, "OOP를 사용하지 마십시오"요소에서 JavaScript 객체는 다른 언어와 비교하여 재생되는 것과 비슷하며 JS가 클래스가 아니기 때문에 계단식 상속 체계를 악용하기 위해 실제로 나가야한다는 것을 기억하십시오. 기반과 합성은 훨씬 더 자연스럽게 온다. JS에서 바보 같은 클래스 또는 프로토 타입 핸드 다운 시스템을 구현하는 경우에는 버리는 것을 고려하십시오. JS에서는 클로저, 프로토 타입을 사용하고 사탕처럼 함수를 전달합니다. 역겹고 불결하고 잘못되었지만 강력하고 간결하며 그것이 우리가 좋아하는 방식입니다.
상속 무거운 접근 방식은 실제로 디자인 패턴에서 반 패턴 (anti-pattern)으로 작성되었으며 15 가지 이상의 수준의 클래스 또는 클래스와 같은 구조를 걸어 본 사람이 어디에서 버려지는 방법의 버전을 찾아 내려고 노력했는지 당신에게서 말할 수 있었다.
왜 그렇게 많은 프로그래머들이이 작업을 좋아하는지 (특히 어떤 이유로 자바 스크립트를 작성하는 자바 사람들) 모르지만, 너무 많이 사용하면 끔찍하고 읽을 수 없으며 완전히 유지할 수 없습니다. 상속은 여기 저기 괜찮지 만 JS에서는 실제로 필요하지 않습니다. 좀 더 매력적인 지름길 인 언어에서는 BunnyRabbit가 포함 된 상속 체인을 통해 좀비 구현을 프랑켄슈타인 화하는 것보다 좀 더 추상적 인 아키텍처 관련 문제에 대비하여 더 추상적 인 아키텍처 문제에 대비해야합니다. 좋은 코드 재사용이 아닙니다. 유지 보수의 악몽입니다.
JS dev Entity / Component / System 기반 엔진은 설계 문제를 분리 한 다음 구현할 객체를 매우 세분화하여 시스템 / 패턴으로 생각합니다. 다시 말해, 어린이는 JavaScript와 같은 언어로 게임을합니다. 그러나 내가 이것을 올바르게 구웠는지 보자.
엔터티-디자인중인 특정 항목입니다. 우리는 적절한 명사 (실제로는 물론 아닙니다)의 방향으로 더 많이 이야기하고 있습니다. 'Scene'이 아니라 'IntroAreaLevelOne'입니다. IntroAreaLevelOne은 일종의 sceneEntity 상자 안에있을 수 있지만 다른 관련 항목과 다른 특정 항목에 중점을두고 있습니다. 코드에서 엔터티는 실제로 유용하거나 사용하기 위해 구현하거나 설정해야하는 여러 가지 요소에 묶인 이름 (또는 ID) 일뿐입니다.
구성 요소-기업이 필요로하는 것들의 유형. 이들은 일반적인 명사입니다. WalkingAnimation처럼. WalkingAnimation 내에서 "Shambling"(좀비 및 식물 몬스터에게 적합) 또는 "ChickenWalker"(역 접합 ed-209ish 로봇 유형에 적합)와 같이보다 구체적으로 얻을 수 있습니다. 참고 : 3D 모델의 렌더링과 어떻게 분리 될 수 있는지 잘 모르겠습니다. 따라서 끔찍한 예일 수도 있지만 숙련 된 게임 개발자보다 JS 전문가에 더 가깝습니다. JS에서는 매핑 메커니즘을 구성 요소와 동일한 상자에 넣었습니다. 자체 구성 요소는 논리 및 시스템이 필요한 경우 구현해야 할 사항을 알려주는 로드맵에 대한 정보를 제공 할 가능성이 높습니다 (ECS에서 일부 구성 요소는 속성 집합 모음 일 뿐임). 구성 요소가 설정되면
시스템-실제 프로그램 고기가 여기에 있습니다. AI 시스템이 구축되고 연결되고 렌더링이 이루어지며 애니메이션 시퀀스가 설정됩니다. 나는 대부분을 상상하고 상상해 보지만 System.AI는 많은 속성을 취하고 함수를 뱉어냅니다. 구현에 궁극적으로 사용되는 객체에 이벤트 핸들러를 추가하는 데 사용됩니다. System.AI의 핵심은 여러 구성 요소 유형을 포함한다는 것입니다. 하나의 구성 요소로 모든 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 및 components 객체에 연결하여 디자이너가 단순히 구성 요소를 혼합하고 일치시켜 기존 엔터티를 조정하거나 새 엔터티를 만들 수 있습니다 (기본 구성 요소가 아닌 경우에는 별도의 메커니즘이 없지만 특별한 구성 요소는 즉시 추가 할 수 있음) 정의 된 컴포넌트가있는 한 코드.