Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*:
- * @plugindesc v1.04 Extensible particle engine, that creates effects by applying behaviours to particle objects.
- * @author Iavra
- *
- * Changelog:
- * - 1.01: The engine has been revamped be reintroducing behaviours.
- * - 1.02: Moved particle aging from behaviours into the emitter.
- * - 1.03: Introduced activities, which run during each update, but before particles are updated.
- * - 1.04: Added a way for behaviours to store and read data on a particle.
- *
- * @help
- * The following script call creates a sample emitter, that displays a bunch of particles wandering around the screen:
- *
- * var emitter = new IAVRA.PARTICLE.Emitter()
- * .setCounter({
- * start: function(emitter) {},
- * update: function(emitter) { return 1; }
- * })
- * .addBehaviour({
- * setup: function(emitter, particle) {
- * particle.texture = PIXI.Texture.fromImage('img/particle.png');
- * particle.position.set(Graphics.width / 2, Graphics.height / 2);
- * particle.life = 200;
- * },
- * update: function(emitter, particle, index) {
- * particle.velocity.x += Math.random() - 0.5;
- * particle.velocity.y += Math.random() - 0.5;
- * }
- * });
- *
- * As you can see, after creating the emitter itself, we are adding two properties to it: A counter and a behaviour.
- *
- * Counters regulate the emission rate and instruct the emitter, how many new particles it has to create at any given
- * time. In our example, we create 1 new particle every frame and no extra particles, when the emitter starts. Each
- * emitter can only have 1 counter and defaults to a dummy instance, that causes no particles to be created.
- *
- * Behaviours control, how particles are created and updated, how the look and interact with each other and their
- * surroundings. They are powerful, but can get very complex. In our example, we assign a texture to the particle and
- * set its starting position to the center of the screen. During each update, it will accelerate in a random direction
- * and increase its age counter by 1, before finally dying after 200 updates. Each emitter can have any number of
- * behaviours assigned and they are executed in the order they were added.
- *
- * Now, the only thing left to do is to start the emitter and add it to the scene. The Emitter class contains a number
- * of functions to interact with emitters and each of them returns the emitter itself, so they can be chained:
- *
- * emitter.start(); Starts the emitter. Before calling this function, it will simply do nothing.
- * emitter.stop(); Stops the emitter, causing all particle creation and updating to stop.
- * emitter.resume(); Resumes the emitter, after it has previously been stopped.
- * emitter.skip(count); Fast forwards a number of update cycles. Creates and updates particles accordingly.
- * emitter.clear(); Removes all current particles from the emitter.
- * emitter.setCounter(counter); Sets the counter to be used. Needs to implement "start" and "update" functions.
- * emitter.setFactory(factory); Replaces the default particle factory. Needs to implement "create" and "dispose".
- * emitter.addBehaviour(...b); Adds one or more behaviours to the emitter.
- * emitter.removeBehaviour(b); Removes all occurences of a given behaviour from the emitter.
- * emitter.removeAllBehaviours(); Removes all behaviours from the emitter.
- *
- * Adding or removing behaviours while the emitter is running, affects both new and existing particles, so keep that in
- * mind. Inside a behaviour, the following properties of the particle can be accessed:
- *
- * texture (PIXI.Texture) Image to be displayed for the particle. If this is not set, you won't see anything.
- * life (number) The particle's lifespan. Defaults to Infinity.
- * age (number) Read-only. Starts at 0 and can be used to track the particle's age.
- * energy (number) Starts at 1 and is used by some behaviours to apply transformation effects.
- * dead (boolean) If this gets set to true, the particle will be disposed by the emitter.
- * oldPosition (PIXI.Point) Read-only. Gets set to the particle's position before the last update.
- * position (PIXI.Point) The particle's current position. Automatically gets updated every update.
- * velocity (PIXI.Point) Marks the rate, at which the particle's position changes.
- * scale (PIXI.Point) Scaling factor of the particle. Its x and y value should be kept synchron.
- * radius (number) Gets used by some behaviours to implement collision.
- * alpha (number) Handles the particle's transparency on a scale from 0 (transparent) to 1 (opaque).
- * rotation (number) The particle's rotation around its center point, in radians.
- * tint (number) Tinting color of the particle, as hex number. 0xFFFFFF removes all tinting.
- * blendMode (number) Blend mode to be used. Support variies between renderers. Default is 0.
- */
- (function($, undefined) {
- "use strict";
- /**
- * Used to assign unique id values to behaviours, when they are added to an emitter.
- */
- var _behaviourId = 0;
- /**
- * Extends a given object with all properties from another given object. Mainly used to simplify subclassing.
- * @param {Object} base - Base object to be extended.
- * @param {Object} extend - Object containing the properties to be appended to the given base object.
- * @returns {Object} The base object, after it has been extended.
- */
- var _extend = function(base, extend) {
- for(var key in extend) { base[key] = extend[key]; }
- return base;
- };
- //=============================================================================
- // IAVRA.PARTICLE
- //=============================================================================
- $.PARTICLE = {
- Particle: function() { this._initialize(); },
- Factory: function() { this._initialize(); },
- Emitter: function() { this._initialize(); },
- Counter: function() {},
- Behaviour: function() {},
- Zone: function() {}
- };
- //=============================================================================
- // IAVRA.PARTICLE.Particle
- //=============================================================================
- /**
- * In this engine, the particle only serves as a data holder and visual object, instead of managing its own update
- * function. This also means, that you should never need to extend this class.
- */
- $.PARTICLE.Particle.prototype = _extend(Object.create(PIXI.Sprite.prototype), {
- /**
- * Initializes the particle. Since particles are reused by the factory, this is usually only called once and
- * the main initialization happens inside the "reset" function.
- */
- _initialize: function() {
- PIXI.Sprite.call(this);
- this.oldPosition = new PIXI.Point();
- this.velocity = new PIXI.Point();
- this.reset();
- },
- /**
- * Resets the particle to its default state. This is called, whenever a particle is disposed and pushed back
- * inside the factory's particle pool, so it can be reused later.
- */
- reset: function() {
- this.anchor.x = this.anchor.y = 0.5;
- this.pivot.x = this.pivot.y = 0.5;
- this.texture = PIXI.Texture.emptyTexture;
- this.life = Infinity;
- this.age = 0;
- this.energy = 1;
- this.dead = false;
- this.oldPosition.x = this.oldPosition.y = 0;
- this.position.x = this.position.y = 0;
- this.velocity.x = this.velocity.y = 0;
- this.scale.x = this.scale.y = 1;
- this.radius = 1;
- this.alpha = 1;
- this.rotation = 0;
- this.tint = 0xFFFFFF;
- this.blendMode = PIXI.blendModes.NORMAL;
- this._behaviourData = {};
- },
- /**
- * Can be used by behaviours to store data inside the particle. Behaviours are uniquely identified by an id,
- * that gets assigned to them, when they are added to an emitter.
- * @param {IAVRA.PARTICLE.Behaviour} behaviour - The behaviour, that wants to store data.
- * @param {(string|number)} key - Key used to store the given data.
- * @param {*} value - Data, that should be stored inside the particle.
- * @returns {*} The value, that has been set.
- */
- setData: function(behaviour, key, value) {
- return this._behaviourData[behaviour.__id + ':' + key] = value;
- },
- /**
- * Can be used by behaviours to retrieve data, that has previously been stored inside the particle. Behaviours
- * are uniquely identified by an id, that gets assigned to them, when they are added to an emitter.
- * @param {IAVRA.PARTICLE.Behaviour} behaviour - The behaviour, that wants to read data.
- * @param {(string|number)} key - Key used to read data.
- * @returns {*} The requested data.
- */
- getData: function(behaviour, key) {
- return this._behaviourData[behaviour.__id + ':' + key];
- }
- });
- //=============================================================================
- // IAVRA.PARTICLE.Factory
- //=============================================================================
- /**
- * Manages the creation and disposing of particles. Disposed ones are kept within the particle pool, so they can
- * be reused later.
- */
- $.PARTICLE.Factory.prototype = {
- /**
- * Initializes the factory and creates an empty particle pool.
- */
- _initialize: function() {
- this._pool = [];
- },
- /**
- * When requested by the emitter, the factory tries to reuse an existing particle first. If the pool is empty,
- * a new one is created and returned, instead.
- * @returns {IAVRA.PARTICLE.Particle} A particle instance to be used by the emitter.
- */
- create: function() {
- return this._pool.length ? this._pool.pop() : new $.PARTICLE.Particle();
- },
- /**
- * Resets the given particle and pushes it back into the particle pool, so it can be reused later.
- * @param {IAVRA.PARTICLE.Particle} Particle instance, that should be disposed.
- */
- dispose: function(particle) {
- particle.reset();
- this._pool.push(particle);
- },
- /**
- * Clears the particle pool. It will automatically get cleared, once the emitter (and the contained factory)
- * gets disposed, but you might want to call this, if you need to free memory.
- */
- clear: function() {
- this._pool.length = 0;
- }
- };
- //=============================================================================
- // IAVRA.PARTICLE.Emitter
- //=============================================================================
- /**
- * The emitter sits at the heart of the engine and handles emitter creation, update and disposing. It also serves
- * as a container for all created particles, so it can easily be added to the scene.
- */
- $.PARTICLE.Emitter.prototype = _extend(Object.create(PIXI.DisplayObjectContainer.prototype), {
- /**
- * Initalizes the emitter, creating default instances for the internal counter and factory.
- */
- _initialize: function() {
- PIXI.DisplayObjectContainer.call(this);
- this._counter = { start: function() {}, update: function() {} };
- this._factory = new $.PARTICLE.Factory();
- this._activities = [];
- this._initializers = [];
- this._behaviours = [];
- this._started = this._running = this._reverse = false;
- },
- /**
- * Sets the counter used to control this emitter's emission rate. A counter needs to implement the functions
- * "start" and "update". For more information, take a look at the documentation for IAVRA.PARTICLE.Counter.
- * @param {IAVRA.PARTICLE.Counter} counter - Counter instance to be used by the emitter.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- setCounter: function(counter) {
- this._counter = counter;
- return this;
- },
- /**
- * Sets the factory used to create and dispose particle instances. A factory need to implement the functions
- * "create" and "dispose". For more information, take a look at the documentation for IAVRA.PARTICLE.Factory.
- * @param {IAVRA.PARTICLE.Factory} factory - Factory instance to be used by the emitter.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- setFactory: function(factory) {
- this._factory = factory;
- return this;
- },
- /**
- * Adds one or more behaviours to the emitter. Behaviours control how particles are created and updated. Also,
- * each behaviour gets assigned a unique id, if it doesn't already exist. This is used inside the particle
- * class to provide a way for behaviours to store data. For more information, take a look at the documentation
- * for IAVRA.PARTICLE.Behaviour.
- * @param {IAVRA.PARTICLE.Behaviour[]} behaviour - One ore more behaviour objects to register.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- addBehaviour: function(behaviour) {
- for(var i = 0, b; b = arguments[i++]; ) {
- if(b.__id === undefined) { b.__id = _behaviourId++; }
- if(b.activity) { this._activities.push(b); }
- if(b.setup) { this._initializers.push(b); }
- if(b.update) { this._behaviours.push(b); }
- }
- return this;
- },
- /**
- * Removes all instances of a given Behaviour object from this emitter.
- * @param {IAVRA.PARTICLE.Behaviour} behaviour - Behaviour to be removed from this emitter.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- removeBehaviour: function(behaviour) {
- for(var i; (i = this._activities.indexOf(behaviour)) > -1; ) { this._activities.splice(i, 1); }
- for(var i; (i = this._initializers.indexOf(behaviour)) > -1; ) { this._initializers.splice(i, 1); }
- for(var i; (i = this._behaviours.indexOf(behaviour)) > -1; ) { this._behaviours.splice(i, 1); }
- return this;
- },
- /**
- * Removes all Behaviour objects from this emitter.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- removeAllBehaviours: function() {
- this._activities.length = this._initializers.length = this._behaviours.length = 0;
- return this;
- },
- /**
- * Starts the emitter, which causes it to create and update particles during update cycles. Calls the internal
- * counter's "start" function to determine, how many particles should be created on startup.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- start: function() {
- if(this._started) { return this; }
- this._started = this._running = true;
- for(var i = 0, max = this._counter.start(this)|0; i < max; ++i) { this._createParticle(); }
- return this;
- },
- /**
- * Stops the emitter (temporarily), causing it to not create or update particles anymore, until its "resume"
- * function gets called.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- stop: function() {
- this._running = false;
- return this;
- },
- /**
- * Resumes emitter execution, after it has previously been stopped.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- resume: function() {
- if(this._started) { this._running = true; }
- return this;
- },
- /**
- * Causes the emitter to fast forward a given number of update cycles, making it appear as though it has been
- * running for a while.
- * @param {number} count - How many updates should be executed.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- skip: function(count) {
- for(var i = 0; i < count; ++i) { this.update(); }
- return this;
- },
- /**
- * Instantly destroys all current particles on the emitter. Does not stop new particles from being created.
- * @returns {IAVRA.PARTICLE.Emitter} This object, to allow chaining.
- */
- clear: function() {
- for(var i = this.children.length; i--; ) { this._disposeParticle(i); }
- return this;
- },
- /**
- * Updates the emitter, causing it to create, update and dispose particles as instructed by the given counter
- * and behaviours. The loop direction changes between each update, to iron out possible errors.
- */
- update: function() {
- if(!this._running) { return; }
- for(var i = 0, max = this._counter.update(this)|0; i < max; ++i) { this._createParticle(); }
- for(var i = 0, behaviour; behaviour = this._activities[i++]; ) { behaviour.activity(this); }
- if(this.children.length) {
- var length = this.children.length, i;
- if(this._reverse ^= true) {
- for(i = length; i--; ) { this._updateParticle(this.children[i], i); }
- } else {
- for(i = 0; i < length; ++i) { this._updateParticle(this.children[i], i); }
- }
- for(i = length; i--; ) { if(this.children[i].dead) { this._disposeParticle(i); } }
- }
- },
- /**
- * Creates a new particle, initializes it according to the registered behaviours and adds it to the emitter.
- */
- _createParticle: function() {
- var particle = this._factory.create();
- for(var i = 0, behaviour; behaviour = this._initializers[i++]; ) { behaviour.setup(this, particle); }
- this.addChild(particle);
- },
- /**
- * Updates the given particle by applying registered behaviours to it, increasing its age and incrementing its
- * position according to its velocity. Particles, that have met the end of their lifetime are marked as dead,
- * so they are removed during the next step.
- * @param {IAVRA.PARTICLE.Particle} particle - Particle instance to be udpdated.
- * @param {number} index - The particle's position inside the emitter's children array.
- */
- _updateParticle: function(particle, index) {
- if(++particle.age > particle.life) { particle.dead = true; }
- for(var i = 0, behaviour; behaviour = this._behaviours[i++]; ) { behaviour.update(this, particle, index); }
- particle.oldPosition.x = particle.position.x;
- particle.oldPosition.y = particle.position.y;
- particle.position.x += particle.velocity.x;
- particle.position.y += particle.velocity.y;
- },
- /**
- * Destroys a particle, by removing it from the emitter and giving it to the factory, so it can be disposed.
- * @param {number} index - The particle's position inside the emitter's children array.
- */
- _disposeParticle: function(index) {
- this._factory.dispose(this.removeChildAt(index));
- }
- });
- //=============================================================================
- // IAVRA.PARTICLE.Counter
- //=============================================================================
- /**
- * A counter object instructs the emitter, when and how many new particles it has to create. It can do so either
- * when the emitter gets started or at the beginning of every update cycle.
- */
- $.PARTICLE.Counter.prototype = {
- /**
- * Gets called, when the emitter is started. Can specify any number of particles to be created at this point.
- * @param {IAVRA.PARTICLE.Emitter} emitter - The enclosing emitter instance.
- * @returns {number|*} Any return value of this function will be treated as an integer by applying a binary
- * OR to it. If this value is higher than 0, the emitter will create that many particles.
- */
- start: function(emitter) {},
- /**
- * Gets called, whenever the emitter updates. Can specify any number of particles to be created at this point.
- * @param {IAVRA.PARTICLE.Emitter} emitter - The enclosing emitter instance.
- * @returns {number|*} Any return value of this function will be treated as an integer by applying a binary
- * OR to it. If this value is higher than 0, the emitter will create that many particles.
- */
- update: function(emitter) {}
- };
- //=============================================================================
- // IAVRA.PARTICLE.Behaviour
- //=============================================================================
- /**
- * A behaviour object modifies the way particles are created and updated. It can define both a setup and/or update
- * function and will automatically be called at the appropiate times.
- */
- $.PARTICLE.Behaviour.prototype = {
- /**
- * Gets called, whenever a new particle is created. If this is not a function, it will be skipped.
- * @param {IAVRA.PARTICLE.Emitter} emitter - Emitter instance, that created the particle.
- * @param {IAVRA.PARTICLE.Particle} particle - A new particle, that has just been created by the emitter.
- */
- setup: null,
- /**
- * Gets called on every update cycle, for each particle. If this is not a function, it will be skipped.
- * @param {IAVRA.PARTICLE.Emitter} emitter - Emitter instance containing the given particle.
- * @param {IAVRA.PARTICLE.Particle} particle - Particle to be updated.
- * @param {number} index - The given particle's position inside the given emitter's children array.
- */
- update: null,
- /**
- * Gets called on every update cycle, before particles are updated. If this is not a function, it will be
- * skipped.
- * @param {IAVRA.PARTICLE.Emitter} emitter - Emitter instance containing this behaviour.
- */
- activity: null
- };
- //=============================================================================
- // IAVRA.PARTICLE.Zone
- //=============================================================================
- /**
- * A zone object defines a shape (like a point, line or circle), that can be used by behaviours to create effects
- * revolving around it.
- */
- $.PARTICLE.Zone.prototype = {
- /**
- * Tests, whether a given point is located inside the zone.
- * @param {number} x - X coordinate of the point to be tested.
- * @param {number} y - Y coordinate of the point to be tested.
- * @returns {boolean} True, if the given coordinates are located inside the zone. False, otherwise.
- */
- contains: function(x, y) {},
- /**
- * Returns a random point inside the zone. This can be used to create a random particle spread inside an area.
- * @returns {PIXI.Point} A random point inside this zone.
- */
- getPoint: function() {},
- /**
- * Returns the area of this zone. This may be used to balance multiple zones in regard to their areas.
- * @returns {number} This zone's area.
- */
- getArea: function() {},
- /**
- * Handles collision between the given particle and this zone.
- * @param {IAVRA.PARTICLE.Particle} particle - Particle instance to test for collision with this zone.
- * @param {number} [bounce=1] - Bounce coefficient. Values between 0 and 1 cause the particle to lose energy,
- * while values higher than 1 cause it to gain energy. Values lower than 0 might lead to unpredictable results.
- * @returns {boolean} True, if a collision was handled. False, otherwise.
- */
- collide: function(particle, bounce) {}
- };
- })(this.IAVRA || (this.IAVRA = {}));
Add Comment
Please, Sign In to add comment