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