Files
GDevelop/Extensions/PrimitiveDrawing/tests/shapepainterruntimeobject.pixiruntimegamewithassets.spec.js
D8H 8e50ddefe4 Allow to configure object resources to be loaded independently from the scene (#8514)
- This is an advanced, experimental feature which is subject to changes. It's in most cases not useful, unless you have a large game or low end devices for which controlling memory usage is important. It's mostly useful to delay loading of large custom objects.
- Objects can be configured so that its resources are not loaded when the scene is loaded. Instead, you can use an action to manually load the resources used by it. When the loading is done (this can be checked using a condition), the object can then be created.
  Be careful: if an object assets are not loaded, it will be broken when displayed on screen.
2026-05-04 10:45:51 +02:00

239 lines
8.1 KiB
JavaScript

// @ts-check
describe('gdjs.ShapePainterRuntimeObject (using a PixiJS RuntimeGame with assets)', function () {
/**
* @param {gdjs.RuntimeScene} runtimeScene
*/
const makeShapePainterRuntimeObject = (runtimeScene) =>
new gdjs.ShapePainterRuntimeObject(runtimeScene, {
name: 'obj1',
type: 'PrimitiveDrawing::Drawer',
variables: [],
behaviors: [],
effects: [],
fillColor: { r: 0, g: 0, b: 0 },
outlineColor: { r: 0, g: 0, b: 0 },
fillOpacity: 255,
outlineOpacity: 255,
outlineSize: 1,
absoluteCoordinates: false,
clearBetweenFrames: false,
antialiasing: 'low',
});
/** @param {gdjs.RuntimeScene} runtimeScene */
const loadScene = (runtimeScene) => {
runtimeScene.loadFromScene({
sceneData: {
layers: [
{
name: '',
visibility: true,
effects: [],
cameras: [],
ambientLightColorR: 0,
ambientLightColorG: 0,
ambientLightColorB: 0,
isLightingLayer: false,
followBaseLayerCamera: true,
},
],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
behaviorsSharedData: [],
objects: [],
objectsGroups: [],
instances: [],
variables: [],
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: [],
});
};
it('properly computes bounds of the object (basics)', async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
const object = makeShapePainterRuntimeObject(runtimeScene);
expect(object.getWidth()).to.be(0);
expect(object.getHeight()).to.be(0);
object.drawLineV2(10, 10, 20, 30, 3);
// Check the automatically computed bounds:
const drawableX = 8.658359213500127;
const drawableY = 9.329179606750063;
const width = 12.683281572999746;
const height = 21.341640786499873;
expect(object.getDrawableX()).to.be(drawableX);
expect(object.getDrawableY()).to.be(drawableY);
expect(object.getWidth()).to.be(width);
expect(object.getHeight()).to.be(height);
// Check the automatic center positioning:
expect(object.getCenterXInScene()).to.be(15);
expect(object.getCenterYInScene()).to.be(20);
expect(object.getCenterX()).to.be(15 - drawableX);
expect(object.getCenterY()).to.be(20 - drawableY);
// Check hit boxes:
expect(object.getAABB()).to.eql({
min: [drawableX, drawableY],
max: [drawableX + width, drawableY + height],
});
// Check after scaling (scaling is done from the origin):
object.setScale(2);
expect(object.getDrawableX()).to.be(2 * drawableX);
expect(object.getDrawableY()).to.be(2 * drawableY);
expect(object.getWidth()).to.be(2 * width);
expect(object.getHeight()).to.be(2 * height);
expect(object.getAABB()).to.eql({
min: [2 * drawableX, 2 * drawableY],
max: [2 * (drawableX + width), 2 * (drawableY + height)],
});
// Check after rotating (rotating is done from the center):
object.setAngle(45);
expect(object.getDrawableX()).to.be(2 * drawableX); // Drawable X/Y is not impacted...
expect(object.getDrawableY()).to.be(2 * drawableY);
expect(object.getWidth()).to.be(2 * width); // ...Neither is the size
expect(object.getHeight()).to.be(2 * height);
expect(object.getAABB()).to.eql({
// The hit boxes/AABB are rotated:
min: [5.940746670252036, 15.940746670252032],
max: [54.05925332974796, 64.05925332974797],
});
});
it('properly computes bounds of the object (custom center)', async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
const object = makeShapePainterRuntimeObject(runtimeScene);
expect(object.getWidth()).to.be(0);
expect(object.getHeight()).to.be(0);
object.drawLineV2(10, 10, 20, 30, 3);
object.setRotationCenter(10, 9);
// Check the automatically computed bounds (not impacted by the center):
const drawableX = 8.658359213500127;
const drawableY = 9.329179606750063;
const width = 12.683281572999746;
const height = 21.341640786499873;
expect(object.getDrawableX()).to.be(drawableX);
expect(object.getDrawableY()).to.be(drawableY);
expect(object.getWidth()).to.be(width);
expect(object.getHeight()).to.be(height);
// Check the center positioning:
expect(object.getCenterXInScene()).to.be(10);
expect(object.getCenterYInScene()).to.be(9);
expect(object.getCenterX()).to.be(10 - drawableX);
expect(object.getCenterY()).to.be(9 - drawableY);
// Check hit boxes (not impacted by the center, as no rotation is made):
expect(object.getAABB()).to.eql({
min: [drawableX, drawableY],
max: [drawableX + width, drawableY + height],
});
// Check after scaling (scaling is done from the origin):
object.setScale(2);
expect(object.getDrawableX()).to.be(2 * drawableX);
expect(object.getDrawableY()).to.be(2 * drawableY);
expect(object.getWidth()).to.be(2 * width);
expect(object.getHeight()).to.be(2 * height);
expect(object.getAABB()).to.eql({
min: [2 * drawableX, 2 * drawableY],
max: [2 * (drawableX + width), 2 * (drawableY + height)],
});
// Check after rotating (rotating is done from the center):
object.setAngle(45);
expect(object.getAABB()).to.eql({
// The hit boxes/AABB are rotated:
min: [-12.544534703986535, 16.568163668221555],
max: [35.57397195550939, 64.6866703277175],
});
// Draw outside of the current bounds.
const oldMinX = object.getAABB().min[0];
const oldMinY = object.getAABB().min[1];
const oldMaxX = object.getAABB().max[0];
const oldMaxY = object.getAABB().max[1];
const oldCenterX = object.getCenterXInScene();
const oldCenterY = object.getCenterYInScene();
object.drawLineV2(-10, -10, 21, 31, 3);
// Check that the center stays the same.
expect(object.getCenterXInScene()).to.be(oldCenterX);
expect(object.getCenterYInScene()).to.be(oldCenterY);
// Check that the AABB expands.
const newAABB = object.getAABB();
expect(newAABB.min[0]).below(oldMinX);
expect(newAABB.min[1]).below(oldMinY);
expect(newAABB.max[0]).above(oldMaxX);
expect(newAABB.max[1]).above(oldMaxY);
});
it('can transform points', async () => {
const runtimeGame = await gdjs.getPixiRuntimeGameWithAssets();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
loadScene(runtimeScene);
const object = makeShapePainterRuntimeObject(runtimeScene);
object.drawLineV2(0, 0, 10, 10, 2);
expect(object.getWidth()).to.be(11.414213562373096);
expect(object.getHeight()).to.be(11.414213562373096);
// Check changes in position/scale are taken into account:
object.setPosition(50, 100);
expect(object.transformToScene(10, 20)).to.eql([60, 120]);
object.setScale(2);
expect(object.transformToScene(10, 20)).to.eql([70, 140]);
// Check rotation with the default center:
expect(object.getCenterXInScene()).to.be(60);
expect(object.getCenterYInScene()).to.be(110);
expect(object.transformToScene(5, 5)).to.eql([60, 110]);
expect(object.transformToScene(10, 20)).to.eql([70, 140]);
object.setAngle(90);
expect(object.transformToScene(5, 5)).to.eql([60, 110]);
expect(object.transformToScene(10, 20)).to.eql([30, 120]);
// Check rotation with a custom center:
object.setRotationCenter(20, 9);
expect(object.transformToScene(10, 20)).to.eql([68, 98]);
expect(object.transformToDrawing(68, 98)).to.eql([10, 20]);
});
});