Files
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

359 lines
10 KiB
JavaScript

// @ts-check
describe('gdjs.LinksManager', function () {
const doTest = (
object1Names,
object2Names,
object3Names,
eventsFunctionContext
) => {
const runtimeGame = gdjs.getPixiRuntimeGame();
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
runtimeScene.loadFromScene({
sceneData: {
layers: [
{
name: '',
visibility: true,
cameras: [],
effects: [],
ambientLightColorR: 127,
ambientLightColorB: 127,
ambientLightColorG: 127,
isLightingLayer: false,
followBaseLayerCamera: false,
},
],
variables: [],
behaviorsSharedData: [],
objects: [],
objectsGroups: [],
instances: [],
r: 0,
v: 0,
b: 0,
mangledName: 'Scene1',
name: 'Scene1',
stopSoundsOnStartup: false,
title: '',
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: [],
});
const manager = gdjs.LinksManager.getManager(runtimeScene);
const object1A = new gdjs.RuntimeObject(
runtimeScene,
{
name: object1Names[0],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object1B = new gdjs.RuntimeObject(
runtimeScene,
{
name: object1Names[1],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object1C = new gdjs.RuntimeObject(
runtimeScene,
{
name: object1Names[2],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object2A = new gdjs.RuntimeObject(
runtimeScene,
{
name: object2Names[0],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object2B = new gdjs.RuntimeObject(
runtimeScene,
{
name: object2Names[1],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object2C = new gdjs.RuntimeObject(
runtimeScene,
{
name: object2Names[2],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object3A = new gdjs.RuntimeObject(
runtimeScene,
{
name: object3Names[0],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object3B = new gdjs.RuntimeObject(
runtimeScene,
{
name: object3Names[1],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
const object3C = new gdjs.RuntimeObject(
runtimeScene,
{
name: object3Names[2],
type: '',
behaviors: [],
variables: [],
effects: [],
},
undefined
);
runtimeScene.addObject(object1A);
runtimeScene.addObject(object1B);
runtimeScene.addObject(object1C);
runtimeScene.addObject(object2A);
runtimeScene.addObject(object2B);
runtimeScene.addObject(object2C);
runtimeScene.addObject(object3A);
runtimeScene.addObject(object3B);
runtimeScene.addObject(object3C);
/**
* @param {gdjs.RuntimeObject} object
* @param {Hashtable<gdjs.RuntimeObject[]>} objectsLists
* @returns a selection of picked objects
*/
const pickObjectsLinkedTo = (object, objectsLists) => {
let pickedSomething = gdjs.evtTools.linkedObjects.pickObjectsLinkedTo(
runtimeScene,
objectsLists,
object,
eventsFunctionContext
);
return { pickedSomething, objectsLists };
};
it('can link two objects', function () {
manager.linkObjects(object1A, object2A);
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object1A,
Hashtable.newFrom({ obj2: [object2A, object2B, object2C] })
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj2').length).to.be(1);
expect(objectsLists.get('obj2')[0]).to.be(object2A);
}
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object2A,
Hashtable.newFrom({ obj1: [object1A, object1B, object1C] })
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj1').length).to.be(1);
expect(objectsLists.get('obj1')[0]).to.be(object1A);
}
});
it('can select from already picked objects', function () {
manager.linkObjects(object1A, object2B);
// object1A <--> object2A, object2B
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object1A,
// object2B was discarded from a parent condition
Hashtable.newFrom({ obj2: [object2A, object2C] })
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj2').length).to.be(1);
expect(objectsLists.get('obj2')[0]).to.be(object2A);
}
});
// This is a local group. In an event function context,
// it can groups objects that are flatten scene groups.
it('can select from a group', function () {
manager.linkObjects(object1A, object3C);
// object1A <--> object2A, object2B, object3C
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object1A,
// This is a group of obj2 and obj3.
Hashtable.newFrom({
obj2: [object2A, object2B, object2C],
obj3: [object3A, object3B, object3C],
})
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj2').length).to.be(2);
expect(objectsLists.get('obj2')).to.contain(object2A);
expect(objectsLists.get('obj2')).to.contain(object2B);
expect(objectsLists.get('obj3').length).to.be(1);
expect(objectsLists.get('obj3')[0]).to.be(object3C);
}
});
it('can link more objects', function () {
// Including the same objects as before
manager.linkObjects(object1A, object2A);
manager.linkObjects(object1A, object2B);
manager.linkObjects(object1A, object2C);
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object1A,
Hashtable.newFrom({ obj2: [object2A, object2B, object2C] })
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj2').length).to.be(3);
expect(objectsLists.get('obj2')).to.contain(object2A);
expect(objectsLists.get('obj2')).to.contain(object2B);
expect(objectsLists.get('obj2')).to.contain(object2C);
}
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object2C,
Hashtable.newFrom({ obj1: [object1A, object1B, object1C] })
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj1').length).to.be(1);
expect(objectsLists.get('obj1')[0]).to.be(object1A);
}
});
it('supports removing links', function () {
manager.removeLinkBetween(object1A, object2B);
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object1A,
Hashtable.newFrom({ obj2: [object2A, object2B, object2C] })
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj2').length).to.be(2);
expect(objectsLists.get('obj2')).to.contain(object2A);
expect(objectsLists.get('obj2')).to.contain(object2C);
}
manager.linkObjects(object1B, object2C);
manager.removeAllLinksOf(object1A);
manager.removeAllLinksOf(object1A);
{
const { pickedSomething } = pickObjectsLinkedTo(
object1A,
Hashtable.newFrom({ obj2: [object2A, object2B, object2C] })
);
expect(pickedSomething).to.be(false);
}
{
const { pickedSomething } = pickObjectsLinkedTo(
object2A,
Hashtable.newFrom({ obj1: [object1A, object1B, object1C] })
);
expect(pickedSomething).to.be(false);
}
{
const { pickedSomething, objectsLists } = pickObjectsLinkedTo(
object2C,
Hashtable.newFrom({ obj1: [object1A, object1B, object1C] })
);
expect(pickedSomething).to.be(true);
expect(objectsLists.get('obj1').length).to.be(1);
expect(objectsLists.get('obj1')[0]).to.be(object1B);
}
});
};
// Following object names are the names of the objects in the scene.
// The test cases are using local names 'obj1', 'obj2', 'obj3'.
[
{
caseName: 'scene',
// Called from a scene, no event function context.
// Scene names are the same as local names.
eventsFunctionContext: null,
object1Names: ['obj1', 'obj1', 'obj1'],
object2Names: ['obj2', 'obj2', 'obj2'],
object3Names: ['obj3', 'obj3', 'obj3'],
},
// In event functions, the object names from the scene are different
// than the local names.
{
caseName: 'extension',
// The implementation only check it exists.
eventsFunctionContext: {},
object1Names: ['SceneObj1', 'SceneObj1', 'SceneObj1'],
object2Names: ['SceneObj2', 'SceneObj2', 'SceneObj2'],
object3Names: ['SceneObj3', 'SceneObj3', 'SceneObj3'],
},
// When groups are passed in parameters, they are flattened as an object.
// So, the lists will contains objects with different names.
{
caseName: 'extension with groups',
// The implementation only checks it exists.
eventsFunctionContext: {},
object1Names: ['SceneObj1', 'SceneObj1p', 'SceneObj1p'],
object2Names: ['SceneObj2p', 'SceneObj2p', 'SceneObj2'],
object3Names: ['SceneObj3p', 'SceneObj3p', 'SceneObj3'],
},
].forEach((testCase) => {
describe(testCase.caseName, function () {
doTest(
testCase.object1Names,
testCase.object2Names,
testCase.object3Names,
testCase.eventsFunctionContext
);
});
});
});