Add more tests on the platformer extension (#2415)

- jumping to grab platforms or ladders
- walking on platforms
- performance
This commit is contained in:
D8H
2021-03-08 22:59:22 +01:00
committed by GitHub
parent b78022a891
commit 3bdaff53e1

View File

@@ -44,7 +44,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
};
const addLadderObject = (runtimeScene) => {
const platform = new gdjs.RuntimeObject(runtimeScene, {
const ladder = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj3',
type: '',
behaviors: [
@@ -55,15 +55,15 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
},
],
});
platform.getWidth = function () {
ladder.getWidth = function () {
return 20;
};
platform.getHeight = function () {
ladder.getHeight = function () {
return 60;
};
runtimeScene.addObject(platform);
runtimeScene.addObject(ladder);
return platform;
return ladder;
};
describe('(falling)', function () {
@@ -305,6 +305,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
object.getBehavior('auto1').simulateJumpKey();
for (let i = 0; i < 18; ++i) {
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(true);
}
// Check that we reached the maximum height
@@ -315,9 +316,19 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be.within(-180, -179);
// Then let the object fall
for (let i = 0; i < 60; ++i) {
for (let i = 0; i < 17; ++i) {
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(true);
}
// The jump finishes one frame before going back to the floor
// because the gravity is not applied on the first step.
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(false);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
expect(object.getY()).to.be(-31);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isOnFloor()).to.be(true);
expect(object.getY()).to.be(-30);
});
@@ -409,7 +420,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be(-30);
});
it('should not grab a platform while jumping', function () {
it('should not grab a platform while in the ascending phase of a jump', function () {
const topPlatform = addPlatformObject(runtimeScene);
topPlatform.setPosition(12, -80);
runtimeScene.renderAndStep(1000 / 60);
@@ -448,6 +459,43 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getBehavior('auto1').isGrabbingPlatform()).to.be(false);
});
it('can grab a platform while in the descending phase of a jump', function () {
const topPlatform = addPlatformObject(runtimeScene);
topPlatform.setPosition(12, -120);
runtimeScene.renderAndStep(1000 / 60);
// Ensure the object falls on the platform
for (let i = 0; i < 10; ++i) {
runtimeScene.renderAndStep(1000 / 60);
}
// Check the object is on the platform
expect(object.getY()).to.be(-30); // -30 = -10 (platform y) + -20 (object height)
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
// Jump, reach the top and go down
object.getBehavior('auto1').simulateJumpKey();
for (let i = 0; i < 30; ++i) {
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(true);
}
// the object is against the platform side
expect(object.getY()).to.be.within(
topPlatform.getY() - object.getHeight(),
topPlatform.getY()
);
// try to grab the platform
for (let i = 0; i < 30; ++i) {
object.getBehavior('auto1').simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
}
// Check that the object grabbed the platform
expect(object.getY()).to.be(topPlatform.getY());
expect(object.getBehavior('auto1').isGrabbingPlatform()).to.be(true);
});
it('should not grab a platform while walking', function () {
const topPlatform = addPlatformObject(runtimeScene);
topPlatform.setPosition(20, platform.getY() - object.getHeight());
@@ -691,6 +739,39 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getY()).to.be(platform.getY() - object.getHeight());
expect(object.getBehavior('auto1').isGrabbingPlatform()).to.be(false);
});
// This may be a bug. Please, remove the skip if you fixed it.
// It fails on the last 2 expect()
it.skip('follows a moving platform when was grabbed to a ladder', function () {
// object is 10 pixel higher than the platform and overlap the ladder
object.setPosition(0, platform.getY() - object.getHeight() - 10);
const ladder = addLadderObject(runtimeScene);
ladder.setPosition(object.getX(), platform.getY() - ladder.getHeight());
// Fall and Grab the platform
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
object.getBehavior('auto1').simulateLadderKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(true);
// move the bottom platform to the object
for (let i = 0; i < 20; ++i) {
platform.setY(platform.getY() - 1);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(true);
}
// the platform reach the object
expect(platform.getY()).to.be(object.getY() + object.getHeight());
for (let i = 0; i < 5; ++i) {
platform.setY(platform.getY() - 1);
runtimeScene.renderAndStep(1000 / 60);
}
// the object follows it and no longer grab the other platform
expect(object.getY()).to.be(platform.getY() - object.getHeight());
expect(object.getBehavior('auto1').isOnLadder()).to.be(false);
expect(object.getBehavior('auto1').isOnFloor()).to.be(true);
});
});
describe('and gdjs.PlatformRuntimeBehavior at same time', function () {
@@ -911,6 +992,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
const lastY = object.getY();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getY()).to.be.above(lastY);
}
};
@@ -921,6 +1003,8 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
object.getBehavior('auto1').simulateUpKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(true);
//TODO Probably a bug, uncomment it after it's fixed
//expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getY()).to.be.below(lastY);
}
};
@@ -930,10 +1014,30 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
const lastY = object.getY();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(true);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
expect(object.getY()).to.be(lastY);
}
};
const jumpAndAscend = (frameCount) => {
for (let i = 0; i < frameCount; ++i) {
const lastY = object.getY();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(true);
expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getY()).to.be.below(lastY);
}
};
const jumpAndDescend = (frameCount) => {
for (let i = 0; i < frameCount; ++i) {
const lastY = object.getY();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isJumping()).to.be(true);
expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getY()).to.be.above(lastY);
}
};
const fallOnPlatform = (maxFrameCount) => {
// Ensure the object falls on the platform
for (let i = 0; i < maxFrameCount; ++i) {
@@ -973,7 +1077,7 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
}
});
it('can jump and grab a ladder', function () {
it('can jump and grab a ladder even on the ascending phase of a jump the 1st time', function () {
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
@@ -996,6 +1100,78 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
climbLadder(2);
});
it('can grab a ladder while on the descending phase of a jump', function () {
// Need a bigger ladder
ladder.getHeight = function () {
return 300;
};
ladder.setPosition(30, -10 - ladder.getHeight());
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
// Jump
object.getBehavior('auto1').simulateJumpKey();
for (let i = 0; i < 19; ++i) {
jumpAndAscend(1);
}
// starting to going down
object.getBehavior('auto1').simulateLadderKey();
stayOnLadder(1);
expect(object.getBehavior('auto1').isJumping()).to.be(false);
stayOnLadder(10);
climbLadder(2);
});
it('can jump from ladder to ladder', function () {
// Need a bigger ladder
ladder.getHeight = function () {
return 300;
};
ladder.setPosition(30, -10 - ladder.getHeight());
const ladder2 = addLadderObject(runtimeScene);
ladder2.getHeight = function () {
return 300;
};
ladder2.setPosition(ladder.getX() + ladder.getWidth(), ladder.getY());
object.setPosition(35, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
// Jump
object.getBehavior('auto1').simulateJumpKey();
for (let i = 0; i < 10; ++i) {
jumpAndAscend(1);
}
// 1st time grabbing this ladder
object.getBehavior('auto1').simulateLadderKey();
stayOnLadder(1);
expect(object.getBehavior('auto1').isJumping()).to.be(false);
// Jump right
object.getBehavior('auto1').simulateJumpKey();
for (let i = 0; i < 15; ++i) {
object.getBehavior('auto1').simulateRightKey();
jumpAndAscend(1);
}
// leave the 1st ladder
expect(object.getX()).to.be.above(ladder2.getX());
// and grab the 2nd one, even if still ascending
object.getBehavior('auto1').simulateLadderKey();
// still moves a little because of inertia
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(true);
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnLadder()).to.be(true);
stayOnLadder(1);
});
it('can fall from a ladder right side', function () {
object.setPosition(30, -32);
// Ensure the object falls on the platform
@@ -1135,4 +1311,252 @@ describe('gdjs.PlatformerObjectRuntimeBehavior', function () {
expect(object.getBehavior('auto1').isGrabbingPlatform()).to.be(true);
});
});
describe('(walk)', function () {
let runtimeScene;
let object;
let platform;
beforeEach(function () {
runtimeScene = makeTestRuntimeScene();
// Put a platformer object on a platform
object = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
type: 'PlatformBehavior::PlatformerObjectBehavior',
name: 'auto1',
gravity: 1500,
maxFallingSpeed: 1500,
acceleration: 500,
deceleration: 1500,
maxSpeed: 500,
jumpSpeed: 900,
canGrabPlatforms: true,
ignoreDefaultControls: true,
slopeMaxAngle: 60,
jumpSustainTime: 0.2,
roundCoordinates: true,
},
],
});
object.getWidth = function () {
return 10;
};
object.getHeight = function () {
return 20;
};
runtimeScene.addObject(object);
// Put a platform.
platform = addPlatformObject(runtimeScene);
platform.setPosition(0, -10);
});
const fall = (frameCount) => {
for (let i = 0; i < frameCount; ++i) {
const lastY = object.getY();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isFalling()).to.be(true);
expect(object.getBehavior('auto1').isMoving()).to.be(true);
expect(object.getY()).to.be.above(lastY);
}
};
const walkRight = (frameCount) => {
for (let i = 0; i < frameCount; ++i) {
const lastX = object.getX();
object.getBehavior('auto1').simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnFloor()).to.be(true);
expect(object.getX()).to.be.above(lastX);
if (Math.abs(object.getX() - lastX) > 1) {
expect(object.getBehavior('auto1').isMoving()).to.be(true);
}
}
};
const fallOnPlatform = (maxFrameCount) => {
// Ensure the object falls on the platform
for (let i = 0; i < maxFrameCount; ++i) {
runtimeScene.renderAndStep(1000 / 60);
}
//Check the object is on the platform
expect(object.getY()).to.be(-30); // -30 = -10 (platform y) + -20 (object height)
expect(object.getBehavior('auto1').isFalling()).to.be(false);
expect(object.getBehavior('auto1').isMoving()).to.be(false);
};
it('can walk from a platform to another one', function () {
const platform2 = addPlatformObject(runtimeScene);
platform2.setPosition(
platform.getX() + platform.getWidth(),
platform.getY()
);
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
// walk from the 1st platform to the 2nd one
walkRight(30);
expect(object.getX()).to.be.above(platform2.getX());
expect(object.getY()).to.be(platform2.getY() - object.getHeight());
});
it('can walk from a platform to another one that not aligned', function () {
// the 2nd platform is 1 pixel higher
const platform2 = addPlatformObject(runtimeScene);
platform2.setPosition(
platform.getX() + platform.getWidth(),
platform.getY() - 1
);
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
// walk from the 1st platform to the 2nd one
walkRight(30);
expect(object.getX()).to.be.above(platform2.getX());
expect(object.getY()).to.be(platform2.getY() - object.getHeight());
});
it("can't walk from a platform to another one that is too high", function () {
// the 2nd platform is 2 pixels higher
const platform2 = addPlatformObject(runtimeScene);
platform2.setPosition(
platform.getX() + platform.getWidth(),
platform.getY() - 2
);
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
// walk right
for (let i = 0; i < 20; ++i) {
object.getBehavior('auto1').simulateRightKey();
runtimeScene.renderAndStep(1000 / 60);
expect(object.getBehavior('auto1').isOnFloor()).to.be(true);
}
// is blocked by the 2nd platform
expect(object.getX()).to.be(platform2.getX() - object.getWidth());
expect(object.getY()).to.be(platform.getY() - object.getHeight());
});
it('can walk from a platform to another one that is rotated', function () {
const platform2 = addPlatformObject(runtimeScene);
const angle = (-30 * Math.PI) / 180;
const centerDeltaX = platform2.getWidth() / 2;
const centerDeltaY = platform2.getHeight() / 2;
// to make the vertex of the 2 platform touch
const vertexDeltaX =
centerDeltaX * Math.cos(angle) +
centerDeltaY * -Math.sin(angle) -
centerDeltaX;
const vertexDeltaY =
centerDeltaX * Math.sin(angle) +
centerDeltaY * Math.cos(angle) -
centerDeltaY;
platform2.setAngle(-30);
platform2.setPosition(
platform.getX() + platform.getWidth() + vertexDeltaX,
platform.getY() + vertexDeltaY
);
object.setPosition(30, -32);
// Ensure the object falls on the platform
fallOnPlatform(10);
// walk from the 1st platform to the 2nd one
walkRight(30);
expect(object.getX()).to.be.above(platform2.getX());
// gone upward following the 2nd platform
expect(object.getY()).to.be.below(platform.getY());
});
});
describe.skip('(benchmark)', function () {
let runtimeScene;
let objects;
const duplicateCount = 60;
const stepCount = 6000;
beforeEach(function () {
runtimeScene = makeTestRuntimeScene();
objects = new Array(duplicateCount);
for (let i = 0; i < duplicateCount; ++i) {
// Put a platformer object on a platform
object = new gdjs.RuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [
{
type: 'PlatformBehavior::PlatformerObjectBehavior',
name: 'auto1',
gravity: 1500,
maxFallingSpeed: 1500,
acceleration: 500,
deceleration: 1500,
maxSpeed: 500,
jumpSpeed: 900,
canGrabPlatforms: true,
ignoreDefaultControls: true,
slopeMaxAngle: 60,
jumpSustainTime: 0.2,
roundCoordinates: true,
},
],
});
object.getWidth = function () {
return 10;
};
object.getHeight = function () {
return 20;
};
runtimeScene.addObject(object);
object.setPosition(100 * i + 60 * 5, 400 * i - 32);
objects[i] = object;
// Put a platform.
for (let p = 0; p < 10; ++p) {
const platform = addPlatformObject(runtimeScene);
platform.setPosition(100 * i + p * platform.getWidth(), 400 * i - 10);
}
}
});
it('benchmark', function () {
this.timeout(30000);
for (let b = 0; b < 10; ++b) {
const benchmarkSuite = makeBenchmarkSuite({
benchmarksCount: 1,
iterationsCount: stepCount,
});
benchmarkSuite.add('jump in loop', (t) => {
for (let i = 0; i < duplicateCount; ++i) {
const object = objects[i];
if (t % 60 == i % 60) {
object.getBehavior('auto1').simulateJumpKey();
}
if (t + (i % 61) < 31) {
object.getBehavior('auto1').simulateRightKey();
}
if (t + (i % 61) >= 31) {
object.getBehavior('auto1').simulateLeftKey();
}
}
runtimeScene.renderAndStep(1000 / 60);
});
console.log(benchmarkSuite.run());
}
});
});
});