q5play 4.0.6 → 4.0.8

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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/q5play.d.ts +8 -0
  3. package/q5play.js +94 -35
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q5play",
3
- "version": "4.0.6",
3
+ "version": "4.0.8",
4
4
  "author": "quinton-ashley",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "description": "A web-based game engine that uses q5.js WebGPU for graphics and Box2D v3 WASM for physics.",
package/q5play.d.ts CHANGED
@@ -70,6 +70,14 @@ declare global {
70
70
  * @default false
71
71
  */
72
72
  renderStats: boolean;
73
+ /**
74
+ * Runs automatically before each draw function call.
75
+ */
76
+ update(): void;
77
+ /**
78
+ * Runs automatically after each draw function call.
79
+ */
80
+ postDraw(): void;
73
81
  }
74
82
  const q5play: Q5Play;
75
83
 
package/q5play.js CHANGED
@@ -248,6 +248,9 @@ async function q5playPreSetup(q) {
248
248
  this.os = {};
249
249
  this.context = 'web';
250
250
 
251
+ this.update = () => q5playUpdate.call($, q);
252
+ this.postdraw = () => q5playPostDraw.call($, q);
253
+
251
254
  if (window.matchMedia) {
252
255
  this.hasMouse = window.matchMedia('(any-hover: none)').matches ? false : true;
253
256
  } else this.hasMouse = true;
@@ -331,7 +334,8 @@ async function q5playPreSetup(q) {
331
334
 
332
335
  // in q5play the default angle mode is degrees
333
336
  const DEGREES = $.DEGREES,
334
- DEGTORAD = Math.PI / 180;
337
+ DEGTORAD = Math.PI / 180,
338
+ RADTODEG = 180 / Math.PI;
335
339
  $.angleMode(DEGREES);
336
340
 
337
341
  // in q5play the default color mode is float RGB
@@ -1001,6 +1005,7 @@ async function q5playPreSetup(q) {
1001
1005
  if (!group.visualOnly) {
1002
1006
  const def = new b2DefaultBodyDef();
1003
1007
  def.type = bodyTypes[this._phys];
1008
+ def.allowFastRotation = true;
1004
1009
  this.bdID = b2CreateBody(wID, def);
1005
1010
  this._physicsEnabled = true;
1006
1011
 
@@ -5345,6 +5350,7 @@ async function q5playPreSetup(q) {
5345
5350
  this.type = type ??= 'glue';
5346
5351
  this.visible = true;
5347
5352
  this.deleted = false;
5353
+ this._springiness = 0;
5348
5354
 
5349
5355
  if (!a._shapes.length) a.addDefaultSensors();
5350
5356
  if (!b._shapes.length) b.addDefaultSensors();
@@ -5533,6 +5539,7 @@ async function q5playPreSetup(q) {
5533
5539
 
5534
5540
  _springMap(val) {
5535
5541
  if (val > 0) {
5542
+ this._springiness = val;
5536
5543
  if (val < 0.1) {
5537
5544
  val = $.map(val, 0, 0.1, 30, 4);
5538
5545
  } else if (val < 0.5) {
@@ -5569,7 +5576,7 @@ async function q5playPreSetup(q) {
5569
5576
  }
5570
5577
 
5571
5578
  get springiness() {
5572
- return Box2D.b2WeldJoint_GetLinearHertz(this.jID);
5579
+ return this._springiness;
5573
5580
  }
5574
5581
  set springiness(val) {
5575
5582
  val = this._springMap(val);
@@ -5645,7 +5652,7 @@ async function q5playPreSetup(q) {
5645
5652
  }
5646
5653
 
5647
5654
  get springiness() {
5648
- return Box2D.b2DistanceJoint_GetSpringHertz(this.jID);
5655
+ return this._springiness;
5649
5656
  }
5650
5657
  set springiness(val) {
5651
5658
  val = this._springMap(val);
@@ -5689,15 +5696,20 @@ async function q5playPreSetup(q) {
5689
5696
  constructor(spriteA, spriteB) {
5690
5697
  super(spriteA, spriteB, 'wheel');
5691
5698
 
5699
+ this._angle = $._angleMode == DEGREES ? 90 : Math.PI / 2;
5700
+ this._motorEnabled = false;
5701
+
5692
5702
  let j = this._init(b2DefaultWheelJointDef(wID));
5693
5703
  j.enableSpring = true;
5694
5704
  j.hertz = 4;
5695
5705
  j.dampingRatio = 0.7;
5696
- j.enableMotor = true;
5706
+ j.enableMotor = false; // neutral by default
5697
5707
  j.maxMotorTorque = 1000;
5698
5708
 
5699
5709
  this.jID = b2CreateWheelJoint(wID, j);
5700
5710
  jointDict[this.jID.index1] = this;
5711
+
5712
+ this._springiness = 0.1;
5701
5713
  }
5702
5714
 
5703
5715
  _init(j) {
@@ -5707,11 +5719,12 @@ async function q5playPreSetup(q) {
5707
5719
  j.base.bodyIdA = a.bdID;
5708
5720
  j.base.bodyIdB = b.bdID;
5709
5721
 
5710
- j.base.localFrameB.p = b2Body_GetLocalPoint(b.bdID, scaleTo(a.x, a.y));
5722
+ let rad = this._angle;
5723
+ if ($._angleMode == DEGREES) rad *= DEGTORAD;
5724
+ j.base.localFrameA.q.SetAngle(rad);
5711
5725
 
5712
- // let qA = b2Body_GetRotation(a.bdID);
5713
- // let qB = b2Body_GetRotation(b.bdID);
5714
- // j.base.localFrameA.q = b2InvMulRot(qA, qB);
5726
+ let pivot = scaleTo(b.x, b.y);
5727
+ j.base.localFrameA.p = b2Body_GetLocalPoint(a.bdID, pivot);
5715
5728
 
5716
5729
  return j;
5717
5730
  }
@@ -5724,9 +5737,12 @@ async function q5playPreSetup(q) {
5724
5737
  set angle(val) {
5725
5738
  if (val == this._angle) return;
5726
5739
  this._angle = val;
5727
- this._j.m_localXAxisA = new b2Vec2($.cos(val), $.sin(val));
5728
- this._j.m_localXAxisA.normalize();
5729
- this._j.m_localYAxisA = b2Vec2.crossNumVec2(1.0, this._j.m_localXAxisA);
5740
+ let rad = $._angleMode == DEGREES ? val * DEGTORAD : val;
5741
+
5742
+ let t = b2Joint_GetLocalFrameA(this.jID);
5743
+ t.q.SetAngle(rad);
5744
+ b2Joint_SetLocalFrameA(this.jID, t);
5745
+ b2Joint_WakeBodies(this.jID);
5730
5746
  }
5731
5747
 
5732
5748
  get springEnabled() {
@@ -5737,7 +5753,7 @@ async function q5playPreSetup(q) {
5737
5753
  }
5738
5754
 
5739
5755
  get springiness() {
5740
- return Box2D.b2WheelJoint_GetSpringHertz(this.jID);
5756
+ return this._springiness;
5741
5757
  }
5742
5758
  set springiness(val) {
5743
5759
  val = this._springMap(val);
@@ -5775,13 +5791,16 @@ async function q5playPreSetup(q) {
5775
5791
  }
5776
5792
  set motorEnabled(val) {
5777
5793
  Box2D.b2WheelJoint_EnableMotor(this.jID, val);
5794
+ this._motorEnabled = val;
5778
5795
  }
5779
5796
 
5780
5797
  get maxPower() {
5781
5798
  return Box2D.b2WheelJoint_GetMaxMotorTorque(this.jID);
5782
5799
  }
5783
5800
  set maxPower(val) {
5801
+ if (val > 0 && !this._motorEnabled) this.motorEnabled = true;
5784
5802
  Box2D.b2WheelJoint_SetMaxMotorTorque(this.jID, val);
5803
+ b2Joint_WakeBodies(this.jID);
5785
5804
  }
5786
5805
 
5787
5806
  get power() {
@@ -5789,10 +5808,14 @@ async function q5playPreSetup(q) {
5789
5808
  }
5790
5809
 
5791
5810
  get speed() {
5811
+ // if in neutral, return the wheel's angular velocity
5812
+ if (!this._motorEnabled) return this.spriteB.rotationSpeed;
5792
5813
  return Box2D.b2WheelJoint_GetMotorSpeed(this.jID);
5793
5814
  }
5794
5815
  set speed(val) {
5816
+ if (!this._motorEnabled) this.motorEnabled = true;
5795
5817
  Box2D.b2WheelJoint_SetMotorSpeed(this.jID, val);
5818
+ b2Joint_WakeBodies(this.jID);
5796
5819
  }
5797
5820
  };
5798
5821
 
@@ -5820,7 +5843,7 @@ async function q5playPreSetup(q) {
5820
5843
  }
5821
5844
 
5822
5845
  get springiness() {
5823
- return Box2D.b2RevoluteJoint_GetSpringHertz(this.jID);
5846
+ return this._springiness;
5824
5847
  }
5825
5848
  set springiness(val) {
5826
5849
  val = this._springMap(val);
@@ -5860,8 +5883,8 @@ async function q5playPreSetup(q) {
5860
5883
  max = val[1];
5861
5884
  }
5862
5885
  if ($._angleMode == DEGREES) {
5863
- min *= $._RADTODEG;
5864
- max *= $._RADTODEG;
5886
+ min *= DEGTORAD;
5887
+ max *= DEGTORAD;
5865
5888
  }
5866
5889
  Box2D.b2RevoluteJoint_SetLimits(this.jID, min, max);
5867
5890
  }
@@ -5890,8 +5913,6 @@ async function q5playPreSetup(q) {
5890
5913
  set maxPower(val) {
5891
5914
  Box2D.b2RevoluteJoint_SetMaxMotorTorque(this.jID, val);
5892
5915
  }
5893
-
5894
- _display() {}
5895
5916
  };
5896
5917
 
5897
5918
  $.SliderJoint = class extends $.Joint {
@@ -5899,17 +5920,39 @@ async function q5playPreSetup(q) {
5899
5920
  super(spriteA, spriteB, 'slider');
5900
5921
 
5901
5922
  let j = this._init(b2DefaultPrismaticJointDef());
5902
- j.enableLimit = true;
5903
- j.lowerTranslation = -1;
5904
- j.upperTranslation = 1;
5923
+ j.enableLimit = false;
5905
5924
  j.enableMotor = true;
5906
- j.maxMotorForce = 50;
5925
+ j.maxMotorForce = 10;
5907
5926
  j.motorSpeed = 0;
5908
5927
 
5909
5928
  this.jID = b2CreatePrismaticJoint(wID, j);
5910
5929
  jointDict[this.jID.index1] = this;
5911
5930
  }
5912
5931
 
5932
+ _init(j) {
5933
+ let a = this.spriteA,
5934
+ b = this.spriteB;
5935
+
5936
+ j.base.bodyIdA = a.bdID;
5937
+ j.base.bodyIdB = b.bdID;
5938
+
5939
+ // set the anchor at sprite A's position in B's local frame
5940
+ j.base.localFrameB.p = b2Body_GetLocalPoint(b.bdID, scaleTo(a.x, a.y));
5941
+
5942
+ // align the slide axis with the vector from A to B
5943
+ let dx = b.x - a.x;
5944
+ let dy = b.y - a.y;
5945
+ let len = Math.sqrt(dx * dx + dy * dy);
5946
+ let angle = len > 0 ? Math.atan2(dy, dx) : 0;
5947
+
5948
+ let qA = b2Body_GetRotation(a.bdID);
5949
+ let qB = b2Body_GetRotation(b.bdID);
5950
+ j.base.localFrameA.q.SetAngle(angle - qA.GetAngle());
5951
+ j.base.localFrameB.q.SetAngle(angle - qB.GetAngle());
5952
+
5953
+ return j;
5954
+ }
5955
+
5913
5956
  get translation() {
5914
5957
  return Box2D.b2PrismaticJoint_GetTranslation(this.jID) * meterSize;
5915
5958
  }
@@ -5940,6 +5983,21 @@ async function q5playPreSetup(q) {
5940
5983
  max = val[1];
5941
5984
  }
5942
5985
  Box2D.b2PrismaticJoint_SetLimits(this.jID, min / meterSize, max / meterSize);
5986
+ Box2D.b2PrismaticJoint_EnableLimit(this.jID, true);
5987
+ }
5988
+
5989
+ set limits(val) {
5990
+ let min, max;
5991
+ if (typeof val == 'number') {
5992
+ val /= 2;
5993
+ min = -val;
5994
+ max = val;
5995
+ } else {
5996
+ min = val[0];
5997
+ max = val[1];
5998
+ }
5999
+ Box2D.b2PrismaticJoint_SetLimits(this.jID, min / meterSize, max / meterSize);
6000
+ Box2D.b2PrismaticJoint_EnableLimit(this.jID, true);
5943
6001
  }
5944
6002
 
5945
6003
  get springEnabled() {
@@ -5950,10 +6008,10 @@ async function q5playPreSetup(q) {
5950
6008
  }
5951
6009
 
5952
6010
  get springiness() {
5953
- return Box2D.b2PrismaticJoint_GetSpringHertz(this.jID);
6011
+ return this._springiness;
5954
6012
  }
5955
6013
  set springiness(val) {
5956
- val = this._springMap ? this._springMap(val) : val;
6014
+ val = this._springMap(val);
5957
6015
  Box2D.b2PrismaticJoint_SetSpringHertz(this.jID, val);
5958
6016
  }
5959
6017
 
@@ -5976,6 +6034,7 @@ async function q5playPreSetup(q) {
5976
6034
  }
5977
6035
  set speed(val) {
5978
6036
  Box2D.b2PrismaticJoint_SetMotorSpeed(this.jID, val);
6037
+ b2Joint_WakeBodies(this.jID);
5979
6038
  }
5980
6039
 
5981
6040
  get maxPower() {
@@ -5989,14 +6048,14 @@ async function q5playPreSetup(q) {
5989
6048
  return Box2D.b2PrismaticJoint_GetMotorForce(this.jID);
5990
6049
  }
5991
6050
 
5992
- // get energy() {
5993
- // return Box2D.b2PrismaticJoint_GetSpeed(this.jID);
5994
- // }
6051
+ get energy() {
6052
+ return Box2D.b2PrismaticJoint_GetSpeed(this.jID);
6053
+ }
5995
6054
  };
5996
6055
 
5997
6056
  $.GrabberJoint = class extends $.Joint {
5998
- constructor(pointer, sprite) {
5999
- sprite ??= pointer;
6057
+ constructor(grabPoint, sprite) {
6058
+ sprite ??= grabPoint;
6000
6059
  super(sprite, sprite, 'grabber');
6001
6060
 
6002
6061
  let bd = b2DefaultBodyDef();
@@ -6022,11 +6081,11 @@ async function q5playPreSetup(q) {
6022
6081
 
6023
6082
  this.jID = b2CreateMotorJoint(wID, j);
6024
6083
 
6025
- let offX = sprite.x - (pointer[0] || pointer.x),
6026
- offY = sprite.y - (pointer[1] || pointer.y);
6084
+ let offX = sprite.x - (grabPoint[0] || grabPoint.x),
6085
+ offY = sprite.y - (grabPoint[1] || grabPoint.y);
6027
6086
  if (!isSlop(offX) || !isSlop(offY)) {
6028
6087
  this._setOffsetB(-offX, -offY);
6029
- this.target = pointer;
6088
+ this.target = grabPoint;
6030
6089
  }
6031
6090
 
6032
6091
  this.sprite = sprite;
@@ -7797,7 +7856,7 @@ async function q5playPreSetup(q) {
7797
7856
  s._vel._magCached = false;
7798
7857
 
7799
7858
  if (s._hasImagery || s._userDefinedDraw) {
7800
- s._rotation = Math.atan2(data[2], data[3]) * $._RADTODEG;
7859
+ s._rotation = Math.atan2(data[2], data[3]) * RADTODEG;
7801
7860
  }
7802
7861
 
7803
7862
  if (s.debug || (!s._hasImagery && !s._userDefinedDraw)) {
@@ -7812,7 +7871,7 @@ async function q5playPreSetup(q) {
7812
7871
  debugYellow = $.color(colorMax, colorMax, 0, colorMax * 0.9),
7813
7872
  debugYellowFill = $.color(colorMax, colorMax, 0, colorMax * 0.1);
7814
7873
 
7815
- if ($._c2d) {
7874
+ if ($.canvas.c2d) {
7816
7875
  // polyfill for q5 WebGPU high efficiency functions
7817
7876
  $._getFillIdx = () => $._fill;
7818
7877
  $._setFillIdx = (v) => ($._fill = v);
@@ -8026,7 +8085,7 @@ function q5playPostSetup() {
8026
8085
  }
8027
8086
 
8028
8087
  // called before each draw function call
8029
- function q5playPreDraw() {
8088
+ function q5playUpdate() {
8030
8089
  const $ = this;
8031
8090
 
8032
8091
  if (!$._q5) {
@@ -8382,6 +8441,6 @@ q5playClassLangs.Group += q5playClassLangs.Sprite;
8382
8441
 
8383
8442
  Q5.addHook('presetup', q5playPreSetup);
8384
8443
  Q5.addHook('postsetup', q5playPostSetup);
8385
- Q5.addHook('predraw', q5playPreDraw);
8444
+ Q5.addHook('predraw', q5playUpdate);
8386
8445
  Q5.addHook('postdraw', q5playPostDraw);
8387
8446
  Q5.addHook('remove', q5playRemove);