1 | /** @module splat-ecs/lib/particles */
|
2 |
|
3 | var random = require("splat-ecs/lib/random");
|
4 |
|
5 | module.exports = {
|
6 | /**
|
7 | * Create between {@link module:splat-ecs/lib/particles.Config#qtyMin qtyMin} and {@link module:splat-ecs/lib/particles.Config#qtyMax qtyMax} particles, and randomize their properties according to <code>config</code>.
|
8 | * @param {object} game The <code>game</code> object that you get in systems and scripts.
|
9 | * @param {module:splat-ecs/lib/particles.Config} config The settings to use to create the particles.
|
10 | */
|
11 | "create": function(game, config) {
|
12 | var particleCount = Math.floor(random.inRange(config.qtyMin, config.qtyMax));
|
13 | for (var i = 0; i < particleCount; i++) {
|
14 | var particle = game.prefabs.instantiate(game.entities, config.prefab);
|
15 | // check if origin is an entity
|
16 | var origin = config.origin;
|
17 | if (typeof config.origin === "number") {
|
18 | origin = choosePointInEntity(game, origin);
|
19 | }
|
20 |
|
21 | var randomSize = random.inRange(config.sizeMin, config.sizeMax);
|
22 | scaleEntityRect(game, particle, randomSize);
|
23 |
|
24 | centerEntityOnPoint(game, particle, origin);
|
25 |
|
26 | var velocity = random.inRange(config.velocityMin, config.velocityMax);
|
27 |
|
28 | var angle = pickAngle(config, i, particleCount);
|
29 | var velocityComponent = game.entities.addComponent(particle, "velocity");
|
30 | var direction = pointOnCircle(angle, velocity);
|
31 | velocityComponent.x = direction.x;
|
32 | velocityComponent.y = direction.y;
|
33 |
|
34 | if (config.accelerationX || config.accelerationY) {
|
35 | var accel = game.entities.addComponent(particle, "acceleration");
|
36 | accel.x = config.accelerationX;
|
37 | accel.y = config.accelerationY;
|
38 | }
|
39 | var lifeSpan = game.entities.addComponent(particle, "lifeSpan");
|
40 | lifeSpan.max = random.inRange(config.lifeSpanMin, config.lifeSpanMax);
|
41 | }
|
42 | },
|
43 |
|
44 | /**
|
45 | * The settings for a type of particle.
|
46 | * @constructor
|
47 | * @param {string} prefab The name of a prefab to instantiate for the particle, as defined in <code>prefabs.json</code>.
|
48 | */
|
49 | "Config": function(prefab) {
|
50 | /**
|
51 | * The name of a prefab to instantiate for the particle, as defined in <code>prefabs.json</code>.
|
52 | * @member {string}
|
53 | */
|
54 | this.prefab = prefab;
|
55 | /**
|
56 | * The origin point in which to create particles.
|
57 | *
|
58 | * If the origin is a number it represents an entity and a random point inside the entity will be used.
|
59 | * If origin is a point like <code>{"x": 50, "y": 50}</code> particles will spawn at that position.
|
60 | * @member {object | number}
|
61 | */
|
62 | this.origin = { "x": 0, "y": 0 };
|
63 | /**
|
64 | * How to distribute particles along the {@link module:splat-ecs/lib/particles.Config#arcWidth arcWidth}.
|
65 | *
|
66 | * Possible values:
|
67 | * <dl>
|
68 | * <dt><code>"even"</code></dt>
|
69 | * <dd>Distribute the particles evenly along the arc.</dd>
|
70 | * <dt><code>"random"</code></dt>
|
71 | * <dd>Scatter the particles on random points of the arc.</dd>
|
72 | * </dl>
|
73 | * @member {string}
|
74 | */
|
75 | this.spreadType = "random";
|
76 | /**
|
77 | * The direction (an angle in radians) that the particles should move.
|
78 | * @member {number}
|
79 | */
|
80 | this.angle = 0;
|
81 | /**
|
82 | * The width of an arc (represented by an angle in radians) to spread the particles. The arc is centered around {@link module:splat-ecs/lib/particles.Config#angle angle}.
|
83 | * @member {number}
|
84 | */
|
85 | this.arcWidth = Math.PI / 2;
|
86 | /**
|
87 | * The minimum number of particles to create.
|
88 | * @member {number}
|
89 | */
|
90 | this.qtyMin = 1;
|
91 | /**
|
92 | * The maximum number of particles to create.
|
93 | * @member {number}
|
94 | */
|
95 | this.qtyMax = 1;
|
96 | /**
|
97 | * The minimum percentage to scale each particle.
|
98 | * <ul>
|
99 | * <li>A scale of 0.5 means the particle will spawn at 50% (half) of the original size.</li>
|
100 | * <li>A scale of 1 means the particle will spawn at the original size.</li>
|
101 | * <li>A scale of 2 means the particle will spawn at 200% (double) the original size.</li>
|
102 | * </ul>
|
103 | * @member {number}
|
104 | */
|
105 | this.sizeMin = 1;
|
106 | /**
|
107 | * The maximum percentage to scale each particle.
|
108 | * <ul>
|
109 | * <li>A scale of 0.5 means the particle will spawn at 50% (half) of the original size.</li>
|
110 | * <li>A scale of 1 means the particle will spawn at the original size.</li>
|
111 | * <li>A scale of 2 means the particle will spawn at 200% (double) the original size.</li>
|
112 | * </ul>
|
113 | * @member {number}
|
114 | */
|
115 | this.sizeMax = 1;
|
116 | /**
|
117 | * The minimum velocity to apply to each particle.
|
118 | * @member {number}
|
119 | */
|
120 | this.velocityMin = 0.5;
|
121 | /**
|
122 | * The maximum velocity to apply to each particle.
|
123 | * @member {number}
|
124 | */
|
125 | this.velocityMax = 0.5;
|
126 | /**
|
127 | * The acceleration on the x-axis to apply to each particle.
|
128 | * @member {number}
|
129 | */
|
130 | this.accelerationX = 0;
|
131 | /**
|
132 | * The acceleration on the y-axis to apply to each particle.
|
133 | * @member {number}
|
134 | */
|
135 | this.accelerationY = 0;
|
136 | /**
|
137 | * The minimum life span to apply to each particle.
|
138 | * @member {number}
|
139 | */
|
140 | this.lifeSpanMin = 0;
|
141 | /**
|
142 | * The maximum life span to apply to each particle.
|
143 | * @member {number}
|
144 | */
|
145 | this.lifeSpanMax = 500;
|
146 | }
|
147 | };
|
148 |
|
149 | function pickAngle(config, particleNumber, particleCount) {
|
150 | var startAngle = config.angle - (config.arcWidth / 2);
|
151 | if (config.spreadType === "even") {
|
152 | return (particleNumber * (config.arcWidth / (particleCount - 1))) + startAngle;
|
153 | } else {
|
154 | var endAngle = startAngle + config.arcWidth;
|
155 | return random.inRange(startAngle, endAngle);
|
156 | }
|
157 | }
|
158 |
|
159 | function scaleEntityRect(game, entity, scaleFactor) {
|
160 | var size = game.entities.getComponent(entity, "size");
|
161 | size.width = size.width * scaleFactor;
|
162 | size.height = size.height * scaleFactor;
|
163 | }
|
164 |
|
165 | function pointOnCircle(angle, radius) {
|
166 | return {
|
167 | "x": (radius * Math.cos(angle)),
|
168 | "y": (radius * Math.sin(angle))
|
169 | };
|
170 | }
|
171 |
|
172 | /**
|
173 | * Center an entity on a given point.
|
174 | * @private
|
175 | * @param {object} game Required for game.entities.get().
|
176 | * @param {integer} entity The id of entity to center.
|
177 | * @param {object} point A point object <code>{"x": 50, "y": 50}</code> on which to center the entity.
|
178 | */
|
179 | function centerEntityOnPoint(game, entity, point) {
|
180 | var size = game.entities.getComponent(entity, "size");
|
181 | var position = game.entities.addComponent(entity, "position");
|
182 | position.x = point.x - (size.width / 2);
|
183 | position.y = point.y - (size.height / 2);
|
184 | }
|
185 |
|
186 | /**
|
187 | * Choose a random point inside the bounding rectangle of an entity.
|
188 | * @private
|
189 | * @param {object} game Required for game.entities.get().
|
190 | * @param {integer} entity The id of entity to pick a point within.
|
191 | * @returns {object} an point object <code>{"x": 50, "y": 50}</code>.
|
192 | */
|
193 | function choosePointInEntity(game, entity) {
|
194 | var position = game.entities.getComponent(entity, "position");
|
195 | var size = game.entities.getComponent(entity, "size");
|
196 | if (size === undefined) {
|
197 | return {
|
198 | "x": position.x,
|
199 | "y": position.y
|
200 | };
|
201 | }
|
202 | return {
|
203 | "x": random.inRange(position.x, (position.x + size.width)),
|
204 | "y": random.inRange(position.y, (position.y + size.height))
|
205 | };
|
206 | }
|