Skip to content

Commit f0127f4

Browse files
authored
Add world.queueUpdate
1 parent 5b611d7 commit f0127f4

File tree

9 files changed

+92
-46
lines changed

9 files changed

+92
-46
lines changed

.changeset/sour-news-float.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"planck": minor
3+
---
4+
5+
Add world.queueUpdate() to queue and defer updates after current simulation step

example/8-Ball.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,13 @@ class BilliardPhysics {
221221
const ball = fA.getUserData() === BALL ? bA : fB.getUserData() === BALL ? bB : null;
222222
const pocket = fA.getUserData() === POCKET ? bA : fB.getUserData() === POCKET ? bB : null;
223223

224-
// do not change world immediately
225-
setTimeout(() => {
226-
if (ball && pocket) {
224+
if (ball && pocket) {
225+
// do not change world immediately
226+
this.world.queueUpdate(() => {
227227
this.world.destroyBody(ball);
228228
this.client?.onBallInPocket(ball, pocket);
229-
}
230-
}, 1);
229+
});
230+
}
231231
};
232232
}
233233

example/Asteroid.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -230,15 +230,19 @@ class AsteroidPhysics {
230230

231231
const asteroid = dataA?.type == "asteroid" ? bodyA : dataB?.type == "asteroid" ? bodyB : null;
232232

233-
setTimeout(() => {
234-
if (ship && asteroid) {
233+
if (ship && asteroid) {
234+
// do not change world immediately
235+
this.world.queueUpdate(() => {
235236
this.client?.collideShipAsteroid(ship, asteroid);
236-
}
237+
});
238+
}
237239

238-
if (bullet && asteroid) {
240+
if (bullet && asteroid) {
241+
// do not change world immediately
242+
this.world.queueUpdate(() => {
239243
this.client?.collideBulletAsteroid(bullet, asteroid);
240-
}
241-
}, 1);
244+
});
245+
}
242246
}
243247

244248
deleteShip(): boolean {

example/Breakable.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,12 @@ world.on("post-solve", function (contact, impulse) {
5353
}
5454

5555
if (maxImpulse > 40.0) {
56-
setTimeout(function () {
57-
Break();
58-
broke = true;
59-
});
56+
broke = true;
57+
world.queueUpdate(breakIt);
6058
}
6159
});
6260

63-
function Break() {
61+
function breakIt() {
6462
// Create two bodies from one.
6563
const center = body1.getWorldCenter();
6664

example/Breakout.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -198,19 +198,27 @@ class BreakoutPhysics {
198198
const drop = typeA === "drop" ? dataA : typeB === "drop" ? dataB : null;
199199

200200
// do not change world immediately
201-
setTimeout(() => {
202-
if (ball && brick) {
201+
if (ball && brick) {
202+
this.world.queueUpdate(() => {
203203
this.client?.collideBallBrick(ball as BallData, brick as BrickData);
204-
} else if (ball && bottom) {
204+
});
205+
} else if (ball && bottom) {
206+
this.world.queueUpdate(() => {
205207
this.client?.collideBallBottom(ball as BallData);
206-
} else if (ball && paddle) {
208+
});
209+
} else if (ball && paddle) {
210+
this.world.queueUpdate(() => {
207211
this.client?.collideBallPaddle(ball as BallData);
208-
} else if (drop && paddle) {
212+
});
213+
} else if (drop && paddle) {
214+
this.world.queueUpdate(() => {
209215
this.client?.collideDropPaddle(drop as DropData);
210-
} else if (drop && bottom) {
216+
});
217+
} else if (drop && bottom) {
218+
this.world.queueUpdate(() => {
211219
this.client?.collideDropBottom(drop as DropData);
212-
}
213-
}, 1);
220+
});
221+
}
214222
};
215223

216224
createBoardPhysics() {

example/Shuffle.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,11 @@ world.on("post-solve", function (contact) {
8787
? bB
8888
: null;
8989

90-
// do not change world immediately
91-
setTimeout(function () {
92-
if (ball && wall) {
90+
if (ball && wall) {
91+
world.queueUpdate(() => {
9392
world.destroyBody(ball);
94-
}
95-
}, 1);
93+
});
94+
}
9695
});
9796

9897
function row(n: number, m: number, r: number, l: number) {

example/Soccer.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,14 @@ world.on("post-solve", function (contact) {
131131
? bB
132132
: null;
133133

134-
// do not change world immediately
135-
setTimeout(function () {
136-
if (ball && goal) {
134+
if (ball && goal) {
135+
// do not change world immediately
136+
world.queueUpdate(function () {
137137
ball.setPosition({ x: 0, y: 0 });
138138
ball.setLinearVelocity({ x: 0, y: 0 });
139139
// world.destroyBody(ball);
140-
}
141-
}, 1);
140+
});
141+
}
142142
});
143143

144144
function team() {

src/dynamics/Body.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ export class Body {
394394
/**
395395
* Set the type of the body to "static", "kinematic" or "dynamic".
396396
* @param type The type of the body.
397+
*
398+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
397399
*/
398400
setType(type: BodyType): void {
399401
if (_ASSERT) console.assert(type === STATIC || type === KINEMATIC || type === DYNAMIC);
@@ -502,6 +504,8 @@ export class Body {
502504
* in collisions, ray-casts, or queries. Joints connected to an inactive body
503505
* are implicitly inactive. An inactive body is still owned by a World object
504506
* and remains
507+
*
508+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
505509
*/
506510
setActive(flag: boolean): void {
507511
if (_ASSERT) console.assert(this.isWorldLocked() == false);
@@ -568,6 +572,8 @@ export class Body {
568572
* Set the position of the body's origin and rotation. Manipulating a body's
569573
* transform may cause non-physical behavior. Note: contacts are updated on the
570574
* next call to World.step.
575+
*
576+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
571577
*
572578
* @param position The world position of the body's local origin.
573579
* @param angle The world rotation in radians.
@@ -577,6 +583,8 @@ export class Body {
577583
* Set the position of the body's origin and rotation. Manipulating a body's
578584
* transform may cause non-physical behavior. Note: contacts are updated on the
579585
* next call to World.step.
586+
*
587+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
580588
*/
581589
setTransform(xf: Transform): void;
582590
setTransform(a: Vec2Value | Transform, b?: number): void {
@@ -863,6 +871,8 @@ export class Body {
863871
* that this changes the center of mass position. Note that creating or
864872
* destroying fixtures can also alter the mass. This function has no effect if
865873
* the body isn't dynamic.
874+
*
875+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
866876
*
867877
* @param massData The mass properties.
868878
*/
@@ -1068,7 +1078,7 @@ export class Body {
10681078
*
10691079
* Contacts are not created until the next time step.
10701080
*
1071-
* Warning: This function is locked during callbacks.
1081+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
10721082
*/
10731083
createFixture(def: FixtureDef): Fixture;
10741084
createFixture(shape: Shape, opt?: FixtureOpt): Fixture;
@@ -1092,8 +1102,8 @@ export class Body {
10921102
* mass of the body if the body is dynamic and the fixture has positive density.
10931103
* All fixtures attached to a body are implicitly destroyed when the body is
10941104
* destroyed.
1095-
*
1096-
* Warning: This function is locked during callbacks.
1105+
*
1106+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
10971107
*
10981108
* @param fixture The fixture to be removed.
10991109
*/

src/dynamics/World.ts

+31-9
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ export class World {
128128
/** @internal */ m_positionIterations: number;
129129
/** @internal */ m_t: number;
130130

131+
/** @internal */ m_step_callback: ((world: World) => unknown)[] = [];
132+
131133
// TODO
132134
/** @internal */ _listeners: {
133135
[key: string]: any[]
@@ -469,10 +471,12 @@ export class World {
469471
* position -= newOrigin
470472
*
471473
* @param newOrigin The new origin with respect to the old origin
474+
*
475+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
472476
*/
473477
shiftOrigin(newOrigin: Vec2Value): void {
474-
if (_ASSERT) console.assert(this.m_locked == false);
475-
if (this.m_locked) {
478+
if (_ASSERT) console.assert(this.isLocked() == false);
479+
if (this.isLocked()) {
476480
return;
477481
}
478482

@@ -510,7 +514,7 @@ export class World {
510514
* Create a rigid body given a definition. No reference to the definition is
511515
* retained.
512516
*
513-
* Warning: This function is locked during callbacks.
517+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
514518
*/
515519
createBody(def?: BodyDef): Body;
516520
createBody(position: Vec2Value, angle?: number): Body;
@@ -565,12 +569,11 @@ export class World {
565569
}
566570

567571
/**
568-
* Destroy a rigid body given a definition. No reference to the definition is
569-
* retained.
572+
* Destroy a body from the world.
570573
*
571574
* Warning: This automatically deletes all associated shapes and joints.
572575
*
573-
* Warning: This function is locked during callbacks.
576+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
574577
*/
575578
destroyBody(b: Body): boolean {
576579
if (_ASSERT) console.assert(this.m_bodyCount > 0);
@@ -647,7 +650,7 @@ export class World {
647650
* Create a joint to constrain bodies together. No reference to the definition
648651
* is retained. This may cause the connected bodies to cease colliding.
649652
*
650-
* Warning: This function is locked during callbacks.
653+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
651654
*/
652655
createJoint<T extends Joint>(joint: T): T | null {
653656
if (_ASSERT) console.assert(!!joint.m_bodyA);
@@ -700,8 +703,11 @@ export class World {
700703
}
701704

702705
/**
703-
* Destroy a joint. This may cause the connected bodies to begin colliding.
704-
* Warning: This function is locked during callbacks.
706+
* Destroy a joint.
707+
*
708+
* Warning: This may cause the connected bodies to begin colliding.
709+
*
710+
* Warning: This function is locked when a world simulation step is in progress. Use queueUpdate to schedule a function to be called after the step.
705711
*/
706712
destroyJoint(joint: Joint): void {
707713
if (_ASSERT) console.assert(this.isLocked() == false);
@@ -854,9 +860,25 @@ export class World {
854860

855861
this.m_locked = false;
856862

863+
let callback: (world: World) => unknown;
864+
while(callback = this.m_step_callback.pop()) {
865+
callback(this);
866+
}
867+
857868
this.publish("post-step", timeStep);
858869
}
859870

871+
/**
872+
* Queue a function to be called after ongoing simulation step. If no simulation is in progress call it immediately.
873+
*/
874+
queueUpdate(callback: (world: World) => unknown): void {
875+
if (!this.isLocked()) {
876+
callback(this);
877+
} else {
878+
this.m_step_callback.push(callback);
879+
}
880+
}
881+
860882
/**
861883
* @internal
862884
* Call this method to find new contacts.

0 commit comments

Comments
 (0)