]> git.r.bdr.sh - rbdr/serpentity/blob - lib/serpentity/serpentity.js
d86c47e49fcbf112b1255682561f2d9a8278d19a
[rbdr/serpentity] / lib / serpentity / serpentity.js
1 require("neon");
2
3 /*
4 Serpentity is a simple entity framework inspired by Ash.
5
6 Usage:
7
8 require('serpentity');
9
10 ## Instantiating an engine
11
12 var engine = Serpentity();
13
14 Add entities or systems, systems are added with a priority (the smaller
15 the number, the earlier it will be called):
16
17 engine.addEntity(entityFactory());
18 engine.addSystem(new GameSystem(), priority);
19
20 Update all systems:
21
22 engine.update(dt);
23
24 Remove entities or systems:
25
26 engine.removeEntity(entityReference);
27 engine.removeSystem(systemReference);
28
29 ## Creating Entities
30
31 Entities are the basic object of Serpentity, and they do nothing.
32
33 var entity = new Serpentity.Entity();
34
35 All the behavior is added through components
36
37 ## Creating Components
38
39 Components define data that we can add to an entity. This data will
40 eventually be consumed by "Systems"
41
42 Class("PositionComponent").inherits(Serpentity.Component)({
43 prototype : {
44 x : 0,
45 y : 0
46 }
47 });
48
49 You can add components to entities by using the add method:
50
51 entity.add(new PositionComponent());
52
53
54 Systems can refer to entities by requesting nodes.
55
56 ## Working with Nodes
57
58 Nodes are sets of components that you define, so your system can require
59 entities that always follow the API defined in the node.
60
61 Class("MovementNode").inherits(Serpentity.Node)({
62 types : {
63 position : PositionComponent,
64 motion : MotionComponent
65 }
66 });
67
68 You can then request an array of all the nodes representing entities
69 that comply with that API
70
71 engine.getNodes(MovementNode);
72
73 ## Creating Systems
74
75 Systems are called on every update, and they use components through nodes.
76
77 Class("TestSystem").inherits(Serpentity.System)({
78 prototype : {
79 added : function added(engine){
80 this.nodeList = engine.getNodes(MovementNode);
81 },
82 removed : function removed(engine){
83 this.nodeList = undefined;
84 }
85 update : function update(dt){
86 this.nodeList.forEach(function (node) {
87 console.log("Current position is: " + node.position.x + "," + node.position.y);
88 });
89 }
90 }
91 });
92
93 ## That's it
94
95 Just run `engine.update(dt)` in your game loop :D
96
97 */
98 Class("Serpentity")({
99 prototype : {
100 systems : null,
101 nodeCollections : null,
102 entities : null,
103
104 init : function init(config) {
105 var property;
106
107 config = config || {};
108
109 this.systems = [];
110 this.entities = [];
111 this.nodeCollections = {};
112
113 for (property in config) {
114 if (config.hasOwnProperty(property)) {
115 this[property] = config[property];
116 }
117 }
118 },
119
120 /*
121 * Adds a system to the engine, so its update method will be called
122 * with the others. Triggers added hook.
123 *
124 * returns true if added succesfully, false if already added
125 */
126 addSystem : function addSystem(system, priority) {
127 var lastIndex, found;
128
129 if (this.systems.indexOf(system) >= 0) {
130 return false;
131 }
132
133 system.priority = priority;
134
135 found = false;
136 lastIndex = 0;
137
138 this.systems.some(function findPriority(existingSystem, i) {
139 lastIndex = i;
140 if (existingSystem.priority >= system.priority) {
141 found = true;
142 return true;
143 }
144 });
145
146 if (!found) {
147 lastIndex += 1
148 }
149
150 this.systems.splice(lastIndex, 0, system);
151 system.added(this);
152 return true;
153 },
154
155 /*
156 * Removes a system from the engine, so its update method will no
157 * longer will be called. Triggers the removed hook.
158 *
159 * returns true if removed succesfully, false if already added
160 */
161 removeSystem : function removeSystem(system) {
162 var position;
163
164 position = this.systems.indexOf(system);
165 if (position >= 0) {
166 this.systems[position].removed(this);
167 this.systems.splice(position, 1);
168 return true;
169 }
170
171 return false;
172 },
173
174 /*
175 * Adds an entity to the engine, adds to existing node collections
176 *
177 * returns true if added, false if already there
178 */
179 addEntity : function addEntity(entity) {
180 var property;
181
182 if (this.entities.indexOf(entity) >= 0) {
183 return false;
184 }
185 this.entities.push(entity);
186
187 for (property in this.nodeCollections) {
188 if (this.nodeCollections.hasOwnProperty(property)) {
189 this.nodeCollections[property].add(entity);
190 }
191 }
192 return true;
193 },
194
195 /*
196 * Removes entity from system, removing from all node collections
197 *
198 * returns true if removed, false if not present
199 */
200 removeEntity : function removeEntity(entity) {
201 var position;
202
203 position = this.entities.indexOf(entity);
204 if (position >= 0) {
205 for (property in this.nodeCollections) {
206 if (this.nodeCollections.hasOwnProperty(property)) {
207 this.nodeCollections[property].remove(entity);
208 }
209 }
210
211 this.entities.splice(position, 1);
212 return true;
213 }
214
215 return false;
216 },
217
218 /*
219 * Given a Node Class, retrieves a list of all the nodes for each
220 * applicable entity.
221 */
222 getNodes : function getNodes(nodeType) {
223 var nodeCollection;
224
225 if (this.nodeCollections.hasOwnProperty(nodeType)) {
226 return this.nodeCollections[nodeType].nodes;
227 }
228
229 nodeCollection = new Serpentity.NodeCollection({
230 type : nodeType,
231 });
232 this.nodeCollections[nodeType] = nodeCollection;
233
234 this.entities.forEach(function (entity) {
235 nodeCollection.add(entity);
236 });
237
238 return nodeCollection.nodes;
239 },
240
241 /*
242 * Calls update for every loaded system.
243 */
244 update : function update(dt) {
245 this.systems.forEach(function (system) {
246 system.update(dt);
247 });
248 }
249 }
250 });
251
252 require("./component.js");
253 require("./entity.js");
254 require("./node.js");
255 require("./node_collection.js");
256 require("./system.js");