mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
18 Commits
v5.2.176
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e503869798 | ||
![]() |
643dbf5329 | ||
![]() |
dddd46a1c1 | ||
![]() |
3f9408575f | ||
![]() |
d38e9330dd | ||
![]() |
fcdc24d4ce | ||
![]() |
47fb1bf6fa | ||
![]() |
8b83436cfe | ||
![]() |
d0a5016a31 | ||
![]() |
969b6815c9 | ||
![]() |
d91fd896ac | ||
![]() |
03a2c04b45 | ||
![]() |
72bccfef13 | ||
![]() |
133fecb34f | ||
![]() |
ac26ca5d9d | ||
![]() |
c7971a9b52 | ||
![]() |
d79886e93c | ||
![]() |
6a05c6ff6b |
@@ -64,7 +64,7 @@ jobs:
|
||||
# Note: Code signing is done using CSC_LINK (see https://www.electron.build/code-signing).
|
||||
- run:
|
||||
name: Build GDevelop IDE
|
||||
command: export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --mac --publish=never
|
||||
command: export CSC_FOR_PULL_REQUEST=true && export NODE_OPTIONS="--max-old-space-size=7168" && cd newIDE/electron-app && npm run build -- --mac --publish=never
|
||||
|
||||
- run:
|
||||
name: Clean dist folder to keep only installers/binaries.
|
||||
@@ -101,8 +101,8 @@ jobs:
|
||||
command: sudo apt-get update && sudo apt install cmake
|
||||
|
||||
- run:
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
@@ -178,8 +178,8 @@ jobs:
|
||||
command: sudo apt-get update && sudo apt install cmake
|
||||
|
||||
- run:
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
name: Install Python3 dependencies for Emscripten
|
||||
command: sudo apt install python-is-python3 python3-distutils -y
|
||||
|
||||
- run:
|
||||
name: Install Emscripten (for GDevelop.js)
|
||||
|
@@ -420,7 +420,6 @@ module.exports = {
|
||||
)
|
||||
.setIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
|
||||
.addIncludeFile('Extensions/Physics2Behavior/box2d.js')
|
||||
.addIncludeFile('Extensions/Physics2Behavior/utils.js');
|
||||
|
||||
// Global
|
||||
aut
|
||||
|
@@ -20,7 +20,13 @@ namespace gdjs {
|
||||
// Start with 1 so the user is safe from default variables value (0)
|
||||
joints: any = {};
|
||||
|
||||
// List of physics behavior in the runtimeScene. It should be updated
|
||||
// when a new physics object is created (constructor), on destruction (onDestroy),
|
||||
// on behavior activation (onActivate) and on behavior deactivation (onDeActivate).
|
||||
_registeredBehaviors: Set<Physics2RuntimeBehavior>;
|
||||
|
||||
constructor(runtimeScene, sharedData) {
|
||||
this._registeredBehaviors = new Set();
|
||||
this.gravityX = sharedData.gravityX;
|
||||
this.gravityY = sharedData.gravityY;
|
||||
this.scaleX = sharedData.scaleX === 0 ? 100 : sharedData.scaleX;
|
||||
@@ -104,6 +110,40 @@ namespace gdjs {
|
||||
return runtimeScene.physics2SharedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a physics object to the list of existing object.
|
||||
*/
|
||||
addToBehaviorsList(physicsBehavior: gdjs.Physics2RuntimeBehavior) {
|
||||
this._registeredBehaviors.add(physicsBehavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a physics object to the list of existing object.
|
||||
*/
|
||||
removeFromBehaviorsList(physicsBehavior: gdjs.Physics2RuntimeBehavior) {
|
||||
this._registeredBehaviors.delete(physicsBehavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all contactsStartedThisFrame and contactsEndedThisFrame of all
|
||||
* registered physics behavior.
|
||||
*/
|
||||
resetStartedAndEndedCollisions() {
|
||||
for (const physicsBehavior of this._registeredBehaviors) {
|
||||
physicsBehavior.contactsStartedThisFrame.length = 0;
|
||||
physicsBehavior.contactsEndedThisFrame.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all registered body.
|
||||
*/
|
||||
updateBodiesFromObjects() {
|
||||
for (const physicsBehavior of this._registeredBehaviors) {
|
||||
physicsBehavior.updateBodyFromObject();
|
||||
}
|
||||
}
|
||||
|
||||
step(deltaTime) {
|
||||
this.frameTime += deltaTime;
|
||||
if (this.frameTime >= this.timeStep) {
|
||||
@@ -241,13 +281,29 @@ namespace gdjs {
|
||||
layers: any;
|
||||
masks: any;
|
||||
shapeScale: number = 1;
|
||||
// Array containing the beginning of contacts reported by onContactBegin. Each contact
|
||||
// should be unique to avoid recording glitches where the object loses and regain
|
||||
// contact between two frames. The array is updated each time the method
|
||||
// onContactBegin is called by the listener, which is only called when stepping
|
||||
// the world i.e. in the first preEvent called by a physics behavior. This array is
|
||||
// cleared just before stepping the world.
|
||||
contactsStartedThisFrame: Array<Physics2RuntimeBehavior>;
|
||||
// Array containing the end of contacts reported by onContactEnd. The array is updated
|
||||
// each time the method onContactEnd is called by the listener, which can be called at
|
||||
// any time. This array is cleared just before stepping the world.
|
||||
contactsEndedThisFrame: Array<Physics2RuntimeBehavior>;
|
||||
// Array containing the exact current contacts with the objects. It is updated
|
||||
// each time the methods onContactBegin and onContactEnd are called by the contact
|
||||
// listener.
|
||||
currentContacts: Array<Physics2RuntimeBehavior>;
|
||||
destroyedDuringFrameLogic: boolean;
|
||||
_body: any = null;
|
||||
_sharedData: any;
|
||||
_tempb2Vec2: any;
|
||||
|
||||
// sharedData is a reference to the shared data of the scene, that registers
|
||||
// every physics behavior that is created so that collisions can be cleared
|
||||
// before stepping the world.
|
||||
_sharedData: Physics2SharedData;
|
||||
// Avoid creating new vectors all the time
|
||||
_tempb2Vec2Sec: any;
|
||||
|
||||
@@ -287,12 +343,14 @@ namespace gdjs {
|
||||
this.contactsEndedThisFrame = [];
|
||||
this.currentContacts = [];
|
||||
this.currentContacts.length = 0;
|
||||
this.destroyedDuringFrameLogic = false;
|
||||
this._sharedData = Physics2SharedData.getSharedData(
|
||||
runtimeScene,
|
||||
behaviorData.name
|
||||
);
|
||||
this._tempb2Vec2 = new Box2D.b2Vec2();
|
||||
this._tempb2Vec2Sec = new Box2D.b2Vec2();
|
||||
this._sharedData.addToBehaviorsList(this);
|
||||
}
|
||||
|
||||
// Stores a Box2D pointer of created vertices
|
||||
@@ -377,6 +435,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
onDeActivate() {
|
||||
this._sharedData.removeFromBehaviorsList(this);
|
||||
if (this._body !== null) {
|
||||
// When a body is deleted, Box2D removes automatically its joints, leaving an invalid pointer in our joints list
|
||||
this._sharedData.clearBodyJoints(this._body);
|
||||
@@ -391,9 +450,22 @@ namespace gdjs {
|
||||
this._sharedData.world.DestroyBody(this._body);
|
||||
this._body = null;
|
||||
}
|
||||
this.contactsEndedThisFrame.length = 0;
|
||||
this.contactsStartedThisFrame.length = 0;
|
||||
this.currentContacts.length = 0;
|
||||
}
|
||||
|
||||
onActivate() {
|
||||
this._sharedData.addToBehaviorsList(this);
|
||||
|
||||
this.contactsEndedThisFrame.length = 0;
|
||||
this.contactsStartedThisFrame.length = 0;
|
||||
this.currentContacts.length = 0;
|
||||
this.updateBodyFromObject();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
this.destroyedDuringFrameLogic = true;
|
||||
this.onDeActivate();
|
||||
}
|
||||
|
||||
@@ -641,8 +713,7 @@ namespace gdjs {
|
||||
recreateShape() {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Destroy the old shape
|
||||
@@ -673,7 +744,8 @@ namespace gdjs {
|
||||
return this._body;
|
||||
}
|
||||
|
||||
createBody() {
|
||||
createBody(): boolean {
|
||||
if (!this.activated() || this.destroyedDuringFrameLogic) return false;
|
||||
// Generate the body definition
|
||||
const bodyDef = new Box2D.b2BodyDef();
|
||||
|
||||
@@ -711,16 +783,15 @@ namespace gdjs {
|
||||
// Update cached size
|
||||
this._objectOldWidth = this.owner.getWidth();
|
||||
this._objectOldHeight = this.owner.getHeight();
|
||||
return true;
|
||||
}
|
||||
|
||||
doStepPreEvents(runtimeScene) {
|
||||
// Create a body if there is not one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
}
|
||||
|
||||
// Step the world if not done this frame yet
|
||||
if (!this._sharedData.stepped) {
|
||||
// Reset started and ended contacts array for all physics instances.
|
||||
this._sharedData.resetStartedAndEndedCollisions();
|
||||
this._sharedData.updateBodiesFromObjects();
|
||||
this._sharedData.step(
|
||||
runtimeScene.getTimeManager().getElapsedTime() / 1000.0
|
||||
);
|
||||
@@ -741,37 +812,25 @@ namespace gdjs {
|
||||
);
|
||||
this.owner.setAngle(gdjs.toDegrees(this._body.GetAngle()));
|
||||
|
||||
// Update cached transform
|
||||
// Update cached transform.
|
||||
this._objectOldX = this.owner.getX();
|
||||
this._objectOldY = this.owner.getY();
|
||||
this._objectOldAngle = this.owner.getAngle();
|
||||
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
this.currentContacts,
|
||||
this.contactsStartedThisFrame,
|
||||
this.contactsEndedThisFrame
|
||||
);
|
||||
}
|
||||
|
||||
doStepPostEvents(runtimeScene) {
|
||||
this._updateBodyFromObject();
|
||||
|
||||
// Reset contacts that happened this frame
|
||||
this.contactsStartedThisFrame.length = 0;
|
||||
this.contactsEndedThisFrame.length = 0;
|
||||
|
||||
// Reset world step to update next frame
|
||||
this._sharedData.stepped = false;
|
||||
}
|
||||
|
||||
onObjectHotReloaded() {
|
||||
this._updateBodyFromObject();
|
||||
this.updateBodyFromObject();
|
||||
}
|
||||
|
||||
_updateBodyFromObject() {
|
||||
updateBodyFromObject() {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// The object size has changed, recreate the shape.
|
||||
@@ -868,8 +927,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update body type
|
||||
@@ -892,8 +950,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update body type
|
||||
@@ -916,8 +973,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update body type
|
||||
@@ -940,8 +996,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update body bullet flag
|
||||
@@ -955,8 +1010,7 @@ namespace gdjs {
|
||||
setFixedRotation(enable): void {
|
||||
this.fixedRotation = enable;
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
this._body.SetFixedRotation(this.fixedRotation);
|
||||
}
|
||||
@@ -968,8 +1022,7 @@ namespace gdjs {
|
||||
setSleepingAllowed(enable): void {
|
||||
this.canSleep = enable;
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
this._body.SetSleepingAllowed(this.canSleep);
|
||||
}
|
||||
@@ -977,7 +1030,7 @@ namespace gdjs {
|
||||
isSleeping(): boolean {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return true;
|
||||
}
|
||||
|
||||
// Get the body sleeping state
|
||||
@@ -1004,8 +1057,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body density
|
||||
@@ -1033,8 +1085,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body friction
|
||||
@@ -1068,8 +1119,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body restitution
|
||||
@@ -1098,8 +1148,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body linear damping
|
||||
@@ -1121,8 +1170,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body angular damping
|
||||
@@ -1144,8 +1192,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body gravity scale
|
||||
@@ -1181,8 +1228,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body layers
|
||||
@@ -1220,8 +1266,7 @@ namespace gdjs {
|
||||
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return;
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Update the body masks
|
||||
@@ -1233,8 +1278,7 @@ namespace gdjs {
|
||||
getLinearVelocityX(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return 0;
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Get the linear velocity on X
|
||||
@@ -1244,7 +1288,7 @@ namespace gdjs {
|
||||
setLinearVelocityX(linearVelocityX): void {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Set the linear velocity on X
|
||||
@@ -1259,8 +1303,7 @@ namespace gdjs {
|
||||
getLinearVelocityY(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return 0;
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Get the linear velocity on Y
|
||||
@@ -1270,7 +1313,7 @@ namespace gdjs {
|
||||
setLinearVelocityY(linearVelocityY): void {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Set the linear velocity on Y
|
||||
@@ -1282,11 +1325,10 @@ namespace gdjs {
|
||||
);
|
||||
}
|
||||
|
||||
getLinearVelocityLength() {
|
||||
getLinearVelocityLength(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return 0;
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Get the linear velocity length
|
||||
@@ -1296,10 +1338,10 @@ namespace gdjs {
|
||||
).Length();
|
||||
}
|
||||
|
||||
getAngularVelocity() {
|
||||
getAngularVelocity(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Get the angular velocity
|
||||
@@ -1309,7 +1351,7 @@ namespace gdjs {
|
||||
setAngularVelocity(angularVelocity): void {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Set the angular velocity
|
||||
@@ -1319,7 +1361,7 @@ namespace gdjs {
|
||||
applyForce(forceX, forceY, positionX, positionY) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1338,7 +1380,7 @@ namespace gdjs {
|
||||
applyPolarForce(angle, length, positionX, positionY) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1358,7 +1400,7 @@ namespace gdjs {
|
||||
applyForceTowardPosition(length, towardX, towardY, positionX, positionY) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1381,7 +1423,7 @@ namespace gdjs {
|
||||
applyImpulse(impulseX, impulseY, positionX, positionY) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1400,7 +1442,7 @@ namespace gdjs {
|
||||
applyPolarImpulse(angle, length, positionX, positionY) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1420,7 +1462,7 @@ namespace gdjs {
|
||||
applyImpulseTowardPosition(length, towardX, towardY, positionX, positionY) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1443,7 +1485,7 @@ namespace gdjs {
|
||||
applyTorque(torque) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1456,7 +1498,7 @@ namespace gdjs {
|
||||
applyAngularImpulse(angularImpulse) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1469,7 +1511,7 @@ namespace gdjs {
|
||||
getMass(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1481,7 +1523,7 @@ namespace gdjs {
|
||||
getInertia(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Wake up the object
|
||||
@@ -1493,7 +1535,7 @@ namespace gdjs {
|
||||
getMassCenterX(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Get the mass center on X
|
||||
@@ -1503,7 +1545,7 @@ namespace gdjs {
|
||||
getMassCenterY(): float {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return 0;
|
||||
}
|
||||
|
||||
// Get the mass center on Y
|
||||
@@ -1514,8 +1556,7 @@ namespace gdjs {
|
||||
isJointFirstObject(jointId): boolean {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return false;
|
||||
if (!this.createBody()) return false;
|
||||
}
|
||||
|
||||
// Get the joint
|
||||
@@ -1533,8 +1574,7 @@ namespace gdjs {
|
||||
isJointSecondObject(jointId): boolean {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
return false;
|
||||
if (!this.createBody()) return false;
|
||||
}
|
||||
|
||||
// Get the joint
|
||||
@@ -1647,7 +1687,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -1818,7 +1858,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Set joint settings
|
||||
@@ -1887,7 +1927,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -2178,7 +2218,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -2503,7 +2543,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -2677,7 +2717,7 @@ namespace gdjs {
|
||||
addGearJoint(jointId1, jointId2, ratio, collideConnected, variable) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Get the first joint
|
||||
@@ -2798,7 +2838,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// Set joint settings
|
||||
@@ -2973,7 +3013,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -3249,7 +3289,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -3383,7 +3423,7 @@ namespace gdjs {
|
||||
addRopeJoint(x1, y1, other, x2, y2, maxLength, collideConnected, variable) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -3490,7 +3530,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -3618,7 +3658,7 @@ namespace gdjs {
|
||||
) {
|
||||
// If there is no body, set a new one
|
||||
if (this._body === null) {
|
||||
this.createBody();
|
||||
if (!this.createBody()) return;
|
||||
}
|
||||
|
||||
// If there is no second object or it doesn't share the behavior, return
|
||||
@@ -3840,6 +3880,7 @@ namespace gdjs {
|
||||
// start again right away. It is considered a glitch
|
||||
// and should not be detected.
|
||||
let i = this.contactsEndedThisFrame.indexOf(otherBehavior);
|
||||
this.currentContacts.push(otherBehavior);
|
||||
if (i !== -1) {
|
||||
this.contactsEndedThisFrame.splice(i, 1);
|
||||
} else {
|
||||
@@ -3849,6 +3890,10 @@ namespace gdjs {
|
||||
|
||||
onContactEnd(otherBehavior: Physics2RuntimeBehavior) {
|
||||
this.contactsEndedThisFrame.push(otherBehavior);
|
||||
const index = this.currentContacts.indexOf(otherBehavior);
|
||||
if (index !== -1) {
|
||||
this.currentContacts.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -65,6 +65,60 @@ function createGameWithSceneWithPhysics2SharedData() {
|
||||
return [runtimeGame, runtimeScene];
|
||||
}
|
||||
|
||||
class BehaviorTest extends gdjs.RuntimeBehavior {
|
||||
collisionsStartedThisFrame = [];
|
||||
collisionsEndedThisFrame = [];
|
||||
currentCollisions = [];
|
||||
other;
|
||||
isPostEventActivated = false;
|
||||
isPreEventActivated = false;
|
||||
|
||||
constructor(runtimeScene, behaviorData, owner) {
|
||||
super(runtimeScene, behaviorData, owner);
|
||||
this.collisionsStartedThisFrame = behaviorData.collisionsStartedThisFrame;
|
||||
this.collisionsEndedThisFrame = behaviorData.collisionsEndedThisFrame;
|
||||
this.currentCollisions = behaviorData.currentCollisions;
|
||||
this.other = behaviorData.other;
|
||||
}
|
||||
|
||||
activatePostEvent(activate) {
|
||||
this.isPostEventActivated = activate;
|
||||
}
|
||||
activatePreEvent(activate) {
|
||||
this.isPreEventActivated = activate;
|
||||
}
|
||||
|
||||
setExpectedCollisions({ started, collision, stopped }) {
|
||||
this.collisionsStartedThisFrame = started;
|
||||
this.currentCollisions = collision;
|
||||
this.collisionsEndedThisFrame = stopped;
|
||||
}
|
||||
|
||||
doStepPreEvents() {
|
||||
if (this.isPreEventActivated) {
|
||||
const physicsBehavior = this.owner.getBehavior('Physics2');
|
||||
assertCollision(this.owner, this.other, {
|
||||
started: this.collisionsStartedThisFrame,
|
||||
collision: this.currentCollisions,
|
||||
stopped: this.collisionsEndedThisFrame,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
doStepPostEvents() {
|
||||
if (this.isPostEventActivated) {
|
||||
const physicsBehavior = this.owner.getBehavior('Physics2');
|
||||
assertCollision(this.owner, this.other, {
|
||||
started: this.collisionsStartedThisFrame,
|
||||
collision: this.currentCollisions,
|
||||
stopped: this.collisionsEndedThisFrame,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gdjs.registerBehavior('Physics2::BehaviorTest', BehaviorTest);
|
||||
|
||||
function createObject(runtimeScene, behaviorProperties) {
|
||||
const object = new gdjs.TestRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
@@ -104,6 +158,223 @@ function createObject(runtimeScene, behaviorProperties) {
|
||||
}
|
||||
|
||||
describe('Physics2RuntimeBehavior', () => {
|
||||
describe('Behavior activation and reactivation', () => {
|
||||
let runtimeGame;
|
||||
let runtimeScene;
|
||||
beforeEach(() => {
|
||||
[runtimeGame, runtimeScene] = createGameWithSceneWithPhysics2SharedData();
|
||||
});
|
||||
|
||||
it('should not leave a living body after removing an object', () => {
|
||||
const object = createObject(runtimeScene);
|
||||
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const behavior = object.getBehavior('Physics2');
|
||||
if (!behavior) {
|
||||
throw new Error('Behavior not found, test cannot be run.');
|
||||
}
|
||||
|
||||
// First render to have the behavior set up
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
expect(behavior.getBody()).not.to.be(null);
|
||||
expect(behavior._sharedData._registeredBehaviors.size).to.be(1);
|
||||
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
|
||||
true
|
||||
);
|
||||
|
||||
// Delete object from scene
|
||||
object.deleteFromScene(runtimeScene);
|
||||
expect(behavior.destroyedDuringFrameLogic).to.be(true);
|
||||
expect(behavior.getBody()).to.be(null);
|
||||
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
|
||||
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
|
||||
false
|
||||
);
|
||||
|
||||
// Call a few methods on the behavior
|
||||
behavior.setLinearDamping(2);
|
||||
behavior.setGravityScale(2);
|
||||
|
||||
// Body should still not exist
|
||||
expect(behavior.getBody()).to.be(null);
|
||||
});
|
||||
|
||||
it("doesn't raise errors if an object with a deactivated physics2 behavior is removed", () => {
|
||||
const object = createObject(runtimeScene);
|
||||
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const behavior = object.getBehavior('Physics2');
|
||||
if (!behavior) {
|
||||
throw new Error('Behavior not found, test cannot be run.');
|
||||
}
|
||||
|
||||
// First render to have the behavior set up
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
expect(behavior.getBody()).not.to.be(null);
|
||||
expect(behavior._sharedData._registeredBehaviors.size).to.be(1);
|
||||
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
|
||||
true
|
||||
);
|
||||
|
||||
object.activateBehavior('Physics2', false);
|
||||
expect(behavior.getBody()).to.be(null);
|
||||
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
|
||||
expect(behavior._sharedData._registeredBehaviors.has(behavior)).to.be(
|
||||
false
|
||||
);
|
||||
|
||||
object.deleteFromScene(runtimeScene);
|
||||
|
||||
expect(behavior.destroyedDuringFrameLogic).to.be(true);
|
||||
expect(behavior.getBody()).to.be(null);
|
||||
expect(behavior._sharedData._registeredBehaviors.size).to.be(0);
|
||||
});
|
||||
|
||||
it("should not recreate object's body when setting or getting behavior properties", () => {
|
||||
const object = createObject(runtimeScene);
|
||||
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const behavior = object.getBehavior('Physics2');
|
||||
if (!behavior) {
|
||||
throw new Error('Behavior not found, test cannot be run.');
|
||||
}
|
||||
|
||||
// First render to have the behavior set up
|
||||
runtimeScene.renderAndStep(1000 / 60);
|
||||
expect(behavior.getBody()).not.to.be(null);
|
||||
|
||||
// Deactivate behavior
|
||||
object.activateBehavior('Physics2', false);
|
||||
expect(behavior.getBody()).to.be(null);
|
||||
|
||||
// Call bunch of methods that should have no impact on the object's body
|
||||
behavior.setDensity(123);
|
||||
behavior.setRestitution(0.5);
|
||||
behavior.getLinearVelocityLength();
|
||||
behavior.applyImpulse(10, -20, 0, 0);
|
||||
behavior.getMassCenterX();
|
||||
|
||||
// Object's body should still not exist
|
||||
expect(behavior.getBody()).to.be(null);
|
||||
|
||||
// Reactivate behavior
|
||||
object.activateBehavior('Physics2', true);
|
||||
expect(behavior.getBody()).not.to.be(null);
|
||||
|
||||
// Behavior should have recorded what was called with its setters while it was de-activated.
|
||||
expect(behavior.getDensity()).to.be(123);
|
||||
expect(behavior.getRestitution()).to.be(0.5);
|
||||
});
|
||||
|
||||
it('should clear contacts when deactivating the physics2 behavior', () => {
|
||||
const fps = 60;
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / fps) * 1000;
|
||||
};
|
||||
|
||||
// Create objects not in contact
|
||||
const object1 = createObject(runtimeScene, { bodyType: 'Dynamic' });
|
||||
object1.setPosition(100, 0);
|
||||
const object2 = createObject(runtimeScene, {
|
||||
bodyType: 'Static',
|
||||
restitution: 0,
|
||||
});
|
||||
object1.setPosition(0, 0);
|
||||
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const object1Behavior = object1.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const object2Behavior = object2.getBehavior('Physics2');
|
||||
if (!object2Behavior || !object1Behavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
}
|
||||
expect(object1Behavior.getBody()).not.to.be(null);
|
||||
expect(object2Behavior.getBody()).not.to.be(null);
|
||||
expect(object1Behavior._sharedData._registeredBehaviors.size).to.be(2);
|
||||
expect(
|
||||
object1Behavior._sharedData._registeredBehaviors.has(object1Behavior)
|
||||
).to.be(true);
|
||||
expect(
|
||||
object1Behavior._sharedData._registeredBehaviors.has(object2Behavior)
|
||||
).to.be(true);
|
||||
|
||||
// Put objects in contact and asset collision started during the frame
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
object1.setPosition(10, 0);
|
||||
object2.setPosition(20, 0);
|
||||
assertCollision(object1, object2, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
});
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
|
||||
// After post event, collision should be present
|
||||
assertCollision(object1, object2, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
|
||||
// Reset scene events
|
||||
runtimeScene.setEventsFunction(() => {});
|
||||
|
||||
// Deactivate physics behavior and test that collisions are cleared.
|
||||
object1.activateBehavior('Physics2', false);
|
||||
assertCollision(object1, object2, {
|
||||
started: false,
|
||||
collision: false,
|
||||
// It should be false because the condition does not have sense anymore
|
||||
// since the behavior is deactivated.
|
||||
stopped: false,
|
||||
});
|
||||
// Objects should have 0 contacts in memory.
|
||||
expect(object1Behavior.currentContacts.length).to.be(0);
|
||||
expect(object1Behavior.contactsEndedThisFrame.length).to.be(0);
|
||||
expect(object1Behavior.contactsStartedThisFrame.length).to.be(0);
|
||||
expect(object1Behavior._sharedData._registeredBehaviors.size).to.be(1);
|
||||
expect(
|
||||
object1Behavior._sharedData._registeredBehaviors.has(object1Behavior)
|
||||
).to.be(false);
|
||||
expect(
|
||||
object1Behavior._sharedData._registeredBehaviors.has(object2Behavior)
|
||||
).to.be(true);
|
||||
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
|
||||
// Reactivate physics behavior and test contact
|
||||
// is not immediately back on but after the first render.
|
||||
object1.activateBehavior('Physics2', true);
|
||||
expect(object1Behavior.currentContacts.length).to.be(0);
|
||||
expect(object1Behavior.contactsEndedThisFrame.length).to.be(0);
|
||||
expect(object1Behavior.contactsStartedThisFrame.length).to.be(0);
|
||||
expect(object1Behavior._sharedData._registeredBehaviors.size).to.be(2);
|
||||
expect(
|
||||
object1Behavior._sharedData._registeredBehaviors.has(object1Behavior)
|
||||
).to.be(true);
|
||||
expect(
|
||||
object1Behavior._sharedData._registeredBehaviors.has(object2Behavior)
|
||||
).to.be(true);
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
assertCollision(object1, object2, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
});
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
|
||||
assertCollision(object1, object2, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Contacts computation', () => {
|
||||
let runtimeGame;
|
||||
let runtimeScene;
|
||||
@@ -122,7 +393,9 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
const staticObject = createObject(runtimeScene, { bodyType: 'Static' });
|
||||
staticObject.setPosition(0, 25);
|
||||
movingObject.setPosition(0, 0);
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
@@ -156,6 +429,13 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
stepIndex++;
|
||||
}
|
||||
|
||||
// Should be cleared at next step.
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: true,
|
||||
});
|
||||
|
||||
if (!hasBounced) {
|
||||
throw new Error('Contact did not happen, nothing was tested.');
|
||||
}
|
||||
@@ -172,7 +452,9 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
const staticObject = createObject(runtimeScene, { bodyType: 'Static' });
|
||||
staticObject.setPosition(0, 25);
|
||||
movingObject.setPosition(0, 0);
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
@@ -232,6 +514,257 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
throw new Error('End of contact was not detected, nothing was tested.');
|
||||
}
|
||||
});
|
||||
|
||||
it('should not detect a contact while already in contact as a new one (the contact jittered).', () => {
|
||||
const fps = 50;
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / fps) * 1000;
|
||||
};
|
||||
|
||||
const movingObject = createObject(runtimeScene);
|
||||
const staticObject = createObject(runtimeScene, {
|
||||
bodyType: 'Static',
|
||||
restitution: 0,
|
||||
});
|
||||
staticObject.setPosition(0, 9);
|
||||
movingObject.setPosition(0, 0);
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
}
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
// Manually call onContactEnd and onContactBegin methods to simulate
|
||||
// a loss of contact followed by a contact beginning during the event.
|
||||
movingObject
|
||||
.getBehavior('Physics2')
|
||||
.onContactEnd(staticObject.getBehavior('Physics2'));
|
||||
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
|
||||
movingObject
|
||||
.getBehavior('Physics2')
|
||||
.onContactBegin(staticObject.getBehavior('Physics2'));
|
||||
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
});
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
});
|
||||
|
||||
it('should not detect a new contact if the contact ended and jittered.', () => {
|
||||
const fps = 50;
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / fps) * 1000;
|
||||
};
|
||||
|
||||
const movingObject = createObject(runtimeScene);
|
||||
const staticObject = createObject(runtimeScene, {
|
||||
bodyType: 'Static',
|
||||
restitution: 0,
|
||||
});
|
||||
staticObject.setPosition(0, 4);
|
||||
movingObject.setPosition(0, 0);
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
}
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
// Manually call onContactEnd and onContactBegin methods to simulate
|
||||
// a loss of contact followed by a contact beginning and another loss
|
||||
// of contact during the event.
|
||||
movingObject
|
||||
.getBehavior('Physics2')
|
||||
.onContactEnd(staticObject.getBehavior('Physics2'));
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
|
||||
movingObject
|
||||
.getBehavior('Physics2')
|
||||
.onContactBegin(staticObject.getBehavior('Physics2'));
|
||||
|
||||
// Started is false because it is like the jittered contact case.
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
|
||||
movingObject
|
||||
.getBehavior('Physics2')
|
||||
.onContactEnd(staticObject.getBehavior('Physics2'));
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
});
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
});
|
||||
|
||||
it('it should end collision on resize (body updated in pre-event).', () => {
|
||||
const fps = 50;
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / fps) * 1000;
|
||||
};
|
||||
|
||||
const movingObject = createObject(runtimeScene);
|
||||
const staticObject = createObject(runtimeScene, {
|
||||
bodyType: 'Static',
|
||||
restitution: 0,
|
||||
});
|
||||
staticObject.setPosition(0, 9);
|
||||
movingObject.setPosition(0, 0);
|
||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
}
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
});
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
|
||||
// Resize (postEvent operation).
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
movingObject.setCustomWidthAndHeight(5, 5);
|
||||
|
||||
// Body should be updated next frame.
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
});
|
||||
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
});
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('it should end collision on object destruction (loss of contact begins during event).', () => {
|
||||
const fps = 50;
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / fps) * 1000;
|
||||
};
|
||||
|
||||
const movingObject = createObject(runtimeScene);
|
||||
const staticObject = createObject(runtimeScene, {
|
||||
bodyType: 'Static',
|
||||
restitution: 0,
|
||||
});
|
||||
staticObject.setPosition(0, 9);
|
||||
movingObject.setPosition(0, 0);
|
||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
}
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
});
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: false,
|
||||
});
|
||||
|
||||
// Destroy (postEvent operation).
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
movingObject.deleteFromScene(runtimeScene);
|
||||
|
||||
// Collision should be reset on destroyed object and
|
||||
// added to contactsStoppedThisFrame array of the other object.
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: false,
|
||||
});
|
||||
assertCollision(staticObject, movingObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
});
|
||||
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: false,
|
||||
});
|
||||
assertCollision(staticObject, movingObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onContactBegin', () => {
|
||||
@@ -248,7 +781,9 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
const object = createObject(runtimeScene);
|
||||
const otherObject = createObject(runtimeScene);
|
||||
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const behavior = object.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const otherBehavior = otherObject.getBehavior('Physics2');
|
||||
if (!behavior || !otherBehavior) {
|
||||
throw new Error('Behavior not found, test cannot be run.');
|
||||
@@ -270,7 +805,9 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
const object = createObject(runtimeScene);
|
||||
const otherObject = createObject(runtimeScene);
|
||||
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const behavior = object.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const otherBehavior = otherObject.getBehavior('Physics2');
|
||||
if (!behavior || !otherBehavior) {
|
||||
throw new Error('Behavior not found, test cannot be run.');
|
||||
@@ -300,7 +837,9 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
const object = createObject(runtimeScene);
|
||||
const otherObject = createObject(runtimeScene);
|
||||
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const behavior = object.getBehavior('Physics2');
|
||||
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||
const otherBehavior = otherObject.getBehavior('Physics2');
|
||||
if (!behavior || !otherBehavior) {
|
||||
throw new Error('Behavior not found, test cannot be run.');
|
||||
@@ -318,4 +857,104 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
expect(behavior.contactsEndedThisFrame.length).to.be(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Behavior interaction with other objects behaviors and extensions.', () => {
|
||||
let runtimeGame;
|
||||
let runtimeScene;
|
||||
beforeEach(() => {
|
||||
[runtimeGame, runtimeScene] = createGameWithSceneWithPhysics2SharedData();
|
||||
});
|
||||
it('BehaviorTest should have access to current started and ended contacts in postEvent.', () => {
|
||||
const fps = 2;
|
||||
runtimeGame.setGameResolutionSize(1000, 1000);
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return (1 / fps) * 1000;
|
||||
};
|
||||
|
||||
const staticObject = createObject(runtimeScene, {
|
||||
bodyType: 'Static',
|
||||
});
|
||||
// Creating the object with the other behavior after the Physics2 only one
|
||||
// should make its behavior's post event play after the Physics2 post event.
|
||||
const movingObjectWithOtherBehavior = createObject(runtimeScene);
|
||||
movingObjectWithOtherBehavior.addNewBehavior({
|
||||
name: 'BehaviorTest',
|
||||
type: 'Physics2::BehaviorTest',
|
||||
other: staticObject,
|
||||
});
|
||||
runtimeScene.addObject(movingObjectWithOtherBehavior);
|
||||
|
||||
/** @type {BehaviorTest | null} */
|
||||
const behaviorTest = movingObjectWithOtherBehavior.getBehavior(
|
||||
'BehaviorTest'
|
||||
);
|
||||
if (!behaviorTest) {
|
||||
throw new Error('Test behavior not found, test cannot be run.');
|
||||
}
|
||||
|
||||
staticObject.setPosition(0, 25);
|
||||
movingObjectWithOtherBehavior.setPosition(0, 0);
|
||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||
const movingObjectBehavior = movingObjectWithOtherBehavior.getBehavior(
|
||||
'Physics2'
|
||||
);
|
||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||
throw new Error('Behaviors not found, test cannot be run.');
|
||||
}
|
||||
|
||||
movingObjectBehavior.setLinearVelocityY(40000);
|
||||
let hasBounced = false;
|
||||
let stepIndex = 0;
|
||||
behaviorTest.activatePostEvent(true);
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
if (movingObjectBehavior.getLinearVelocityY() > 0) {
|
||||
const expectedCollisionTest = {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: false,
|
||||
};
|
||||
// If the moving object has a positive velocity, it hasn't bounced
|
||||
// on the static object
|
||||
assertCollision(
|
||||
movingObjectWithOtherBehavior,
|
||||
staticObject,
|
||||
expectedCollisionTest
|
||||
);
|
||||
behaviorTest.setExpectedCollisions(expectedCollisionTest);
|
||||
} else {
|
||||
hasBounced = true;
|
||||
const expectedCollisionTest = {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: true,
|
||||
};
|
||||
expect(
|
||||
movingObjectWithOtherBehavior.getY() < staticObject.getY()
|
||||
).to.be(true);
|
||||
assertCollision(
|
||||
movingObjectWithOtherBehavior,
|
||||
staticObject,
|
||||
expectedCollisionTest
|
||||
);
|
||||
behaviorTest.setExpectedCollisions(expectedCollisionTest);
|
||||
}
|
||||
});
|
||||
while (stepIndex < 10 && !hasBounced) {
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
stepIndex++;
|
||||
}
|
||||
|
||||
// Should be cleared at next step.
|
||||
assertCollision(movingObjectWithOtherBehavior, staticObject, {
|
||||
started: true,
|
||||
collision: true,
|
||||
stopped: true,
|
||||
});
|
||||
|
||||
if (!hasBounced) {
|
||||
throw new Error('Contact did not happen, nothing was tested.');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,108 +0,0 @@
|
||||
// @ts-check
|
||||
|
||||
describe('computeCurrentContactsFromStartedAndEndedContacts', () => {
|
||||
it('returns same current contacts if nothing happened', () => {
|
||||
const contacts = ['A', 'B', 'C'];
|
||||
const startedContacts = [];
|
||||
const endedContacts = [];
|
||||
const expectedResolvedContacts = [...contacts];
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
contacts,
|
||||
startedContacts,
|
||||
endedContacts
|
||||
);
|
||||
expect(contacts).to.eql(expectedResolvedContacts);
|
||||
});
|
||||
|
||||
it('returns current contacts with started contacts added', () => {
|
||||
const contacts = ['A', 'B', 'C'];
|
||||
const startedContacts = ['Z', 'Q'];
|
||||
const endedContacts = [];
|
||||
const expectedResolvedContacts = [...contacts, ...startedContacts];
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
contacts,
|
||||
startedContacts,
|
||||
endedContacts
|
||||
);
|
||||
expect(contacts).to.eql(expectedResolvedContacts);
|
||||
});
|
||||
|
||||
it('returns current contacts with ended contacts removed', () => {
|
||||
const contacts = ['A', 'B', 'C'];
|
||||
const startedContacts = [];
|
||||
const endedContacts = ['A', 'C'];
|
||||
const expectedResolvedContacts = ['B'];
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
contacts,
|
||||
startedContacts,
|
||||
endedContacts
|
||||
);
|
||||
expect(contacts).to.eql(expectedResolvedContacts);
|
||||
});
|
||||
|
||||
it('returns same current contacts if all started contacts also ended', () => {
|
||||
const contacts = ['A', 'B', 'C'];
|
||||
const startedContacts = ['Z', 'X'];
|
||||
const endedContacts = ['Z', 'X'];
|
||||
const expectedResolvedContacts = [...contacts];
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
contacts,
|
||||
startedContacts,
|
||||
endedContacts
|
||||
);
|
||||
expect(contacts).to.eql(expectedResolvedContacts);
|
||||
});
|
||||
|
||||
it('returns current contacts without started contacts that also ended', () => {
|
||||
const contacts = ['A', 'B', 'C'];
|
||||
const startedContacts = ['Z', 'X', 'W'];
|
||||
const endedContacts = ['Z', 'A', 'X'];
|
||||
const expectedResolvedContacts = ['B', 'C', 'W'];
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
contacts,
|
||||
startedContacts,
|
||||
endedContacts
|
||||
);
|
||||
expect(contacts).to.eql(expectedResolvedContacts);
|
||||
});
|
||||
|
||||
it('returns current contacts with a contact that started and also jittered', () => {
|
||||
// Should handle cases when this happens during the frame:
|
||||
// - contact Z starts
|
||||
// - contact Z ends
|
||||
// - contact Z starts
|
||||
// Contact Z should appear in the current contacts.
|
||||
// We consider a contact shouldn't be able to do that but it should be handled
|
||||
// in case it happens.
|
||||
const contacts = ['A', 'B', 'C'];
|
||||
const startedContacts = ['Z', 'Z'];
|
||||
const endedContacts = ['Z'];
|
||||
const expectedResolvedContacts = [...contacts, 'Z'];
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
contacts,
|
||||
startedContacts,
|
||||
endedContacts
|
||||
);
|
||||
expect(contacts).to.eql(expectedResolvedContacts);
|
||||
});
|
||||
|
||||
it('returns current contacts without a contact that ended and also jittered', () => {
|
||||
// Should handle cases where contact C was here and, during the frame:
|
||||
// - contact C ends
|
||||
// - contact C starts
|
||||
// - contact C ends
|
||||
// Contact C should not appear in the current contacts
|
||||
// We consider a contact shouldn't be able to do that but it should be handled
|
||||
// in case it happens.
|
||||
const contacts = ['A', 'B', 'C'];
|
||||
const startedContacts = ['C'];
|
||||
const endedContacts = ['C', 'C'];
|
||||
const expectedResolvedContacts = ['A', 'B'];
|
||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
||||
contacts,
|
||||
startedContacts,
|
||||
endedContacts
|
||||
);
|
||||
expect(contacts).to.eql(expectedResolvedContacts);
|
||||
});
|
||||
});
|
@@ -1,19 +0,0 @@
|
||||
namespace gdjs {
|
||||
export namespace physics2 {
|
||||
export const computeCurrentContactsFromStartedAndEndedContacts = <T>(
|
||||
current: Array<T>,
|
||||
started: Array<T>,
|
||||
ended: Array<T>
|
||||
): void => {
|
||||
started.forEach((startedItem) => {
|
||||
current.push(startedItem);
|
||||
});
|
||||
ended.forEach((endedItem) => {
|
||||
const index = current.indexOf(endedItem);
|
||||
if (index !== -1) {
|
||||
current.splice(index, 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
@@ -75,7 +75,6 @@ module.exports = function (config) {
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/LinkedObjects/linkedobjects.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Inventory/inventory.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Inventory/inventorytools.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/utils.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/box2d.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/physics2tools.js',
|
||||
|
Reference in New Issue
Block a user