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