Compare commits

...

1 Commits

Author SHA1 Message Date
Florian Rival
efd1f6f48b [WIP] Fix ghost object when an object with the Physics engine is deleted
* This was happening when an object with the Physics behavior is deleted and then manipulated by other actions before the end of the current frame.
2022-03-24 16:15:04 +01:00
8 changed files with 208 additions and 7 deletions

View File

@@ -49,7 +49,6 @@ describe('gdjs.DraggableRuntimeBehavior', function () {
{
name: 'Behavior1',
type: 'DraggableBehavior::Draggable',
// @ts-ignore - properties are not typed
checkCollisionMask: true,
},
],

View File

@@ -57,7 +57,6 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
{
type: 'PathfindingBehavior::PathfindingBehavior',
name: 'auto1',
// @ts-ignore - properties are not typed
allowDiagonals: allowDiagonals,
acceleration: 400,
maxSpeed: 200,
@@ -70,6 +69,7 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
collisionMethod: collisionMethod,
},
],
variables: [],
effects: [],
});
player.getWidth = function () {
@@ -89,11 +89,12 @@ describe('gdjs.PathfindingRuntimeBehavior', function () {
behaviors: [
{
type: 'PathfindingBehavior::PathfindingObstacleBehavior',
// @ts-ignore - properties are not typed
name: 'PathfindingObstacleBehavior',
impassable: true,
cost: 2,
},
],
variables: [],
effects: [],
});
obstacle.getWidth = function () {

View File

@@ -1,2 +1,34 @@
// TODO: Improve type checking by using proper typings for Box2D.
declare var Box2D: any;
declare interface Box2DBody {
ApplyAngularImpulse(arg0: any): any;
ApplyForce(arg0: any, arg1: any): any;
ApplyLinearImpulse(arg0: any, arg1: any): any;
ApplyTorque(arg0: any): any;
CreateFixture(arg0: any): any;
DestroyFixture(arg0: any): any;
GetAngle(): any;
GetAngularVelocity(): any;
GetContactList(): any;
GetLinearVelocity(): any;
GetLocalPoint(arg0: any): any;
GetPosition(): any;
GetWorldCenter(): any;
IsAwake(): any;
ResetMassData(): any;
SetAngularDamping(arg0: number): any;
SetAngularVelocity(arg0: number): any;
SetAwake(arg0: boolean): any;
SetBullet(arg0: boolean): any;
SetFixedRotation(arg0: boolean): any;
SetGravityScale(arg0: number): any;
SetLinearDamping(arg0: number): any;
SetLinearVelocity(arg0: number): any;
SetSleepingAllowed(arg0: boolean): any;
SetTransform(arg0: any, arg1: any): any;
SetType(arg0: any): any;
GetFixtureList(): any;
gdjsAssociatedBehavior: gdjs.Physics2RuntimeBehavior;
}

View File

@@ -20,7 +20,7 @@ namespace gdjs {
// Start with 1 so the user is safe from default variables value (0)
joints: any = {};
constructor(runtimeScene, sharedData) {
constructor(runtimeScene: gdjs.RuntimeScene, sharedData) {
this.gravityX = sharedData.gravityX;
this.gravityY = sharedData.gravityY;
this.scaleX = sharedData.scaleX === 0 ? 100 : sharedData.scaleX;
@@ -242,7 +242,7 @@ namespace gdjs {
masks: any;
shapeScale: number = 1;
currentContacts: any;
_body: any = null;
_body: Box2DBody | null = null;
_sharedData: any;
_tempb2Vec2: any;
@@ -662,6 +662,7 @@ namespace gdjs {
}
getBody() {
// TODO: if the object was delete, don't recreate the body
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
@@ -670,6 +671,8 @@ namespace gdjs {
}
createBody() {
// TODO: if the object was delete, don't recreate the body
// Generate the body definition
const bodyDef = new Box2D.b2BodyDef();
@@ -927,7 +930,6 @@ namespace gdjs {
// If there is no body, set a new one
if (this._body === null) {
this.createBody();
return;
}
// Update body bullet flag

View File

@@ -0,0 +1,162 @@
// @ts-check
describe('gdjs.Physics2RuntimeBehavior', function () {
const setupSceneAndObject = () => {
const runtimeGame = new gdjs.RuntimeGame({
variables: [],
resources: { resources: [] },
// @ts-ignore
properties: { windowWidth: 800, windowHeight: 600 },
});
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
layers: [
{
name: '',
visibility: true,
cameras: [],
effects: [],
ambientLightColorR: 127,
ambientLightColorB: 127,
ambientLightColorG: 127,
isLightingLayer: false,
followBaseLayerCamera: false,
},
],
variables: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
behaviorsSharedData: [
{
name: 'Physics2',
type: 'Physics2::Physics2Behavior',
gravityX: 0,
gravityY: 9.8,
scaleX: 100,
scaleY: 100,
},
],
objects: [],
instances: [],
});
runtimeScene._timeManager.getElapsedTime = function () {
return (1 / 60) * 1000;
};
const object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
name: 'Physics2',
type: 'Physics2::Physics2Behavior',
bodyType: 'Dynamic',
bullet: false,
fixedRotation: false,
canSleep: true,
shape: 'Box',
shapeDimensionA: 0,
shapeDimensionB: 0,
shapeOffsetX: 0,
shapeOffsetY: 0,
polygonOrigin: 'Center',
vertices: [],
density: 1,
friction: 0.3,
restitution: 0.1,
linearDamping: 0.1,
angularDamping: 0.1,
gravityScale: 1,
layers: 1,
masks: 1,
},
],
variables: [],
effects: [],
});
runtimeScene.addObject(object);
const behavior = object.getBehavior('Physics2');
if (!behavior || !(behavior instanceof gdjs.Physics2RuntimeBehavior)) {
throw new Error(
'The Physics2 behavior could not be created/found on the object.'
);
}
return { runtimeScene, object, behavior };
};
it('creates the body when needed', function () {
const { runtimeScene, object, behavior } = setupSceneAndObject();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
expect(object.getX()).to.be(0);
expect(object.getY()).to.be(0.2717692870646715);
runtimeScene.renderAndStep(1000 / 60);
runtimeScene.renderAndStep(1000 / 60);
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
expect(object.getX()).to.be(0);
expect(object.getY()).to.be(2.713174745440483);
runtimeScene.unloadScene();
});
it('allows some properties to be changed', function () {
const { runtimeScene, object, behavior } = setupSceneAndObject();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
// Check default values
expect(behavior.isStatic()).to.be(false);
expect(behavior.isDynamic()).to.be(true);
expect(behavior.getFriction()).to.be(0.3);
expect(behavior.isSleepingAllowed()).to.be(true);
// Check a few values can be changed
behavior.setStatic();
expect(behavior.isStatic()).to.be(true);
behavior.setFriction(2);
expect(behavior.getFriction()).to.be(2);
behavior.setSleepingAllowed(false);
expect(behavior.isSleepingAllowed()).to.be(false);
runtimeScene.unloadScene();
});
it('destroy the body when needed', function () {
const { runtimeScene, object, behavior } = setupSceneAndObject();
runtimeScene.renderAndStep(1000 / 60);
expect(behavior.getBody()).not.to.be(null);
expect(object.getX()).to.be(0);
expect(object.getY()).to.be(0.2717692870646715);
object.deleteFromScene(runtimeScene);
behavior.setStatic();
behavior.setDynamic();
behavior.setFixedRotation(true);
behavior.setFixedRotation(false);
behavior.setBullet(true);
behavior.setBullet(false);
behavior.setAngularDamping(1);
behavior.setAngularVelocity(1);
behavior.setFriction(1);
behavior.setSleepingAllowed(false);
behavior.setSleepingAllowed(true);
expect(behavior._body).to.be(null);
runtimeScene.unloadScene();
});
});

View File

@@ -49,11 +49,11 @@ describe('gdjs.TopDownMovementRuntimeBehavior', function () {
name: 'player',
type: '',
effects: [],
variables: [],
behaviors: [
{
type: 'TopDownMovementBehavior::TopDownMovementBehavior',
name: 'auto1',
// @ts-ignore - properties are not typed
allowDiagonals: allowDiagonals,
acceleration: 400,
deceleration: 800,

View File

@@ -60,6 +60,8 @@ declare type BehaviorData = {
name: string;
/** The behavior type. Used by GDJS to find the proper behavior to construct. */
type: string;
[key: string]: any;
};
declare interface GdVersionData {
@@ -87,6 +89,7 @@ declare interface LayoutData {
declare interface BehaviorSharedData {
name: string;
type: string;
[key: string]: any;
}
declare interface ExternalLayoutData {

View File

@@ -80,6 +80,8 @@ module.exports = function (config) {
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Lighting/lightobstacleruntimebehavior.js',
'../../newIDE/app/resources/GDJS/Runtime/Extensions/PathfindingBehavior/pathfindingobstacleruntimebehavior.js',
'../../newIDE/app/resources/GDJS/Runtime/Extensions/PathfindingBehavior/pathfindingruntimebehavior.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/PrimitiveDrawing/shapepainterruntimeobject.js',
'../../newIDE/app/resources/GDJS/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject-pixi-renderer.js',
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TextInput/textinputruntimeobject.js',