Files
GDevelop/Extensions/TileMap/tests/tilemapcollisionmaskruntimeobject.spec.js
D8H 7dd4d13a7a Upgrade to Three.js 0.185.1 (#8853)
- Point lights and spot lights now uses a realistic decay. It's not possible to find the exact same result as before, but you can tweak the intensity and the decay properties. Hemisphere lights, directional lights and ambient lights still work the same as before.
- The 3D particle emitter must be updated to the last version to handle the new 3D renderer world scale property.
- Ensure any 3D extension you use is also updated to the latest version.
2026-10-03 13:55:50 +02:00

363 lines
11 KiB
JavaScript

// @ts-check
describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
const createScene = (framePerSecond = 60) => {
const runtimeGame = gdjs.getPixiRuntimeGame({
resources: {
resources: [
{
file: 'base/GDJS/tests/tests-utils/simple-tiled-map/SmallTiledMap.json',
kind: 'json',
metadata: '',
name: 'SmallTiledMap.json',
userAdded: true,
},
{
file: 'base/GDJS/tests/tests-utils/simple-tiled-map/FlippingTiledMap.json',
kind: 'json',
metadata: '',
name: 'FlippingTiledMap.json',
userAdded: true,
},
{
file: 'base/GDJS/tests/tests-utils/simple-tiled-map/MiniTiledSet.json',
kind: 'json',
metadata: '',
name: 'MiniTiledSet.json',
userAdded: true,
},
],
},
});
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
sceneData: {
layers: [
{
name: '',
visibility: true,
effects: [],
cameras: [],
ambientLightColorR: 0,
ambientLightColorG: 0,
ambientLightColorB: 0,
isLightingLayer: false,
followBaseLayerCamera: true,
},
],
variables: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
renderer3DWorldScale: 100,
behaviorsSharedData: [],
objects: [],
objectsGroups: [],
instances: [],
usedResources: [],
uiSettings: {
grid: false,
gridType: 'rectangular',
gridWidth: 10,
gridHeight: 10,
gridDepth: 10,
gridOffsetX: 0,
gridOffsetY: 0,
gridOffsetZ: 0,
gridColor: 0,
gridAlpha: 1,
snap: false,
},
},
usedExtensionsWithVariablesData: [],
});
setFramesPerSecond(runtimeScene, framePerSecond);
return runtimeScene;
};
const setFramesPerSecond = (runtimeScene, framePerSecond) => {
runtimeScene._timeManager.getElapsedTime = function () {
return 1000 / framePerSecond;
};
};
const addTileMapCollisionMask = (runtimeScene, tileMapJsonFile) => {
const tileMap = new gdjs.TileMapCollisionMaskRuntimeObject(runtimeScene, {
name: 'tilemap',
type: 'TileMap::CollisionMask',
behaviors: [],
effects: [],
content: {
tilemapJsonFile: tileMapJsonFile,
tilesetJsonFile: 'MiniTiledSet.json',
layerIndex: 0,
collisionMaskTag: 'obstacle',
debugMode: false,
fillColor: '#ffffff',
outlineColor: '#ffffff',
fillOpacity: 1,
outlineOpacity: 1,
outlineSize: 1,
},
});
runtimeScene.addObject(tileMap);
return tileMap;
};
const addObject = (runtimeScene) => {
const object = new gdjs.TestRuntimeObject(runtimeScene, {
name: 'obj1',
type: '',
behaviors: [],
effects: [],
variables: [],
});
object.setCustomWidthAndHeight(8, 8);
runtimeScene.addObject(object);
return object;
};
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
describe('Basic cases', function () {
let runtimeScene;
/**
* @type {gdjs.TileMapCollisionMaskRuntimeObject}
*/
let tileMap;
beforeEach(async function () {
runtimeScene = createScene();
tileMap = addTileMapCollisionMask(runtimeScene, 'SmallTiledMap.json');
// TODO find a clean way to wait for the json to be read.
for (
let index = 0;
index < 30 && tileMap._collisionTileMap.getDimensionX() === 0;
index++
) {
await delay(100);
}
if (tileMap._collisionTileMap.getDimensionX() === 0) {
throw new Error('Timeout reading the tile map JSON file.');
}
});
it('can be measured', function () {
tileMap.setPosition(100, 200);
expect(tileMap.getWidth()).to.be(32);
expect(tileMap.getHeight()).to.be(16);
expect(tileMap.getCenterX()).to.be(16);
expect(tileMap.getCenterY()).to.be(8);
});
/**
* insideObject usually use the AABB of the object.
* But, in case of a tile map, it makes more sense to look each tile individually.
* It returns true when there is an hitbox in the tile.
*/
it('can detect a point inside the collision mask', function () {
tileMap.setPosition(100, 200);
// The point is in the black square with an hitbox.
expect(tileMap.insideObject(104, 204)).to.be(true);
expect(tileMap.isCollidingWithPoint(104, 204)).to.be(true);
// The point is in wite square without any hitbox.
expect(tileMap.insideObject(112, 212)).to.be(false);
expect(tileMap.isCollidingWithPoint(112, 212)).to.be(false);
// The point is in the top black triangle that has an hitbox.
expect(tileMap.insideObject(120, 202)).to.be(true);
expect(tileMap.isCollidingWithPoint(120, 202)).to.be(true);
// The point is in the left white triangle that has an hitbox.
expect(tileMap.insideObject(118, 204)).to.be(true);
expect(tileMap.isCollidingWithPoint(118, 204)).to.be(false);
});
it('can detect collisions with an object', function () {
tileMap.setPosition(100, 200);
const object = addObject(runtimeScene);
object.setPosition(96, 196);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
true
);
object.setPosition(90, 190);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
object.setPosition(115, 207);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
true
);
object.setPosition(116, 208);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it('can check collisions with an object on empty tiles without any issue', function () {
tileMap.setPosition(100, 200);
const object = addObject(runtimeScene);
object.setPosition(116, 208);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it('can detect collisions with an object on vertically flipped tiles', function () {
tileMap.setPosition(100, 200);
const object = addObject(runtimeScene);
// The object is over the black triangle.
object.setPosition(118, 214);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
true
);
// The object is over the red triangle without touching a black polygon.
object.setPosition(130, 204);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it("can detect collisions with an object when it's rotated", function () {
tileMap.setPosition(100, 200);
tileMap.setAngle(90);
const object = addObject(runtimeScene);
object.setPosition(123, 185);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
true
);
object.setPosition(124, 184);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
it('can detect collisions with an object when it has a custom size', function () {
tileMap.setPosition(100, 200);
tileMap.setWidth(2 * tileMap.getWidth());
tileMap.setHeight(2 * tileMap.getHeight());
const object = addObject(runtimeScene);
object.setPosition(163, 231);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
true
);
object.setPosition(164, 232);
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
false
);
});
});
describe('Flipping cases', function () {
let runtimeScene;
/**
* @type {gdjs.TileMapCollisionMaskRuntimeObject}
*/
let tileMap;
beforeEach(async function () {
runtimeScene = createScene();
tileMap = addTileMapCollisionMask(runtimeScene, 'FlippingTiledMap.json');
// TODO find a clean way to wait for the json to be read.
for (
let index = 0;
index < 100 && tileMap._collisionTileMap.getDimensionX() === 0;
index++
) {
await delay(50);
}
if (tileMap._collisionTileMap.getDimensionX() === 0) {
throw new Error('Timeout reading the tile map JSON file.');
}
});
// 4 rotations
it('can detect a point inside (vertical: false, horizontal: false, diagonal: false)', function () {
tileMap.setPosition(100, 200);
const x = 100;
const y = 200;
expect(tileMap.isCollidingWithPoint(x + 3, y + 1)).to.be(true);
});
it('can detect a point inside (vertical: false, horizontal: true, diagonal: true)', function () {
tileMap.setPosition(100, 200);
const x = 108;
const y = 200;
expect(tileMap.isCollidingWithPoint(x + 7, y + 3)).to.be(true);
});
it('can detect a point inside (vertical: true, horizontal: true, diagonal: false)', function () {
tileMap.setPosition(100, 200);
const x = 108;
const y = 208;
expect(tileMap.isCollidingWithPoint(x + 5, y + 7)).to.be(true);
});
it('can detect a point inside (vertical: true, horizontal: false, diagonal: true)', function () {
tileMap.setPosition(100, 200);
const x = 100;
const y = 208;
expect(tileMap.isCollidingWithPoint(x + 1, y + 5)).to.be(true);
});
// 4 rotations but diagonal is negated
it('can detect a point inside (vertical: false, horizontal: false, diagonal: true)', function () {
tileMap.setPosition(100, 200);
const x = 116;
const y = 200;
expect(tileMap.isCollidingWithPoint(x + 1, y + 3)).to.be(true);
});
it('can detect a point inside (vertical: false, horizontal: true, diagonal: false)', function () {
tileMap.setPosition(100, 200);
const x = 124;
const y = 200;
expect(tileMap.isCollidingWithPoint(x + 5, y + 1)).to.be(true);
});
it('can detect a point inside (vertical: true, horizontal: true, diagonal: true)', function () {
tileMap.setPosition(100, 200);
const x = 124;
const y = 208;
expect(tileMap.isCollidingWithPoint(x + 7, y + 5)).to.be(true);
});
it('can detect a point inside (vertical: true, horizontal: false, diagonal: false)', function () {
tileMap.setPosition(100, 200);
const x = 116;
const y = 208;
expect(tileMap.isCollidingWithPoint(x + 3, y + 7)).to.be(true);
});
});
});