q5play 4.1.0 → 4.1.2
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/package.json +2 -2
- package/q5play.d.ts +18 -19
- package/q5play.js +88 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "q5play",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"author": "quinton-ashley",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"description": "A beginner friendly, web-based game engine that uses q5.js WebGPU for graphics and Box2D v3 WASM for physics.",
|
|
@@ -25,6 +25,6 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"box2d3-wasm": "^5.2.0",
|
|
28
|
-
"q5": "^4.6.
|
|
28
|
+
"q5": "^4.6.6"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/q5play.d.ts
CHANGED
|
@@ -105,6 +105,7 @@ declare global {
|
|
|
105
105
|
type: string;
|
|
106
106
|
geom: any;
|
|
107
107
|
density: number;
|
|
108
|
+
applyWind(speed: number, angle: number, drag?: number, lift?: number): void;
|
|
108
109
|
scaleBy(scaleX: number, scaleY?: number): void;
|
|
109
110
|
delete(): void;
|
|
110
111
|
}
|
|
@@ -678,11 +679,7 @@ declare global {
|
|
|
678
679
|
get rotationDrag(): number;
|
|
679
680
|
set rotationDrag(val: number);
|
|
680
681
|
/**
|
|
681
|
-
* Known issue, this doesn't work yet.
|
|
682
|
-
* https://github.com/q5play/q5play/issues/36
|
|
683
|
-
*
|
|
684
682
|
* If true, the sprite can not rotate.
|
|
685
|
-
* @deprecated
|
|
686
683
|
* @default false
|
|
687
684
|
*/
|
|
688
685
|
get rotationLock(): boolean;
|
|
@@ -870,6 +867,14 @@ declare global {
|
|
|
870
867
|
* @param n The point the force is applied from, relative to the sprite's center of mass. Accepts a coordinate array or object with x and y properties. If not given, the force is applied at the center of mass.
|
|
871
868
|
*/
|
|
872
869
|
applyForceScaled(amount: number, origin?: any): void;
|
|
870
|
+
/**
|
|
871
|
+
* Applies wind force to the sprite.
|
|
872
|
+
* @param strength the strength of the wind
|
|
873
|
+
* @param angle the angle the wind is blowing at
|
|
874
|
+
* @param drag the force that opposes the relative velocity
|
|
875
|
+
* @param lift the force that is perpendicular to the relative velocity
|
|
876
|
+
*/
|
|
877
|
+
applyWind(strength: number, angle: number, drag?: number, lift?: number): void;
|
|
873
878
|
/**
|
|
874
879
|
* Applies a force to the sprite's center of mass attracting it to
|
|
875
880
|
* the given position.
|
|
@@ -1521,10 +1526,7 @@ declare global {
|
|
|
1521
1526
|
*/
|
|
1522
1527
|
rotationDrag: number;
|
|
1523
1528
|
/**
|
|
1524
|
-
* Known issue, this doesn't work.
|
|
1525
|
-
*
|
|
1526
1529
|
* If true, the group sprites can not rotate.
|
|
1527
|
-
* @deprecated
|
|
1528
1530
|
*/
|
|
1529
1531
|
rotationLock: boolean;
|
|
1530
1532
|
/**
|
|
@@ -1732,6 +1734,7 @@ declare global {
|
|
|
1732
1734
|
passes(target: Group): void;
|
|
1733
1735
|
applyForce(amount: number, origin?: [] | { x: number; y: number } | Q5.Vector): void;
|
|
1734
1736
|
applyForceScaled(amount: number, origin?: [] | { x: number; y: number } | Q5.Vector): void;
|
|
1737
|
+
applyWind(speed: number, angle: number, drag?: number, lift?: number): void;
|
|
1735
1738
|
attractTo(x: number | any, y?: number, force?: number): void;
|
|
1736
1739
|
applyTorque(torque: any): void;
|
|
1737
1740
|
moveTowards(x: number | any, y?: number, tracking?: number): void;
|
|
@@ -1915,8 +1918,7 @@ declare global {
|
|
|
1915
1918
|
set allowSleeping(val: boolean);
|
|
1916
1919
|
/**
|
|
1917
1920
|
* Finds the first sprite (with a physics body) that
|
|
1918
|
-
* intersects a ray (line)
|
|
1919
|
-
* with the starting point.
|
|
1921
|
+
* intersects a ray (line).
|
|
1920
1922
|
*
|
|
1921
1923
|
* @param startPos starting position of the ray cast
|
|
1922
1924
|
* @param direction direction of the ray
|
|
@@ -1926,14 +1928,13 @@ declare global {
|
|
|
1926
1928
|
rayCast(startPos: any, direction: number, maxDistance: number): Sprite;
|
|
1927
1929
|
/**
|
|
1928
1930
|
* Finds sprites (with physics bodies) that intersect
|
|
1929
|
-
* a line (ray)
|
|
1930
|
-
* starting point.
|
|
1931
|
+
* a line (ray).
|
|
1931
1932
|
*
|
|
1932
1933
|
* @param startPos starting position of the ray cast
|
|
1933
1934
|
* @param direction direction of the ray
|
|
1934
1935
|
* @param maxDistance max distance the ray should check
|
|
1935
|
-
* @param limiter
|
|
1936
|
-
* @returns An array of sprites that the ray cast hit, sorted by distance. The sprite closest to the starting point will be at index 0.
|
|
1936
|
+
* @param limiter callback that's run each time the ray intersects a sprite, receives an intersected sprite as an input parameter, return true to stop the ray
|
|
1937
|
+
* @returns An array of sprites that the ray cast hit, sorted by distance. The sprite closest to the starting point will be at index 0. If a limiter is provided, this array includes the sprite that caused the ray to stop.
|
|
1937
1938
|
*/
|
|
1938
1939
|
rayCastAll(startPos: any, direction: number, maxDistance: number, limiter?: Function): Sprite[];
|
|
1939
1940
|
/**
|
|
@@ -2172,10 +2173,12 @@ declare global {
|
|
|
2172
2173
|
set limitsEnabled(val: boolean);
|
|
2173
2174
|
/**
|
|
2174
2175
|
* The minimum length allowed when limits are enabled.
|
|
2176
|
+
* @readonly
|
|
2175
2177
|
*/
|
|
2176
2178
|
get minLength(): number;
|
|
2177
2179
|
/**
|
|
2178
2180
|
* The maximum length allowed when limits are enabled.
|
|
2181
|
+
* @readonly
|
|
2179
2182
|
*/
|
|
2180
2183
|
get maxLength(): number;
|
|
2181
2184
|
/**
|
|
@@ -2354,12 +2357,12 @@ declare global {
|
|
|
2354
2357
|
set limitsEnabled(val: boolean);
|
|
2355
2358
|
/**
|
|
2356
2359
|
* The lower limit of rotation.
|
|
2357
|
-
* @
|
|
2360
|
+
* @readonly
|
|
2358
2361
|
*/
|
|
2359
2362
|
get minAngle(): number;
|
|
2360
2363
|
/**
|
|
2361
2364
|
* The upper limit of rotation.
|
|
2362
|
-
* @
|
|
2365
|
+
* @readonly
|
|
2363
2366
|
*/
|
|
2364
2367
|
get maxAngle(): number;
|
|
2365
2368
|
/**
|
|
@@ -2453,10 +2456,6 @@ declare global {
|
|
|
2453
2456
|
* or an array with the lower and upper translation limits.
|
|
2454
2457
|
*/
|
|
2455
2458
|
set range(val: [number, number] | number);
|
|
2456
|
-
/**
|
|
2457
|
-
* Alias for range.
|
|
2458
|
-
*/
|
|
2459
|
-
set limits(val: [number, number] | number);
|
|
2460
2459
|
/**
|
|
2461
2460
|
* Whether spring behavior is enabled.
|
|
2462
2461
|
* @default false
|
package/q5play.js
CHANGED
|
@@ -85,6 +85,7 @@ async function q5playPreSetup(q) {
|
|
|
85
85
|
b2World_OverlapShape,
|
|
86
86
|
b2World_CastRay,
|
|
87
87
|
b2World_CastRayClosest,
|
|
88
|
+
b2World_CastShape,
|
|
88
89
|
b2World_SetCustomFilterCallback,
|
|
89
90
|
b2World_SetPreSolveCallback,
|
|
90
91
|
b2World_GetGravity,
|
|
@@ -381,7 +382,10 @@ async function q5playPreSetup(q) {
|
|
|
381
382
|
$.imageMode($.CENTER);
|
|
382
383
|
|
|
383
384
|
const ZERO_VEC = new b2Vec2(0, 0),
|
|
384
|
-
ZERO_ROT = b2MakeRot(0)
|
|
385
|
+
ZERO_ROT = b2MakeRot(0),
|
|
386
|
+
NULL_FILTER = new b2QueryFilter();
|
|
387
|
+
NULL_FILTER.categoryBits = 0xffffffff;
|
|
388
|
+
NULL_FILTER.maskBits = 0xffffffff;
|
|
385
389
|
|
|
386
390
|
let meterSize = 60;
|
|
387
391
|
|
|
@@ -535,6 +539,12 @@ async function q5playPreSetup(q) {
|
|
|
535
539
|
this._density = val;
|
|
536
540
|
b2Shape_SetDensity(this.id, val);
|
|
537
541
|
}
|
|
542
|
+
|
|
543
|
+
applyWind(strength, angle, drag = 0, lift = 0) {
|
|
544
|
+
const wind = new b2Vec2(strength * $.cos(angle), strength * $.sin(angle));
|
|
545
|
+
b2Shape_ApplyWind(this.id, wind, drag, lift, true);
|
|
546
|
+
wind.delete();
|
|
547
|
+
}
|
|
538
548
|
};
|
|
539
549
|
|
|
540
550
|
const Sensor = class extends Shape {
|
|
@@ -2050,17 +2060,15 @@ async function q5playPreSetup(q) {
|
|
|
2050
2060
|
}
|
|
2051
2061
|
set rotationLock(val) {
|
|
2052
2062
|
if (this.watch) this.mod[25] = true;
|
|
2063
|
+
this._rotationLock = val;
|
|
2064
|
+
if (!this._physicsEnabled) return;
|
|
2053
2065
|
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
// TODO: not working, shape is ignored by physics sim after this
|
|
2057
|
-
let locks = new b2MotionLocks();
|
|
2066
|
+
const locks = new b2MotionLocks();
|
|
2058
2067
|
locks.linearX = false;
|
|
2059
2068
|
locks.linearY = false;
|
|
2060
2069
|
locks.angularZ = val;
|
|
2061
2070
|
b2Body_SetMotionLocks(this.bdID, locks);
|
|
2062
|
-
|
|
2063
|
-
// this.mass = mass;
|
|
2071
|
+
locks.delete();
|
|
2064
2072
|
}
|
|
2065
2073
|
|
|
2066
2074
|
get rotationSpeed() {
|
|
@@ -2760,6 +2768,14 @@ async function q5playPreSetup(q) {
|
|
|
2760
2768
|
b2Body_ApplyTorque(this.bdID, val, true);
|
|
2761
2769
|
}
|
|
2762
2770
|
|
|
2771
|
+
applyWind(strength, angle, drag = 0, lift = 0) {
|
|
2772
|
+
const wind = new b2Vec2(strength * $.cos(angle), strength * $.sin(angle));
|
|
2773
|
+
for (let shape of this._shapes) {
|
|
2774
|
+
b2Shape_ApplyWind(shape.id, wind, drag, lift, true);
|
|
2775
|
+
}
|
|
2776
|
+
wind.delete();
|
|
2777
|
+
}
|
|
2778
|
+
|
|
2763
2779
|
angleTo(x, y) {
|
|
2764
2780
|
if (typeof x == 'object') {
|
|
2765
2781
|
y = x.y;
|
|
@@ -4464,6 +4480,16 @@ async function q5playPreSetup(q) {
|
|
|
4464
4480
|
}
|
|
4465
4481
|
}
|
|
4466
4482
|
|
|
4483
|
+
applyWind(strength, angle, drag = 0, lift = 0) {
|
|
4484
|
+
const wind = new b2Vec2(strength * $.cos(angle), strength * $.sin(angle));
|
|
4485
|
+
for (let s of this) {
|
|
4486
|
+
for (let shape of s._shapes) {
|
|
4487
|
+
b2Shape_ApplyWind(shape.id, wind, drag, lift, true);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
wind.delete();
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4467
4493
|
_resetCentroid() {
|
|
4468
4494
|
let x = 0;
|
|
4469
4495
|
let y = 0;
|
|
@@ -4784,7 +4810,7 @@ async function q5playPreSetup(q) {
|
|
|
4784
4810
|
$.Visuals.prototype.addAni = $.Group.prototype.addAni = $.Sprite.prototype.addAni;
|
|
4785
4811
|
$.Visuals.prototype.addAnis = $.Group.prototype.addAnis = $.Sprite.prototype.addAnis;
|
|
4786
4812
|
|
|
4787
|
-
class
|
|
4813
|
+
class CastInfo {
|
|
4788
4814
|
constructor(sprite, px, py, nx, ny, fraction, maxDistance) {
|
|
4789
4815
|
this.sprite = sprite;
|
|
4790
4816
|
this._px = px;
|
|
@@ -5075,14 +5101,9 @@ async function q5playPreSetup(q) {
|
|
|
5075
5101
|
|
|
5076
5102
|
const point = scaleTo(x, y),
|
|
5077
5103
|
proxy = b2MakeProxy(point, 1, radius / meterSize),
|
|
5078
|
-
filter = new b2QueryFilter(),
|
|
5079
5104
|
shapes = [];
|
|
5080
5105
|
|
|
5081
|
-
|
|
5082
|
-
filter.categoryBits = 0xffffffff;
|
|
5083
|
-
filter.maskBits = 0xffffffff;
|
|
5084
|
-
|
|
5085
|
-
b2World_OverlapShape(wID, proxy, filter, (overlapResult) => {
|
|
5106
|
+
b2World_OverlapShape(wID, proxy, NULL_FILTER, (overlapResult) => {
|
|
5086
5107
|
const { shapeId } = overlapResult;
|
|
5087
5108
|
if (shapeId) {
|
|
5088
5109
|
shapes.push(shapeDict[shapeId.index1]);
|
|
@@ -5091,7 +5112,6 @@ async function q5playPreSetup(q) {
|
|
|
5091
5112
|
});
|
|
5092
5113
|
|
|
5093
5114
|
proxy.delete();
|
|
5094
|
-
filter.delete();
|
|
5095
5115
|
|
|
5096
5116
|
if (!shapes.length) return [];
|
|
5097
5117
|
|
|
@@ -5179,17 +5199,14 @@ async function q5playPreSetup(q) {
|
|
|
5179
5199
|
const origin = scaleTo(startX, startY);
|
|
5180
5200
|
const translation = scaleTo(endX - startX, endY - startY);
|
|
5181
5201
|
|
|
5182
|
-
const filter = new b2QueryFilter();
|
|
5183
|
-
filter.categoryBits = 0xffffffff;
|
|
5184
|
-
filter.maskBits = 0xffffffff;
|
|
5185
|
-
|
|
5186
5202
|
const results = [];
|
|
5187
5203
|
|
|
5188
|
-
b2World_CastRay(wID, origin, translation,
|
|
5204
|
+
b2World_CastRay(wID, origin, translation, NULL_FILTER, (castResult) => {
|
|
5189
5205
|
const shape = shapeDict[castResult.shapeId.index1];
|
|
5190
5206
|
if (shape?.sprite) {
|
|
5191
5207
|
const s = shape.sprite;
|
|
5192
|
-
|
|
5208
|
+
|
|
5209
|
+
s.cast = new CastInfo(
|
|
5193
5210
|
s,
|
|
5194
5211
|
castResult.point.x,
|
|
5195
5212
|
castResult.point.y,
|
|
@@ -5199,18 +5216,62 @@ async function q5playPreSetup(q) {
|
|
|
5199
5216
|
maxDistance
|
|
5200
5217
|
);
|
|
5201
5218
|
results.push(s);
|
|
5219
|
+
|
|
5220
|
+
if (limiter && limiter(s)) return 0; // stop raycast
|
|
5202
5221
|
}
|
|
5203
5222
|
return 1; // continue to collect all hits
|
|
5204
5223
|
});
|
|
5205
5224
|
|
|
5206
|
-
filter.delete();
|
|
5207
|
-
|
|
5208
5225
|
// sort results by distance from start
|
|
5209
|
-
results.sort((a, b) => a.
|
|
5226
|
+
results.sort((a, b) => a.cast.distance - b.cast.distance);
|
|
5227
|
+
return results;
|
|
5228
|
+
}
|
|
5229
|
+
|
|
5230
|
+
circleCast(startPos, endPos, radius) {
|
|
5231
|
+
let sprites = this.circleCastAll(startPos, endPos, radius, () => true);
|
|
5232
|
+
return sprites[0];
|
|
5233
|
+
}
|
|
5234
|
+
|
|
5235
|
+
circleCastAll(startPos, endPos, radius, limiter) {
|
|
5236
|
+
const startX = startPos.x ?? startPos[0];
|
|
5237
|
+
const startY = startPos.y ?? startPos[1];
|
|
5238
|
+
const endX = endPos.x ?? endPos[0];
|
|
5239
|
+
const endY = endPos.y ?? endPos[1];
|
|
5240
|
+
|
|
5241
|
+
const maxDistance = Math.sqrt((endX - startX) ** 2 + (endY - startY) ** 2);
|
|
5242
|
+
|
|
5243
|
+
const center = scaleTo(startX, startY);
|
|
5244
|
+
const proxy = b2MakeProxy(center, 1, radius / meterSize);
|
|
5245
|
+
const translation = scaleTo(endX - startX, endY - startY);
|
|
5210
5246
|
|
|
5211
|
-
|
|
5247
|
+
const results = [];
|
|
5212
5248
|
|
|
5213
|
-
|
|
5249
|
+
b2World_CastShape(wID, proxy, translation, NULL_FILTER, (castResult) => {
|
|
5250
|
+
const shape = shapeDict[castResult.shapeId.index1];
|
|
5251
|
+
if (shape?.sprite) {
|
|
5252
|
+
const s = shape.sprite;
|
|
5253
|
+
|
|
5254
|
+
s.cast = new CastInfo(
|
|
5255
|
+
s,
|
|
5256
|
+
castResult.point.x,
|
|
5257
|
+
castResult.point.y,
|
|
5258
|
+
castResult.normal.x,
|
|
5259
|
+
castResult.normal.y,
|
|
5260
|
+
castResult.fraction,
|
|
5261
|
+
maxDistance
|
|
5262
|
+
);
|
|
5263
|
+
results.push(s);
|
|
5264
|
+
|
|
5265
|
+
if (limiter && limiter(s)) return 0; // stop cast
|
|
5266
|
+
}
|
|
5267
|
+
return 1; // continue to collect all hits
|
|
5268
|
+
});
|
|
5269
|
+
|
|
5270
|
+
proxy.delete();
|
|
5271
|
+
|
|
5272
|
+
// sort results by distance from start
|
|
5273
|
+
results.sort((a, b) => a.cast.distance - b.cast.distance);
|
|
5274
|
+
return results;
|
|
5214
5275
|
}
|
|
5215
5276
|
};
|
|
5216
5277
|
|
|
@@ -5506,7 +5567,7 @@ async function q5playPreSetup(q) {
|
|
|
5506
5567
|
}
|
|
5507
5568
|
|
|
5508
5569
|
_draw(xA, yA, xB, yB) {
|
|
5509
|
-
if (xB) $.line(xA, yA, xB, yB);
|
|
5570
|
+
if (xB || yB) $.line(xA, yA, xB, yB);
|
|
5510
5571
|
else $.point(xA, yA);
|
|
5511
5572
|
}
|
|
5512
5573
|
|
|
@@ -6054,20 +6115,6 @@ async function q5playPreSetup(q) {
|
|
|
6054
6115
|
Box2D.b2PrismaticJoint_EnableLimit(this.jID, true);
|
|
6055
6116
|
}
|
|
6056
6117
|
|
|
6057
|
-
set limits(val) {
|
|
6058
|
-
let min, max;
|
|
6059
|
-
if (typeof val == 'number') {
|
|
6060
|
-
val /= 2;
|
|
6061
|
-
min = -val;
|
|
6062
|
-
max = val;
|
|
6063
|
-
} else {
|
|
6064
|
-
min = val[0];
|
|
6065
|
-
max = val[1];
|
|
6066
|
-
}
|
|
6067
|
-
Box2D.b2PrismaticJoint_SetLimits(this.jID, min / meterSize, max / meterSize);
|
|
6068
|
-
Box2D.b2PrismaticJoint_EnableLimit(this.jID, true);
|
|
6069
|
-
}
|
|
6070
|
-
|
|
6071
6118
|
get springEnabled() {
|
|
6072
6119
|
return Box2D.b2PrismaticJoint_IsSpringEnabled(this.jID);
|
|
6073
6120
|
}
|