This is pretty new to me as well so let’s see if I can run through this step by step:
ScriptRegistry.prototype.add = function(script) {
var self = this;
if (this._scripts.hasOwnProperty(script.__name)) {
setTimeout(function() {
if (script.prototype.swap) {
var old = self._scripts[script.__name];
var ind = self._list.indexOf(old);
self._list[ind] = script;
self._scripts[script.__name] = script;
self.fire("swap", script.__name, script);
self.fire("swap:" + script.__name, script);
} else {
console.warn("script registry already has '" + script.__name + "' script, define 'swap' method for new script type to enable code hot swapping");
}
});
return false;
}
This part check if the script class definition already exists and if there is a swap
function, call that for hot reloading of the script during development https://blog.playcanvas.com/playcanvas-scripts-2-0/
this._scripts[script.__name] = script;
this._list.push(script);
this.fire("add", script.__name, script);
this.fire("add:" + script.__name, script);
This adds it to the registry so it can be found by name. When the entity is created, it will contain the names of the scripts to be added to the entity.

setTimeout(function() {
if (!self._scripts.hasOwnProperty(script.__name)) {
return;
}
var components = self.app.systems.script._components;
var i, s, scriptInstance, attributes;
var scriptInstances = [];
var scriptInstancesInitialized = [];
for (i = 0;i < components.length;i++) {
if (components[i]._scriptsIndex[script.__name] && components[i]._scriptsIndex[script.__name].awaiting) {
if (components[i]._scriptsData && components[i]._scriptsData[script.__name]) {
attributes = components[i]._scriptsData[script.__name].attributes;
}
scriptInstance = components[i].create(script.__name, {preloading:true, ind:components[i]._scriptsIndex[script.__name].ind, attributes:attributes});
if (scriptInstance) {
scriptInstances.push(scriptInstance);
}
}
}
for (i = 0;i < scriptInstances.length;i++) {
scriptInstances[i].__initializeAttributes();
}
for (i = 0;i < scriptInstances.length;i++) {
if (scriptInstances[i].enabled) {
scriptInstances[i]._initialized = true;
scriptInstancesInitialized.push(scriptInstances[i]);
if (scriptInstances[i].initialize) {
scriptInstances[i].initialize();
}
}
}
for (i = 0;i < scriptInstancesInitialized.length;i++) {
scriptInstancesInitialized[i]._postInitialized = true;
if (scriptInstancesInitialized[i].postInitialize) {
scriptInstancesInitialized[i].postInitialize();
}
}
});
This bit is interesting. By using setTimeout
with a time of 0, the code in the callback is called in the next frame.
It looks like (and I’m guessing here), is that it finds all the instances of the PlayCanvas script definition and calls initialize
and then postInitialize
.
So in terms of code flow, it would look something like this:
// Script
var Pulse = pc.createScript('pulse');
// In engine
this._scripts[script.__name] = script;
this._list.push(script);
this.fire("add", script.__name, script);
this.fire("add:" + script.__name, script);
// Back to script
Pulse.prototype.initialize = function() {
this.secsSinceStart = this.pulseTimeSecs + 1;
this.entity.on("pulse:start", this.onStartEvent, this);
};
// update code called every frame
Pulse.prototype.update = function(dt) {
this.secsSinceStart += dt;
if (this.secsSinceStart <= this.pulseTimeSecs) {
var normalisedTime = this.secsSinceStart / this.pulseTimeSecs;
var pingPongTime = Math.abs(normalisedTime -0.5) * 2;
var scale = (0.3 * pingPongTime) + 0.7;
var localScale = this.entity.getLocalScale();
localScale.set(scale, scale, scale);
this.entity.setLocalScale(localScale);
}
};
Pulse.prototype.onStartEvent = function() {
this.secsSinceStart = 0;
};
// And then on the next frame, all the timeouts are called
{
if (!self._scripts.hasOwnProperty(script.__name)) {
return;
}
var components = self.app.systems.script._components;
var i, s, scriptInstance, attributes;
var scriptInstances = [];
var scriptInstancesInitialized = [];
for (i = 0;i < components.length;i++) {
if (components[i]._scriptsIndex[script.__name] && components[i]._scriptsIndex[script.__name].awaiting) {
if (components[i]._scriptsData && components[i]._scriptsData[script.__name]) {
attributes = components[i]._scriptsData[script.__name].attributes;
}
scriptInstance = components[i].create(script.__name, {preloading:true, ind:components[i]._scriptsIndex[script.__name].ind, attributes:attributes});
if (scriptInstance) {
scriptInstances.push(scriptInstance);
}
}
}
for (i = 0;i < scriptInstances.length;i++) {
scriptInstances[i].__initializeAttributes();
}
for (i = 0;i < scriptInstances.length;i++) {
if (scriptInstances[i].enabled) {
scriptInstances[i]._initialized = true;
scriptInstancesInitialized.push(scriptInstances[i]);
if (scriptInstances[i].initialize) {
scriptInstances[i].initialize();
}
}
}
for (i = 0;i < scriptInstancesInitialized.length;i++) {
scriptInstancesInitialized[i]._postInitialized = true;
if (scriptInstancesInitialized[i].postInitialize) {
scriptInstancesInitialized[i].postInitialize();
}
}
}