mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
22 Commits
v5.0.137
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e503869798 | ||
![]() |
643dbf5329 | ||
![]() |
dddd46a1c1 | ||
![]() |
3f9408575f | ||
![]() |
d38e9330dd | ||
![]() |
fcdc24d4ce | ||
![]() |
47fb1bf6fa | ||
![]() |
8b83436cfe | ||
![]() |
d0a5016a31 | ||
![]() |
969b6815c9 | ||
![]() |
d91fd896ac | ||
![]() |
03a2c04b45 | ||
![]() |
72bccfef13 | ||
![]() |
133fecb34f | ||
![]() |
ac26ca5d9d | ||
![]() |
c7971a9b52 | ||
![]() |
d79886e93c | ||
![]() |
6a05c6ff6b | ||
![]() |
81ef11163d | ||
![]() |
fcc19a6dcf | ||
![]() |
bcad2d5667 | ||
![]() |
3c83e5d24a |
@@ -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,20 +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();
|
||||
}
|
||||
|
||||
// Reset contacts that happened this frame
|
||||
this.contactsStartedThisFrame.length = 0;
|
||||
this.contactsEndedThisFrame.length = 0;
|
||||
|
||||
// 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
|
||||
);
|
||||
@@ -745,33 +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 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.');
|
||||
@@ -131,8 +404,8 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
|
||||
let hasBounced = false;
|
||||
let stepIndex = 0;
|
||||
while (stepIndex < 10 && !hasBounced) {
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
if (movingObjectBehavior.getLinearVelocityY() > 0) {
|
||||
// If the moving object has a positive velocity, it hasn't bounced
|
||||
// on the static object
|
||||
@@ -150,9 +423,19 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
stopped: true,
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
while (stepIndex < 10 && !hasBounced) {
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
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.');
|
||||
}
|
||||
@@ -169,17 +452,19 @@ 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.');
|
||||
}
|
||||
movingObjectBehavior.setLinearVelocityY(40000);
|
||||
|
||||
let hasBounced = false;
|
||||
let hasBegunBouncing = false;
|
||||
let stepIndex = 0;
|
||||
while (stepIndex < 10 && !hasBounced) {
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
if (movingObjectBehavior.getLinearVelocityY() > 0) {
|
||||
// If the moving object has a positive velocity, it hasn't bounced
|
||||
// on the static object
|
||||
@@ -189,7 +474,8 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
stopped: false,
|
||||
});
|
||||
} else {
|
||||
hasBounced = true;
|
||||
hasBegunBouncing = true;
|
||||
// At first frame, collision should have only started
|
||||
expect(movingObject.getY() < staticObject.getY()).to.be(true);
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: true,
|
||||
@@ -197,17 +483,283 @@ describe('Physics2RuntimeBehavior', () => {
|
||||
stopped: false,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
while (stepIndex < 10 && !hasBegunBouncing) {
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
stepIndex++;
|
||||
}
|
||||
if (!hasBounced) {
|
||||
throw new Error('Contact did not happen, nothing was tested.');
|
||||
|
||||
if (!hasBegunBouncing) {
|
||||
throw new Error(
|
||||
'Start of contact was not detected, nothing was tested.'
|
||||
);
|
||||
}
|
||||
|
||||
// At next frame, end of collision should be detected
|
||||
let hasFinishedBouncing = false;
|
||||
|
||||
runtimeScene.setEventsFunction(() => {
|
||||
hasFinishedBouncing = true;
|
||||
assertCollision(movingObject, staticObject, {
|
||||
started: false,
|
||||
collision: false,
|
||||
stopped: true,
|
||||
});
|
||||
});
|
||||
|
||||
runtimeScene.renderAndStep(1000 / fps);
|
||||
|
||||
// At next frame, end of collision should be detected
|
||||
if (!hasFinishedBouncing) {
|
||||
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,
|
||||
@@ -229,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.');
|
||||
@@ -251,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.');
|
||||
@@ -281,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.');
|
||||
@@ -299,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);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
@@ -664,7 +664,21 @@ namespace gdjs {
|
||||
}
|
||||
const baseUrl = 'https://api.gdevelop-app.com/analytics';
|
||||
this._playerId = this._makePlayerUuid();
|
||||
let lastSessionHitTime = Date.now();
|
||||
/**
|
||||
* The duration that is already sent to the service
|
||||
* (in milliseconds).
|
||||
**/
|
||||
let sentDuration = 0;
|
||||
/**
|
||||
* The duration that is not yet sent to the service to avoid flooding
|
||||
* (in milliseconds).
|
||||
**/
|
||||
let notYetSentDuration = 0;
|
||||
/**
|
||||
* The last time when duration has been counted
|
||||
* either in sendedDuration or notYetSentDuration.
|
||||
**/
|
||||
let lastSessionResumeTime = Date.now();
|
||||
fetch(baseUrl + '/session', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -715,24 +729,39 @@ namespace gdjs {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
notYetSentDuration += now - lastSessionResumeTime;
|
||||
lastSessionResumeTime = now;
|
||||
|
||||
// Group repeated calls to sendSessionHit - which could
|
||||
// happen because of multiple event listeners being fired.
|
||||
if (Date.now() - lastSessionHitTime < 3 * 1000) {
|
||||
if (notYetSentDuration < 5 * 1000) {
|
||||
return;
|
||||
}
|
||||
lastSessionHitTime = Date.now();
|
||||
// The backend use seconds for duration.
|
||||
// The milliseconds will stay in notYetSentDuration.
|
||||
const toBeSentDuration = Math.floor(notYetSentDuration / 1000) * 1000;
|
||||
sentDuration += toBeSentDuration;
|
||||
notYetSentDuration -= toBeSentDuration;
|
||||
|
||||
navigator.sendBeacon(
|
||||
baseUrl + '/session-hit',
|
||||
JSON.stringify({
|
||||
gameId: this._data.properties.projectUuid,
|
||||
playerId: this._playerId,
|
||||
sessionId: this._sessionId,
|
||||
duration: Math.floor(sentDuration / 1000),
|
||||
})
|
||||
);
|
||||
};
|
||||
if (typeof navigator !== 'undefined' && typeof document !== 'undefined') {
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
sendSessionHit();
|
||||
if (document.visibilityState === 'visible') {
|
||||
// Skip the duration the game was hidden.
|
||||
lastSessionResumeTime = Date.now();
|
||||
} else {
|
||||
sendSessionHit();
|
||||
}
|
||||
});
|
||||
window.addEventListener(
|
||||
'pagehide',
|
||||
|
@@ -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',
|
||||
|
@@ -41,7 +41,7 @@ module.exports = [
|
||||
"languageCode": "cs_CZ",
|
||||
"languageName": "Czech",
|
||||
"languageNativeName": "čeština",
|
||||
"translationRatio": 0.14570195818121479
|
||||
"translationRatio": 0.1495187520743445
|
||||
},
|
||||
{
|
||||
"languageCode": "da_DK",
|
||||
@@ -77,7 +77,7 @@ module.exports = [
|
||||
"languageCode": "es_ES",
|
||||
"languageName": "Spanish",
|
||||
"languageNativeName": "Español",
|
||||
"translationRatio": 0.985894457351477
|
||||
"translationRatio": 0.9878858280783273
|
||||
},
|
||||
{
|
||||
"languageCode": "fa_IR",
|
||||
@@ -131,7 +131,7 @@ module.exports = [
|
||||
"languageCode": "id_ID",
|
||||
"languageName": "Indonesian",
|
||||
"languageNativeName": "Bahasa Indonesia",
|
||||
"translationRatio": 0.5386657816130103
|
||||
"translationRatio": 0.5393295718552937
|
||||
},
|
||||
{
|
||||
"languageCode": "ig_NG",
|
||||
@@ -149,7 +149,7 @@ module.exports = [
|
||||
"languageCode": "ja_JP",
|
||||
"languageName": "Japanese",
|
||||
"languageNativeName": "日本語",
|
||||
"translationRatio": 0.9774311317623631
|
||||
"translationRatio": 0.9797543976103551
|
||||
},
|
||||
{
|
||||
"languageCode": "ka_GE",
|
||||
@@ -227,7 +227,7 @@ module.exports = [
|
||||
"languageCode": "pt_BR",
|
||||
"languageName": "Brazilian Portuguese",
|
||||
"languageNativeName": "Português brasileiro",
|
||||
"translationRatio": 0.9535346830401593
|
||||
"translationRatio": 0.9624958513109857
|
||||
},
|
||||
{
|
||||
"languageCode": "pt_PT",
|
||||
@@ -263,7 +263,7 @@ module.exports = [
|
||||
"languageCode": "sl_SI",
|
||||
"languageName": "Slovene",
|
||||
"languageNativeName": "slovenski jezik",
|
||||
"translationRatio": 0.9910388317291736
|
||||
"translationRatio": 0.9933620975771656
|
||||
},
|
||||
{
|
||||
"languageCode": "sq_AL",
|
||||
@@ -305,7 +305,7 @@ module.exports = [
|
||||
"languageCode": "uk_UA",
|
||||
"languageName": "Ukrainian",
|
||||
"languageNativeName": "Українська",
|
||||
"translationRatio": 0.8647527381347494
|
||||
"translationRatio": 0.868901427149021
|
||||
},
|
||||
{
|
||||
"languageCode": "ur_PK",
|
||||
@@ -323,7 +323,7 @@ module.exports = [
|
||||
"languageCode": "vi_VN",
|
||||
"languageName": "Vietnamese",
|
||||
"languageNativeName": "Tiếng Việt",
|
||||
"translationRatio": 0.05426485230667111
|
||||
"translationRatio": 0.05741785595751747
|
||||
},
|
||||
{
|
||||
"languageCode": "yo_NG",
|
||||
@@ -335,7 +335,7 @@ module.exports = [
|
||||
"languageCode": "zh_CN",
|
||||
"languageName": "Chinese Simplified",
|
||||
"languageNativeName": "简化字",
|
||||
"translationRatio": 0.9918685695320278
|
||||
"translationRatio": 0.9925323597743113
|
||||
},
|
||||
{
|
||||
"languageCode": "zh_TW",
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
||||
"name": "gdevelop",
|
||||
"productName": "GDevelop 5",
|
||||
"description": "GDevelop 5 IDE - the open-source, cross-platform game engine designed for everyone",
|
||||
"version": "5.0.137",
|
||||
"version": "5.0.138",
|
||||
"author": "GDevelop Team <hello@gdevelop.io>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://gdevelop.io",
|
||||
|
Reference in New Issue
Block a user