forked from playcanvas/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathentity.js
More file actions
350 lines (315 loc) · 12.1 KB
/
entity.js
File metadata and controls
350 lines (315 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
pc.extend(pc, function () {
/**
* @name pc.Entity
* @param {String} [name] The non-unique name of the entity, default is "Untitled".
* @param {pc.Application} [app] The application the entity belongs to, default is the current application.
* @class The Entity is the core primitive of a PlayCanvas game. Generally speaking an object in your game will consist of an {@link pc.Entity},
* and a set of {@link pc.Component}s which are managed by their respective {@link pc.ComponentSystem}s. One of those components maybe a
* {@link pc.ScriptComponent} which allows you to write custom code to attach to your Entity.
* <p>
* The Entity uniquely identifies the object and also provides a transform for position and orientation
* which it inherits from {@link pc.GraphNode} so can be added into the scene graph.
* The Component and ComponentSystem provide the logic to give an Entity a specific type of behavior. e.g. the ability to
* render a model or play a sound. Components are specific to an instance of an Entity and are attached (e.g. `this.entity.model`)
* ComponentSystems allow access to all Entities and Components and are attached to the {@link pc.Application}.
* </p>
*
* @example
* var app = ... // Get the pc.Application
*
* var entity = new pc.Entity();
*
* // Add a Component to the Entity
* entity.addComponent("camera", {
* fov: 45,
* nearClip: 1,
* farClip: 10000
* });
*
* // Add the Entity into the scene graph
* app.root.addChild(entity);
*
* // Move the entity
* entity.translate(10, 0, 0);
*
* // Or translate it by setting it's position directly
* var p = entity.getPosition();
* entity.setPosition(p.x + 10, p.y, p.z);
*
* // Change the entity's rotation in local space
* var e = entity.getLocalEulerAngles();
* entity.setLocalEulerAngles(e.x, e.y + 90, e.z);
*
* // Or use rotateLocal
* entity.rotateLocal(0, 90, 0);
*
* @extends pc.GraphNode
*/
var Entity = function(name, app){
if (name instanceof pc.Application) app = name;
this._guid = pc.guid.create(); // Globally Unique Identifier
this._batchHandle = null; // The handle for a RequestBatch, set this if you want to Component's to load their resources using a pre-existing RequestBatch.
this.c = {}; // Component storage
this._app = app; // store app
if (!app) {
this._app = pc.Application.getApplication(); // get the current application
if (!this._app) {
console.error("Couldn't find current application");
}
}
pc.events.attach(this);
};
Entity = pc.inherits(Entity, pc.GraphNode);
/**
* @function
* @name pc.Entity#addComponent
* @description Create a new component and add it to the entity.
* Use this to add functionality to the entity like rendering a model, playing sounds and so on.
* @param {String} type The name of the component to add. Valid strings are:
* <ul>
* <li>"animation" - see {@link pc.AnimationComponent}</li>
* <li>"audiolistener" - see {@link pc.AudioListenerComponent}</li>
* <li>"camera" - see {@link pc.CameraComponent}</li>
* <li>"collision" - see {@link pc.CollisionComponent}</li>
* <li>"element" - see {@link pc.ElementComponent}</li>
* <li>"light" - see {@link pc.LightComponent}</li>
* <li>"model" - see {@link pc.ModelComponent}</li>
* <li>"particlesystem" - see {@link pc.ParticleSystemComponent}</li>
* <li>"rigidbody" - see {@link pc.RigidBodyComponent}</li>
* <li>"screen" - see {@link pc.ScreenComponent}</li>
* <li>"script" - see {@link pc.ScriptComponent}</li>
* <li>"sound" - see {@link pc.SoundComponent}</li>
* <li>"zone" - see {@link pc.ZoneComponent}</li>
* </ul>
* @param {Object} data The initialization data for the specific component type. Refer to each
* specific component's API reference page for details on valid values for this parameter.
* @returns {pc.Component} The new Component that was attached to the entity
* @example
* var entity = new pc.Entity();
* entity.addComponent("light"); // Add a light component with default properties
* entity.addComponent("camera", { // Add a camera component with some specified properties
* fov: 45,
* clearColor: new pc.Color(1,0,0),
* });
*/
Entity.prototype.addComponent = function (type, data) {
var system = this._app.systems[type];
if (system) {
if (!this.c[type]) {
return system.addComponent(this, data);
} else {
logERROR(pc.string.format("Entity already has {0} Component", type));
}
} else {
logERROR(pc.string.format("System: '{0}' doesn't exist", type));
return null;
}
};
/**
* @function
* @name pc.Entity#removeComponent
* @description Remove a component from the Entity.
* @param {String} type The name of the Component type
* @example
* var entity = new pc.Entity();
* entity.addComponent("light"); // add new light component
* //...
* entity.removeComponent("light"); // remove light component
*/
Entity.prototype.removeComponent = function (type) {
var system = this._app.systems[type];
if (system) {
if (this.c[type]) {
system.removeComponent(this);
} else {
logERROR(pc.string.format("Entity doesn't have {0} Component", type));
}
} else {
logERROR(pc.string.format("System: '{0}' doesn't exist", type));
}
};
/**
* @private
* @function
* @name pc.Entity#getGuid
* @description Get the GUID value for this Entity
* @returns {String} The GUID of the Entity
*/
Entity.prototype.getGuid = function () {
return this._guid;
};
/**
* @private
* @function
* @name pc.Entity#setGuid
* @description Set the GUID value for this Entity.
*
* N.B. It is unlikely that you should need to change the GUID value of an Entity at run-time. Doing so will corrupt the graph this Entity is in.
* @param {String} guid The GUID to assign to the Entity
*/
Entity.prototype.setGuid = function (guid) {
this._guid = guid;
};
Entity.prototype._notifyHierarchyStateChanged = function (node, enabled) {
var enableFirst = false;
if (node === this && this._app._enableList.length === 0)
enableFirst = true;
node._onHierarchyStateChanged(enabled);
if (node._onHierarchyStatePostChanged)
this._app._enableList.push(node);
var i, len;
var c = node._children;
for (i = 0, len = c.length; i < len; i++) {
if (c[i]._enabled)
this._notifyHierarchyStateChanged(c[i], enabled);
}
if (enableFirst) {
for(i = 0, len = this._app._enableList.length; i < len; i++)
this._app._enableList[i]._onHierarchyStatePostChanged();
this._app._enableList.length = 0;
}
};
Entity.prototype._onHierarchyStateChanged = function (enabled) {
pc.Entity._super._onHierarchyStateChanged.call(this, enabled);
// enable / disable all the components
var component;
var components = this.c;
for (var type in components) {
if (components.hasOwnProperty(type)) {
component = components[type];
if (component.enabled) {
if (enabled) {
component.onEnable();
} else {
component.onDisable();
}
}
}
}
};
Entity.prototype._onHierarchyStatePostChanged = function () {
// post enable all the components
var components = this.c;
for (var type in components) {
if (components.hasOwnProperty(type))
components[type].onPostStateChange();
}
};
/**
* @private
* @function
* @name pc.Entity#setRequest
* @description Used during resource loading to ensure that child resources of Entities are tracked
* @param {ResourceRequest} request The request being used to load this entity
*/
Entity.prototype.setRequest = function (request) {
this._request = request;
};
/**
* @private
* @function
* @name pc.Entity#getRequest
* @description Get the Request that is being used to load this Entity
* @returns {ResourceRequest} The Request
*/
Entity.prototype.getRequest = function () {
return this._request;
};
/**
* @function
* @name pc.Entity#findByGuid
* @description Find a descendant of this Entity with the GUID
* @returns {pc.Entity} The Entity with the GUID or null
*/
Entity.prototype.findByGuid = function (guid) {
if (this._guid === guid) return this;
for (var i = 0; i < this._children.length; i++) {
if(this._children[i].findByGuid) {
var found = this._children[i].findByGuid(guid);
if (found !== null) return found;
}
}
return null;
};
/**
* @function
* @name pc.Entity#destroy
* @description Remove all components from the Entity and detach it from the Entity hierarchy. Then recursively destroy all ancestor Entities
* @example
* var firstChild = this.entity.children[0];
* firstChild.destroy(); // delete child, all components and remove from hierarchy
*/
Entity.prototype.destroy = function () {
var name;
// Disable all enabled components first
for (name in this.c) {
this.c[name].enabled = false;
}
// Remove all components
for (name in this.c) {
this.c[name].system.removeComponent(this);
}
// Detach from parent
if (this._parent)
this._parent.removeChild(this);
var children = this._children;
var child = children.shift();
while (child) {
if (child instanceof pc.Entity) {
child.destroy();
}
// make sure child._parent is null because
// we have removed it from the children array before calling
// destroy on it
child._parent = null;
child = children.shift();
}
// fire destroy event
this.fire('destroy', this);
// clear all events
if (this._callbacks)
this._callbacks = null;
if (this._callbackActive)
this._callbackActive = null;
};
/**
* @function
* @name pc.Entity#clone
* @description Create a deep copy of the Entity. Duplicate the full Entity hierarchy, with all Components and all descendants.
* Note, this Entity is not in the hierarchy and must be added manually.
* @returns {pc.Entity} A new Entity which is a deep copy of the original.
* @example
* var e = this.entity.clone(); // Clone Entity
* this.entity.parent.addChild(e); // Add it as a sibling to the original
*/
Entity.prototype.clone = function () {
var type;
var c = new pc.Entity(this._app);
pc.Entity._super._cloneInternal.call(this, c);
for (type in this.c) {
var component = this.c[type];
component.system.cloneComponent(this, c);
}
var i;
for (i = 0; i < this._children.length; i++) {
var child = this._children[i];
if (child instanceof pc.Entity) {
c.addChild(child.clone());
}
}
return c;
};
return {
Entity: Entity
};
}());
/**
* @event
* @name pc.Entity#destroy
* @description Fired after the entity is destroyed.
* @param {pc.Entity} entity The entity that was destroyed.
* @example
* entity.on("destroy", function (e) {
* console.log('entity ' + e.name + ' has been destroyed');
* });
*/