@caperjs/plugin-crunch 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +205 -0
- package/lib/Actor.d.ts +227 -0
- package/lib/Actor.d.ts.map +1 -0
- package/lib/CrunchPhysicsPlugin.d.ts +508 -0
- package/lib/CrunchPhysicsPlugin.d.ts.map +1 -0
- package/lib/Entity.d.ts +377 -0
- package/lib/Entity.d.ts.map +1 -0
- package/lib/Group.d.ts +122 -0
- package/lib/Group.d.ts.map +1 -0
- package/lib/Sensor.d.ts +161 -0
- package/lib/Sensor.d.ts.map +1 -0
- package/lib/Solid.d.ts +109 -0
- package/lib/Solid.d.ts.map +1 -0
- package/lib/System.d.ts +265 -0
- package/lib/System.d.ts.map +1 -0
- package/lib/caper-plugin-crunch.mjs +1331 -0
- package/lib/caper-plugin-crunch.mjs.map +1 -0
- package/lib/index.d.ts +13 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/interfaces.d.ts +32 -0
- package/lib/interfaces.d.ts.map +1 -0
- package/lib/types.d.ts +246 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/utils.d.ts +63 -0
- package/lib/utils.d.ts.map +1 -0
- package/lib/version.d.ts +2 -0
- package/lib/version.d.ts.map +1 -0
- package/package.json +42 -0
- package/src/Actor.ts +726 -0
- package/src/CrunchPhysicsPlugin.ts +718 -0
- package/src/Entity.ts +762 -0
- package/src/Group.ts +181 -0
- package/src/Sensor.ts +427 -0
- package/src/Solid.ts +380 -0
- package/src/System.ts +1546 -0
- package/src/collision-layers.d.ts +87 -0
- package/src/index.ts +18 -0
- package/src/interfaces.ts +33 -0
- package/src/types.ts +336 -0
- package/src/utils.ts +72 -0
- package/src/version.ts +1 -0
package/src/Solid.ts
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { Actor } from './Actor';
|
|
2
|
+
import { Entity } from './Entity';
|
|
3
|
+
import { Sensor } from './Sensor';
|
|
4
|
+
import { EntityData, PhysicsEntityConfig, PhysicsEntityType } from './types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Static or moving solid object that other entities can collide with.
|
|
8
|
+
* Solids are typically used for platforms, walls, and other obstacles.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Static collision boundaries
|
|
12
|
+
* - Support for moving platforms
|
|
13
|
+
* - Carries actors and sensors that are riding it
|
|
14
|
+
* - Pushes overlapping entities out of the way
|
|
15
|
+
*
|
|
16
|
+
* @typeParam T - Application type, defaults to base Application
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* // Create a static platform
|
|
21
|
+
* const platform = physics.createSolid({
|
|
22
|
+
* type: 'Platform',
|
|
23
|
+
* position: [0, 500],
|
|
24
|
+
* size: [800, 32],
|
|
25
|
+
* view: platformSprite
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Create a moving platform
|
|
29
|
+
* const movingPlatform = physics.createSolid({
|
|
30
|
+
* type: 'Platform',
|
|
31
|
+
* position: [100, 300],
|
|
32
|
+
* size: [200, 32],
|
|
33
|
+
* view: platformSprite
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Move the platform back and forth
|
|
37
|
+
* gsap.to(movingPlatform, {
|
|
38
|
+
* x: 500,
|
|
39
|
+
* duration: 2,
|
|
40
|
+
* yoyo: true,
|
|
41
|
+
* repeat: -1
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export class Solid<D extends EntityData = EntityData> extends Entity<D> {
|
|
46
|
+
public readonly entityType: PhysicsEntityType = 'Solid';
|
|
47
|
+
/** Whether this solid should be removed when culled (typically false) */
|
|
48
|
+
public shouldRemoveOnCull = false;
|
|
49
|
+
|
|
50
|
+
/** Whether this solid can be collided with */
|
|
51
|
+
private _canCollide: boolean = true;
|
|
52
|
+
|
|
53
|
+
get canCollide(): boolean {
|
|
54
|
+
return this._canCollide;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Whether this solid has collisions enabled */
|
|
58
|
+
public collideable: boolean = true;
|
|
59
|
+
|
|
60
|
+
/** Whether this solid is currently moving */
|
|
61
|
+
public moving: boolean = false;
|
|
62
|
+
|
|
63
|
+
private _nextX: number;
|
|
64
|
+
private _nextY: number;
|
|
65
|
+
|
|
66
|
+
public setPosition(x: number, y: number): void {
|
|
67
|
+
this.x = x;
|
|
68
|
+
this.y = y;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Sets the solid's X position.
|
|
73
|
+
* For moving solids, this queues the movement to be applied on next update.
|
|
74
|
+
*/
|
|
75
|
+
public set x(value: number) {
|
|
76
|
+
this._nextX = Math.round(value);
|
|
77
|
+
this.moving = true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public get x(): number {
|
|
81
|
+
return this._x;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Sets the solid's Y position.
|
|
86
|
+
* For moving solids, this queues the movement to be applied on next update.
|
|
87
|
+
*/
|
|
88
|
+
public set y(value: number) {
|
|
89
|
+
this._nextY = Math.round(value);
|
|
90
|
+
this.moving = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public get y(): number {
|
|
94
|
+
return this._y;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Initializes or reinitializes the solid with new configuration.
|
|
99
|
+
*
|
|
100
|
+
* @param config - Configuration for the solid
|
|
101
|
+
*/
|
|
102
|
+
public init(config: PhysicsEntityConfig<D>): void {
|
|
103
|
+
super.init(config);
|
|
104
|
+
if (config) {
|
|
105
|
+
this._nextX = this._x;
|
|
106
|
+
this._nextY = this._y;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Right edge X coordinate */
|
|
111
|
+
public get right(): number {
|
|
112
|
+
return this.x + this.width;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Left edge X coordinate */
|
|
116
|
+
public get left(): number {
|
|
117
|
+
return this.x;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Top edge Y coordinate */
|
|
121
|
+
public get top(): number {
|
|
122
|
+
return this.y;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Bottom edge Y coordinate */
|
|
126
|
+
public get bottom(): number {
|
|
127
|
+
return this.y + this.height;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Checks if this solid can collide with the given entity type.
|
|
132
|
+
* This method is kept for backward compatibility.
|
|
133
|
+
*
|
|
134
|
+
* @returns Always returns true as we're now using only collision layers/masks
|
|
135
|
+
*/
|
|
136
|
+
canCollideWith(): boolean {
|
|
137
|
+
// First check if collisions are enabled at all
|
|
138
|
+
if (!this.collideable) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// We're now using only collision layers/masks, so this method is simplified
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Moves the solid by the specified amount, carrying any riding entities.
|
|
148
|
+
* Also pushes any overlapping entities out of the way.
|
|
149
|
+
*
|
|
150
|
+
* @param x - X distance to move
|
|
151
|
+
* @param y - Y distance to move
|
|
152
|
+
* @param actors - Set of actors to check for riding/pushing
|
|
153
|
+
* @param sensors - Set of sensors to check for riding/pushing
|
|
154
|
+
*/
|
|
155
|
+
public move(
|
|
156
|
+
x: number,
|
|
157
|
+
y: number,
|
|
158
|
+
actors: Set<Actor> = this.system.actors,
|
|
159
|
+
sensors: Set<Sensor> = this.system.sensors,
|
|
160
|
+
force: boolean = false,
|
|
161
|
+
): void {
|
|
162
|
+
if (!this.active) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Calculate total movement including remainder
|
|
167
|
+
const totalX = x + (this._nextX - this._x);
|
|
168
|
+
const totalY = y + (this._nextY - this._y);
|
|
169
|
+
|
|
170
|
+
this._xRemainder += totalX;
|
|
171
|
+
this._yRemainder += totalY;
|
|
172
|
+
|
|
173
|
+
const moveX = Math.round(this._xRemainder);
|
|
174
|
+
const moveY = Math.round(this._yRemainder);
|
|
175
|
+
|
|
176
|
+
if (moveX !== 0 || moveY !== 0 || force) {
|
|
177
|
+
if (this.collideable) {
|
|
178
|
+
// Get all riding actors and sensors before movement
|
|
179
|
+
const ridingActors = new Set<Actor>();
|
|
180
|
+
const ridingSensors = new Set<Sensor>();
|
|
181
|
+
|
|
182
|
+
for (const actor of actors) {
|
|
183
|
+
// Check if actor can collide with this solid based on collision layers/masks
|
|
184
|
+
if (
|
|
185
|
+
(actor.collisionLayer & this.collisionMask) !== 0 &&
|
|
186
|
+
(this.collisionLayer & actor.collisionMask) !== 0 &&
|
|
187
|
+
actor.isRiding(this)
|
|
188
|
+
) {
|
|
189
|
+
ridingActors.add(actor);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const sensor of sensors) {
|
|
194
|
+
// Check if sensor can collide with this solid based on collision layers/masks
|
|
195
|
+
if (
|
|
196
|
+
(sensor.collisionLayer & this.collisionMask) !== 0 &&
|
|
197
|
+
(this.collisionLayer & sensor.collisionMask) !== 0 &&
|
|
198
|
+
sensor.isRiding(this)
|
|
199
|
+
) {
|
|
200
|
+
ridingSensors.add(sensor);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Make this solid non-collidable so actors don't get stuck
|
|
205
|
+
this._canCollide = false;
|
|
206
|
+
|
|
207
|
+
if (moveX !== 0) {
|
|
208
|
+
this._xRemainder -= moveX;
|
|
209
|
+
this._x += moveX;
|
|
210
|
+
|
|
211
|
+
if (moveX > 0) {
|
|
212
|
+
// Moving right
|
|
213
|
+
for (const actor of actors) {
|
|
214
|
+
// Check if actor can collide with this solid based on collision layers/masks
|
|
215
|
+
if (
|
|
216
|
+
this.overlaps(actor) &&
|
|
217
|
+
(actor.collisionLayer & this.collisionMask) !== 0 &&
|
|
218
|
+
(this.collisionLayer & actor.collisionMask) !== 0
|
|
219
|
+
) {
|
|
220
|
+
// Push right
|
|
221
|
+
actor.moveX(this.right - actor.x, actor.squish, this);
|
|
222
|
+
} else if (ridingActors.has(actor)) {
|
|
223
|
+
// Carry right
|
|
224
|
+
actor.moveX(moveX);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
for (const sensor of sensors) {
|
|
228
|
+
// Check if sensor can collide with this solid based on collision layers/masks
|
|
229
|
+
if (
|
|
230
|
+
this.overlaps(sensor) &&
|
|
231
|
+
(sensor.collisionLayer & this.collisionMask) !== 0 &&
|
|
232
|
+
(this.collisionLayer & sensor.collisionMask) !== 0
|
|
233
|
+
) {
|
|
234
|
+
// Push right
|
|
235
|
+
sensor.moveX(this.right - sensor.x);
|
|
236
|
+
} else if (ridingSensors.has(sensor)) {
|
|
237
|
+
// Carry right
|
|
238
|
+
sensor.moveX(moveX);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
// Moving left
|
|
243
|
+
for (const actor of actors) {
|
|
244
|
+
// Check if actor can collide with this solid based on collision layers/masks
|
|
245
|
+
if (
|
|
246
|
+
this.overlaps(actor) &&
|
|
247
|
+
(actor.collisionLayer & this.collisionMask) !== 0 &&
|
|
248
|
+
(this.collisionLayer & actor.collisionMask) !== 0
|
|
249
|
+
) {
|
|
250
|
+
// Push left
|
|
251
|
+
actor.moveX(this.left - (actor.x + actor.width), actor.squish, this);
|
|
252
|
+
} else if (ridingActors.has(actor)) {
|
|
253
|
+
// Carry left
|
|
254
|
+
actor.moveX(moveX);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const sensor of sensors) {
|
|
258
|
+
// Check if sensor can collide with this solid based on collision layers/masks
|
|
259
|
+
if (
|
|
260
|
+
this.overlaps(sensor) &&
|
|
261
|
+
(sensor.collisionLayer & this.collisionMask) !== 0 &&
|
|
262
|
+
(this.collisionLayer & sensor.collisionMask) !== 0
|
|
263
|
+
) {
|
|
264
|
+
// Push left
|
|
265
|
+
sensor.moveX(this.left - (sensor.x + sensor.width));
|
|
266
|
+
} else if (ridingSensors.has(sensor)) {
|
|
267
|
+
// Carry left
|
|
268
|
+
sensor.moveX(moveX);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (moveY !== 0) {
|
|
275
|
+
this._yRemainder -= moveY;
|
|
276
|
+
this._y += moveY;
|
|
277
|
+
|
|
278
|
+
if (moveY > 0) {
|
|
279
|
+
// Moving down
|
|
280
|
+
for (const actor of actors) {
|
|
281
|
+
// Check if actor can collide with this solid based on collision layers/masks
|
|
282
|
+
if (
|
|
283
|
+
this.overlaps(actor) &&
|
|
284
|
+
(actor.collisionLayer & this.collisionMask) !== 0 &&
|
|
285
|
+
(this.collisionLayer & actor.collisionMask) !== 0
|
|
286
|
+
) {
|
|
287
|
+
// Push down
|
|
288
|
+
actor.moveY(this.bottom - actor.y, actor.squish, this);
|
|
289
|
+
} else if (ridingActors.has(actor)) {
|
|
290
|
+
// Carry down
|
|
291
|
+
actor.moveY(moveY);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
for (const sensor of sensors) {
|
|
295
|
+
// Check if sensor can collide with this solid based on collision layers/masks
|
|
296
|
+
if (
|
|
297
|
+
this.overlaps(sensor) &&
|
|
298
|
+
(sensor.collisionLayer & this.collisionMask) !== 0 &&
|
|
299
|
+
(this.collisionLayer & sensor.collisionMask) !== 0
|
|
300
|
+
) {
|
|
301
|
+
// Push down
|
|
302
|
+
sensor.moveY(this.bottom - sensor.y);
|
|
303
|
+
} else if (ridingSensors.has(sensor)) {
|
|
304
|
+
// Carry down
|
|
305
|
+
sensor.moveY(moveY);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} else {
|
|
309
|
+
// Moving up
|
|
310
|
+
for (const actor of actors) {
|
|
311
|
+
// Check if actor can collide with this solid based on collision layers/masks
|
|
312
|
+
if (
|
|
313
|
+
this.overlaps(actor) &&
|
|
314
|
+
(actor.collisionLayer & this.collisionMask) !== 0 &&
|
|
315
|
+
(this.collisionLayer & actor.collisionMask) !== 0
|
|
316
|
+
) {
|
|
317
|
+
// Push up
|
|
318
|
+
actor.moveY(this.top - (actor.y + actor.height), actor.squish, this);
|
|
319
|
+
} else if (ridingActors.has(actor)) {
|
|
320
|
+
// Carry up
|
|
321
|
+
actor.moveY(moveY);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const sensor of sensors) {
|
|
325
|
+
// Check if sensor can collide with this solid based on collision layers/masks
|
|
326
|
+
if (
|
|
327
|
+
this.overlaps(sensor) &&
|
|
328
|
+
(sensor.collisionLayer & this.collisionMask) !== 0 &&
|
|
329
|
+
(this.collisionLayer & sensor.collisionMask) !== 0
|
|
330
|
+
) {
|
|
331
|
+
// Push up
|
|
332
|
+
sensor.moveY(this.top - (sensor.y + sensor.height));
|
|
333
|
+
} else if (ridingSensors.has(sensor)) {
|
|
334
|
+
// Carry up
|
|
335
|
+
sensor.moveY(moveY);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Re-enable collisions
|
|
342
|
+
this._canCollide = true;
|
|
343
|
+
} else {
|
|
344
|
+
// When collisions are disabled, just move without affecting other entities
|
|
345
|
+
if (moveX !== 0) {
|
|
346
|
+
this._xRemainder -= moveX;
|
|
347
|
+
this._x += moveX;
|
|
348
|
+
}
|
|
349
|
+
if (moveY !== 0) {
|
|
350
|
+
this._yRemainder -= moveY;
|
|
351
|
+
this._y += moveY;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Update next positions to match current
|
|
356
|
+
this._nextX = this._x;
|
|
357
|
+
this._nextY = this._y;
|
|
358
|
+
|
|
359
|
+
// Update the view position
|
|
360
|
+
this.updateView();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Checks if this solid overlaps with the given entity.
|
|
366
|
+
*
|
|
367
|
+
* @param entity - Entity to check for overlap
|
|
368
|
+
* @returns True if overlapping
|
|
369
|
+
*/
|
|
370
|
+
private overlaps(entity: Actor | Sensor): boolean {
|
|
371
|
+
// Check collision layers and masks
|
|
372
|
+
// A collision occurs when (A.layer & B.mask) !== 0 && (B.layer & A.mask) !== 0
|
|
373
|
+
if ((this.collisionLayer & entity.collisionMask) === 0 || (entity.collisionLayer & this.collisionMask) === 0) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Check for AABB overlap
|
|
378
|
+
return this.system.aabbOverlap(this, entity);
|
|
379
|
+
}
|
|
380
|
+
}
|