mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
18 Commits
v5.5.225
...
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).
|
# Note: Code signing is done using CSC_LINK (see https://www.electron.build/code-signing).
|
||||||
- run:
|
- run:
|
||||||
name: Build GDevelop IDE
|
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:
|
- run:
|
||||||
name: Clean dist folder to keep only installers/binaries.
|
name: Clean dist folder to keep only installers/binaries.
|
||||||
@@ -101,8 +101,8 @@ jobs:
|
|||||||
command: sudo apt-get update && sudo apt install cmake
|
command: sudo apt-get update && sudo apt install cmake
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Python3 dependencies for Emscripten
|
name: Install Python3 dependencies for Emscripten
|
||||||
command: sudo apt install python-is-python3 python3-distutils -y
|
command: sudo apt install python-is-python3 python3-distutils -y
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Emscripten (for GDevelop.js)
|
name: Install Emscripten (for GDevelop.js)
|
||||||
@@ -178,8 +178,8 @@ jobs:
|
|||||||
command: sudo apt-get update && sudo apt install cmake
|
command: sudo apt-get update && sudo apt install cmake
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Python3 dependencies for Emscripten
|
name: Install Python3 dependencies for Emscripten
|
||||||
command: sudo apt install python-is-python3 python3-distutils -y
|
command: sudo apt install python-is-python3 python3-distutils -y
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Install Emscripten (for GDevelop.js)
|
name: Install Emscripten (for GDevelop.js)
|
||||||
|
@@ -420,7 +420,6 @@ module.exports = {
|
|||||||
)
|
)
|
||||||
.setIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
|
.setIncludeFile('Extensions/Physics2Behavior/physics2runtimebehavior.js')
|
||||||
.addIncludeFile('Extensions/Physics2Behavior/box2d.js')
|
.addIncludeFile('Extensions/Physics2Behavior/box2d.js')
|
||||||
.addIncludeFile('Extensions/Physics2Behavior/utils.js');
|
|
||||||
|
|
||||||
// Global
|
// Global
|
||||||
aut
|
aut
|
||||||
|
@@ -20,7 +20,13 @@ namespace gdjs {
|
|||||||
// Start with 1 so the user is safe from default variables value (0)
|
// Start with 1 so the user is safe from default variables value (0)
|
||||||
joints: any = {};
|
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) {
|
constructor(runtimeScene, sharedData) {
|
||||||
|
this._registeredBehaviors = new Set();
|
||||||
this.gravityX = sharedData.gravityX;
|
this.gravityX = sharedData.gravityX;
|
||||||
this.gravityY = sharedData.gravityY;
|
this.gravityY = sharedData.gravityY;
|
||||||
this.scaleX = sharedData.scaleX === 0 ? 100 : sharedData.scaleX;
|
this.scaleX = sharedData.scaleX === 0 ? 100 : sharedData.scaleX;
|
||||||
@@ -104,6 +110,40 @@ namespace gdjs {
|
|||||||
return runtimeScene.physics2SharedData;
|
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) {
|
step(deltaTime) {
|
||||||
this.frameTime += deltaTime;
|
this.frameTime += deltaTime;
|
||||||
if (this.frameTime >= this.timeStep) {
|
if (this.frameTime >= this.timeStep) {
|
||||||
@@ -241,13 +281,29 @@ namespace gdjs {
|
|||||||
layers: any;
|
layers: any;
|
||||||
masks: any;
|
masks: any;
|
||||||
shapeScale: number = 1;
|
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>;
|
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>;
|
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>;
|
currentContacts: Array<Physics2RuntimeBehavior>;
|
||||||
|
destroyedDuringFrameLogic: boolean;
|
||||||
_body: any = null;
|
_body: any = null;
|
||||||
_sharedData: any;
|
|
||||||
_tempb2Vec2: 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
|
// Avoid creating new vectors all the time
|
||||||
_tempb2Vec2Sec: any;
|
_tempb2Vec2Sec: any;
|
||||||
|
|
||||||
@@ -287,12 +343,14 @@ namespace gdjs {
|
|||||||
this.contactsEndedThisFrame = [];
|
this.contactsEndedThisFrame = [];
|
||||||
this.currentContacts = [];
|
this.currentContacts = [];
|
||||||
this.currentContacts.length = 0;
|
this.currentContacts.length = 0;
|
||||||
|
this.destroyedDuringFrameLogic = false;
|
||||||
this._sharedData = Physics2SharedData.getSharedData(
|
this._sharedData = Physics2SharedData.getSharedData(
|
||||||
runtimeScene,
|
runtimeScene,
|
||||||
behaviorData.name
|
behaviorData.name
|
||||||
);
|
);
|
||||||
this._tempb2Vec2 = new Box2D.b2Vec2();
|
this._tempb2Vec2 = new Box2D.b2Vec2();
|
||||||
this._tempb2Vec2Sec = new Box2D.b2Vec2();
|
this._tempb2Vec2Sec = new Box2D.b2Vec2();
|
||||||
|
this._sharedData.addToBehaviorsList(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores a Box2D pointer of created vertices
|
// Stores a Box2D pointer of created vertices
|
||||||
@@ -377,6 +435,7 @@ namespace gdjs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDeActivate() {
|
onDeActivate() {
|
||||||
|
this._sharedData.removeFromBehaviorsList(this);
|
||||||
if (this._body !== null) {
|
if (this._body !== null) {
|
||||||
// When a body is deleted, Box2D removes automatically its joints, leaving an invalid pointer in our joints list
|
// When a body is deleted, Box2D removes automatically its joints, leaving an invalid pointer in our joints list
|
||||||
this._sharedData.clearBodyJoints(this._body);
|
this._sharedData.clearBodyJoints(this._body);
|
||||||
@@ -391,9 +450,22 @@ namespace gdjs {
|
|||||||
this._sharedData.world.DestroyBody(this._body);
|
this._sharedData.world.DestroyBody(this._body);
|
||||||
this._body = null;
|
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() {
|
onDestroy() {
|
||||||
|
this.destroyedDuringFrameLogic = true;
|
||||||
this.onDeActivate();
|
this.onDeActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,8 +713,7 @@ namespace gdjs {
|
|||||||
recreateShape() {
|
recreateShape() {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy the old shape
|
// Destroy the old shape
|
||||||
@@ -673,7 +744,8 @@ namespace gdjs {
|
|||||||
return this._body;
|
return this._body;
|
||||||
}
|
}
|
||||||
|
|
||||||
createBody() {
|
createBody(): boolean {
|
||||||
|
if (!this.activated() || this.destroyedDuringFrameLogic) return false;
|
||||||
// Generate the body definition
|
// Generate the body definition
|
||||||
const bodyDef = new Box2D.b2BodyDef();
|
const bodyDef = new Box2D.b2BodyDef();
|
||||||
|
|
||||||
@@ -711,16 +783,15 @@ namespace gdjs {
|
|||||||
// Update cached size
|
// Update cached size
|
||||||
this._objectOldWidth = this.owner.getWidth();
|
this._objectOldWidth = this.owner.getWidth();
|
||||||
this._objectOldHeight = this.owner.getHeight();
|
this._objectOldHeight = this.owner.getHeight();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
doStepPreEvents(runtimeScene) {
|
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
|
// Step the world if not done this frame yet
|
||||||
if (!this._sharedData.stepped) {
|
if (!this._sharedData.stepped) {
|
||||||
|
// Reset started and ended contacts array for all physics instances.
|
||||||
|
this._sharedData.resetStartedAndEndedCollisions();
|
||||||
|
this._sharedData.updateBodiesFromObjects();
|
||||||
this._sharedData.step(
|
this._sharedData.step(
|
||||||
runtimeScene.getTimeManager().getElapsedTime() / 1000.0
|
runtimeScene.getTimeManager().getElapsedTime() / 1000.0
|
||||||
);
|
);
|
||||||
@@ -741,37 +812,25 @@ namespace gdjs {
|
|||||||
);
|
);
|
||||||
this.owner.setAngle(gdjs.toDegrees(this._body.GetAngle()));
|
this.owner.setAngle(gdjs.toDegrees(this._body.GetAngle()));
|
||||||
|
|
||||||
// Update cached transform
|
// Update cached transform.
|
||||||
this._objectOldX = this.owner.getX();
|
this._objectOldX = this.owner.getX();
|
||||||
this._objectOldY = this.owner.getY();
|
this._objectOldY = this.owner.getY();
|
||||||
this._objectOldAngle = this.owner.getAngle();
|
this._objectOldAngle = this.owner.getAngle();
|
||||||
|
|
||||||
gdjs.physics2.computeCurrentContactsFromStartedAndEndedContacts(
|
|
||||||
this.currentContacts,
|
|
||||||
this.contactsStartedThisFrame,
|
|
||||||
this.contactsEndedThisFrame
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doStepPostEvents(runtimeScene) {
|
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
|
// Reset world step to update next frame
|
||||||
this._sharedData.stepped = false;
|
this._sharedData.stepped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onObjectHotReloaded() {
|
onObjectHotReloaded() {
|
||||||
this._updateBodyFromObject();
|
this.updateBodyFromObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateBodyFromObject() {
|
updateBodyFromObject() {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The object size has changed, recreate the shape.
|
// The object size has changed, recreate the shape.
|
||||||
@@ -868,8 +927,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update body type
|
// Update body type
|
||||||
@@ -892,8 +950,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update body type
|
// Update body type
|
||||||
@@ -916,8 +973,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update body type
|
// Update body type
|
||||||
@@ -940,8 +996,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update body bullet flag
|
// Update body bullet flag
|
||||||
@@ -955,8 +1010,7 @@ namespace gdjs {
|
|||||||
setFixedRotation(enable): void {
|
setFixedRotation(enable): void {
|
||||||
this.fixedRotation = enable;
|
this.fixedRotation = enable;
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this._body.SetFixedRotation(this.fixedRotation);
|
this._body.SetFixedRotation(this.fixedRotation);
|
||||||
}
|
}
|
||||||
@@ -968,8 +1022,7 @@ namespace gdjs {
|
|||||||
setSleepingAllowed(enable): void {
|
setSleepingAllowed(enable): void {
|
||||||
this.canSleep = enable;
|
this.canSleep = enable;
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
this._body.SetSleepingAllowed(this.canSleep);
|
this._body.SetSleepingAllowed(this.canSleep);
|
||||||
}
|
}
|
||||||
@@ -977,7 +1030,7 @@ namespace gdjs {
|
|||||||
isSleeping(): boolean {
|
isSleeping(): boolean {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the body sleeping state
|
// Get the body sleeping state
|
||||||
@@ -1004,8 +1057,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body density
|
// Update the body density
|
||||||
@@ -1033,8 +1085,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body friction
|
// Update the body friction
|
||||||
@@ -1068,8 +1119,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body restitution
|
// Update the body restitution
|
||||||
@@ -1098,8 +1148,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body linear damping
|
// Update the body linear damping
|
||||||
@@ -1121,8 +1170,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body angular damping
|
// Update the body angular damping
|
||||||
@@ -1144,8 +1192,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body gravity scale
|
// Update the body gravity scale
|
||||||
@@ -1181,8 +1228,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body layers
|
// Update the body layers
|
||||||
@@ -1220,8 +1266,7 @@ namespace gdjs {
|
|||||||
|
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the body masks
|
// Update the body masks
|
||||||
@@ -1233,8 +1278,7 @@ namespace gdjs {
|
|||||||
getLinearVelocityX(): float {
|
getLinearVelocityX(): float {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the linear velocity on X
|
// Get the linear velocity on X
|
||||||
@@ -1244,7 +1288,7 @@ namespace gdjs {
|
|||||||
setLinearVelocityX(linearVelocityX): void {
|
setLinearVelocityX(linearVelocityX): void {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the linear velocity on X
|
// Set the linear velocity on X
|
||||||
@@ -1259,8 +1303,7 @@ namespace gdjs {
|
|||||||
getLinearVelocityY(): float {
|
getLinearVelocityY(): float {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the linear velocity on Y
|
// Get the linear velocity on Y
|
||||||
@@ -1270,7 +1313,7 @@ namespace gdjs {
|
|||||||
setLinearVelocityY(linearVelocityY): void {
|
setLinearVelocityY(linearVelocityY): void {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the linear velocity on Y
|
// 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 there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the linear velocity length
|
// Get the linear velocity length
|
||||||
@@ -1296,10 +1338,10 @@ namespace gdjs {
|
|||||||
).Length();
|
).Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
getAngularVelocity() {
|
getAngularVelocity(): float {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the angular velocity
|
// Get the angular velocity
|
||||||
@@ -1309,7 +1351,7 @@ namespace gdjs {
|
|||||||
setAngularVelocity(angularVelocity): void {
|
setAngularVelocity(angularVelocity): void {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the angular velocity
|
// Set the angular velocity
|
||||||
@@ -1319,7 +1361,7 @@ namespace gdjs {
|
|||||||
applyForce(forceX, forceY, positionX, positionY) {
|
applyForce(forceX, forceY, positionX, positionY) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1338,7 +1380,7 @@ namespace gdjs {
|
|||||||
applyPolarForce(angle, length, positionX, positionY) {
|
applyPolarForce(angle, length, positionX, positionY) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1358,7 +1400,7 @@ namespace gdjs {
|
|||||||
applyForceTowardPosition(length, towardX, towardY, positionX, positionY) {
|
applyForceTowardPosition(length, towardX, towardY, positionX, positionY) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1381,7 +1423,7 @@ namespace gdjs {
|
|||||||
applyImpulse(impulseX, impulseY, positionX, positionY) {
|
applyImpulse(impulseX, impulseY, positionX, positionY) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1400,7 +1442,7 @@ namespace gdjs {
|
|||||||
applyPolarImpulse(angle, length, positionX, positionY) {
|
applyPolarImpulse(angle, length, positionX, positionY) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1420,7 +1462,7 @@ namespace gdjs {
|
|||||||
applyImpulseTowardPosition(length, towardX, towardY, positionX, positionY) {
|
applyImpulseTowardPosition(length, towardX, towardY, positionX, positionY) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1443,7 +1485,7 @@ namespace gdjs {
|
|||||||
applyTorque(torque) {
|
applyTorque(torque) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1456,7 +1498,7 @@ namespace gdjs {
|
|||||||
applyAngularImpulse(angularImpulse) {
|
applyAngularImpulse(angularImpulse) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1469,7 +1511,7 @@ namespace gdjs {
|
|||||||
getMass(): float {
|
getMass(): float {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1481,7 +1523,7 @@ namespace gdjs {
|
|||||||
getInertia(): float {
|
getInertia(): float {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the object
|
// Wake up the object
|
||||||
@@ -1493,7 +1535,7 @@ namespace gdjs {
|
|||||||
getMassCenterX(): float {
|
getMassCenterX(): float {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the mass center on X
|
// Get the mass center on X
|
||||||
@@ -1503,7 +1545,7 @@ namespace gdjs {
|
|||||||
getMassCenterY(): float {
|
getMassCenterY(): float {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the mass center on Y
|
// Get the mass center on Y
|
||||||
@@ -1514,8 +1556,7 @@ namespace gdjs {
|
|||||||
isJointFirstObject(jointId): boolean {
|
isJointFirstObject(jointId): boolean {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the joint
|
// Get the joint
|
||||||
@@ -1533,8 +1574,7 @@ namespace gdjs {
|
|||||||
isJointSecondObject(jointId): boolean {
|
isJointSecondObject(jointId): boolean {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return false;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the joint
|
// Get the joint
|
||||||
@@ -1647,7 +1687,7 @@ namespace gdjs {
|
|||||||
) {
|
) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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 there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set joint settings
|
// Set joint settings
|
||||||
@@ -1887,7 +1927,7 @@ namespace gdjs {
|
|||||||
) {
|
) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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 there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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 there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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) {
|
addGearJoint(jointId1, jointId2, ratio, collideConnected, variable) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the first joint
|
// Get the first joint
|
||||||
@@ -2798,7 +2838,7 @@ namespace gdjs {
|
|||||||
) {
|
) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set joint settings
|
// Set joint settings
|
||||||
@@ -2973,7 +3013,7 @@ namespace gdjs {
|
|||||||
) {
|
) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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 there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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) {
|
addRopeJoint(x1, y1, other, x2, y2, maxLength, collideConnected, variable) {
|
||||||
// If there is no body, set a new one
|
// If there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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 there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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 there is no body, set a new one
|
||||||
if (this._body === null) {
|
if (this._body === null) {
|
||||||
this.createBody();
|
if (!this.createBody()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no second object or it doesn't share the behavior, 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
|
// start again right away. It is considered a glitch
|
||||||
// and should not be detected.
|
// and should not be detected.
|
||||||
let i = this.contactsEndedThisFrame.indexOf(otherBehavior);
|
let i = this.contactsEndedThisFrame.indexOf(otherBehavior);
|
||||||
|
this.currentContacts.push(otherBehavior);
|
||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
this.contactsEndedThisFrame.splice(i, 1);
|
this.contactsEndedThisFrame.splice(i, 1);
|
||||||
} else {
|
} else {
|
||||||
@@ -3849,6 +3890,10 @@ namespace gdjs {
|
|||||||
|
|
||||||
onContactEnd(otherBehavior: Physics2RuntimeBehavior) {
|
onContactEnd(otherBehavior: Physics2RuntimeBehavior) {
|
||||||
this.contactsEndedThisFrame.push(otherBehavior);
|
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];
|
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) {
|
function createObject(runtimeScene, behaviorProperties) {
|
||||||
const object = new gdjs.TestRuntimeObject(runtimeScene, {
|
const object = new gdjs.TestRuntimeObject(runtimeScene, {
|
||||||
name: 'obj1',
|
name: 'obj1',
|
||||||
@@ -104,6 +158,223 @@ function createObject(runtimeScene, behaviorProperties) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('Physics2RuntimeBehavior', () => {
|
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', () => {
|
describe('Contacts computation', () => {
|
||||||
let runtimeGame;
|
let runtimeGame;
|
||||||
let runtimeScene;
|
let runtimeScene;
|
||||||
@@ -122,7 +393,9 @@ describe('Physics2RuntimeBehavior', () => {
|
|||||||
const staticObject = createObject(runtimeScene, { bodyType: 'Static' });
|
const staticObject = createObject(runtimeScene, { bodyType: 'Static' });
|
||||||
staticObject.setPosition(0, 25);
|
staticObject.setPosition(0, 25);
|
||||||
movingObject.setPosition(0, 0);
|
movingObject.setPosition(0, 0);
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||||
throw new Error('Behaviors not found, test cannot be run.');
|
throw new Error('Behaviors not found, test cannot be run.');
|
||||||
@@ -156,6 +429,13 @@ describe('Physics2RuntimeBehavior', () => {
|
|||||||
stepIndex++;
|
stepIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should be cleared at next step.
|
||||||
|
assertCollision(movingObject, staticObject, {
|
||||||
|
started: true,
|
||||||
|
collision: true,
|
||||||
|
stopped: true,
|
||||||
|
});
|
||||||
|
|
||||||
if (!hasBounced) {
|
if (!hasBounced) {
|
||||||
throw new Error('Contact did not happen, nothing was tested.');
|
throw new Error('Contact did not happen, nothing was tested.');
|
||||||
}
|
}
|
||||||
@@ -172,7 +452,9 @@ describe('Physics2RuntimeBehavior', () => {
|
|||||||
const staticObject = createObject(runtimeScene, { bodyType: 'Static' });
|
const staticObject = createObject(runtimeScene, { bodyType: 'Static' });
|
||||||
staticObject.setPosition(0, 25);
|
staticObject.setPosition(0, 25);
|
||||||
movingObject.setPosition(0, 0);
|
movingObject.setPosition(0, 0);
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
const staticObjectBehavior = staticObject.getBehavior('Physics2');
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
const movingObjectBehavior = movingObject.getBehavior('Physics2');
|
||||||
if (!staticObjectBehavior || !movingObjectBehavior) {
|
if (!staticObjectBehavior || !movingObjectBehavior) {
|
||||||
throw new Error('Behaviors not found, test cannot be run.');
|
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.');
|
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', () => {
|
describe('onContactBegin', () => {
|
||||||
@@ -248,7 +781,9 @@ describe('Physics2RuntimeBehavior', () => {
|
|||||||
const object = createObject(runtimeScene);
|
const object = createObject(runtimeScene);
|
||||||
const otherObject = createObject(runtimeScene);
|
const otherObject = createObject(runtimeScene);
|
||||||
|
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const behavior = object.getBehavior('Physics2');
|
const behavior = object.getBehavior('Physics2');
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const otherBehavior = otherObject.getBehavior('Physics2');
|
const otherBehavior = otherObject.getBehavior('Physics2');
|
||||||
if (!behavior || !otherBehavior) {
|
if (!behavior || !otherBehavior) {
|
||||||
throw new Error('Behavior not found, test cannot be run.');
|
throw new Error('Behavior not found, test cannot be run.');
|
||||||
@@ -270,7 +805,9 @@ describe('Physics2RuntimeBehavior', () => {
|
|||||||
const object = createObject(runtimeScene);
|
const object = createObject(runtimeScene);
|
||||||
const otherObject = createObject(runtimeScene);
|
const otherObject = createObject(runtimeScene);
|
||||||
|
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const behavior = object.getBehavior('Physics2');
|
const behavior = object.getBehavior('Physics2');
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const otherBehavior = otherObject.getBehavior('Physics2');
|
const otherBehavior = otherObject.getBehavior('Physics2');
|
||||||
if (!behavior || !otherBehavior) {
|
if (!behavior || !otherBehavior) {
|
||||||
throw new Error('Behavior not found, test cannot be run.');
|
throw new Error('Behavior not found, test cannot be run.');
|
||||||
@@ -300,7 +837,9 @@ describe('Physics2RuntimeBehavior', () => {
|
|||||||
const object = createObject(runtimeScene);
|
const object = createObject(runtimeScene);
|
||||||
const otherObject = createObject(runtimeScene);
|
const otherObject = createObject(runtimeScene);
|
||||||
|
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const behavior = object.getBehavior('Physics2');
|
const behavior = object.getBehavior('Physics2');
|
||||||
|
/** @type {gdjs.Physics2RuntimeBehavior | null} */
|
||||||
const otherBehavior = otherObject.getBehavior('Physics2');
|
const otherBehavior = otherObject.getBehavior('Physics2');
|
||||||
if (!behavior || !otherBehavior) {
|
if (!behavior || !otherBehavior) {
|
||||||
throw new Error('Behavior not found, test cannot be run.');
|
throw new Error('Behavior not found, test cannot be run.');
|
||||||
@@ -318,4 +857,104 @@ describe('Physics2RuntimeBehavior', () => {
|
|||||||
expect(behavior.contactsEndedThisFrame.length).to.be(0);
|
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/LinkedObjects/linkedobjects.js',
|
||||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Inventory/inventory.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/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/box2d.js',
|
||||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js',
|
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js',
|
||||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/physics2tools.js',
|
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Physics2Behavior/physics2tools.js',
|
||||||
|
Reference in New Issue
Block a user