New version is now up and running, tis way better!!! Even with objects culled you can still use their data and properties, like checking their position and such!!
This new version turned out amazing, i even added the feature to permanently cull objects, so that you can delete staying bullets without any events or logic.
That is great. Is the script useful if off-screen objects have to maintain behaviors, move, collide and so on? I imagine that everything that depends on the animation number, frame number and so on would have to be replaced by variables in that case, right?
I just found an issue that ill have to fix tomorrow.
The script dosent recreate all the properties, i need to add size and scale to it as well, also z order as such.
I dont have any tiles sprites on my recent projects, so i tried it on a old project that did and the result isnt very good, tiles sprites or sprites that were previously changed in size in the editor get recreated with their original size, which is an issue…
Like i said, ill try to fix it tomorrow and add it in when its done
Objects now get culled based on their Bounding Box and not their Origin Point, this ensures objects that are still in view but the Origin Point is not, dont get culled untill the entire object is out of view.
Recreating works the same, if the bounding box is in view, then the object is recreated.
More property handling to faithfully recreate objects, things like Size, Angle, Hidden/Showing and more was added.
Objects now get recreated with the same size they were culled, same for angles, or if the object was hidden, it will be set as hidden again.
All previous information is still being stored, like X and Y Position, to be called on and used even if the object is Culled.
I think at this point it does quite literally everything except for collision, and thats impossible without keeping the object in scene.
Every method i tried for collision while objects are culled does not work, at least for me, the performance gained is minimal, if any, completly deleting the object like my script does is the only way iv found of deastically improving performance.
The new version handles object timers, storing them and recreating while “sort of” keeping them running even when the object is culled.
What is actually happening is the timer is stored, then when it gets recreated, the elapsed time between cull and recreation is added to the object, essentially keeping it running.
If instead youd like the object timer to do things like “every x seconds add to a variable”, you can simple exclude the object that has that timer from being culled.
Version 4.0 will be put up tomorrow after i do some more testing and making sure it cant be optimized more!
This has been fun but its getting close to the definitive version!
If any of you has any feedback, it would be much appreciated!
I think you should make it an extension it would be great
Please sorry to say I got lost on the way this was exactly what my chatgpt app told me but I didn’t understand the process of culling still confused
Let me just go straight to the point I want to use this to make my Minecraft game performance better since it deals with so many blocks
For anyone who wants to read on what exactly goes on in the script, heres all the info on how it works!
Version 4.0
Overview
This script is designed to improve the performance of a game made with GDevelop 5 by culling (removing) objects that are outside the camera view and then restoring them when they come back into view. This is achieved by storing the state of objects before they are culled and restoring their state when they are recreated.
Detailed Breakdown
Initialization and Setup
Initialize Deleted Objects Storage: The script starts by checking if a property deletedObjects exists on runtimeScene. If it doesn’t exist, it initializes it as an empty object. This object will be used to store the state of culled objects.
if (!runtimeScene.deletedObjects) {
runtimeScene.deletedObjects = {};
}
Get Camera Information: The script fetches the camera position and size once. This is used to determine the visible area and calculate the boundaries for culling.
Get Cull Buffer: The script retrieves the CullBuffer scene variable. This variable is used to add a buffer zone around the camera’s visible area to prevent objects from being culled too aggressively.
Calculate Culling Boundaries: The script calculates the left, right, top, and bottom boundaries for culling based on the camera position, size, and cull buffer.
Set Up for Processing in Batches: To avoid spikes in CPU usage, the script processes objects in batches. It determines the start and end indices for the current batch based on a scene variable CullStartIndex.
const maxObjectsPerFrame = 50; // Adjust this number based on performance needs
const startIndex = runtimeScene.getVariables().has("CullStartIndex")
? runtimeScene.getVariables().get("CullStartIndex").getAsNumber()
: 0;
const endIndex = Math.min(startIndex + maxObjectsPerFrame, allObjectsInstances.length);
runtimeScene.getVariables().get("CullStartIndex").setNumber(endIndex === allObjectsInstances.length ? 0 : endIndex);
Get Current Time: The script gets the current scene time. This is used to track when objects are culled.
const currentTime = runtimeScene.getTimeManager().getTimeFromStart() / 1000; // Time in seconds
Iterate Over Objects: The script iterates over the objects in the current batch. For each object, it performs the following checks and actions:
for (let i = startIndex; i < endIndex; i++) {
const object = allObjectsInstances[i];
const objectName = object.getName();
const objectLayer = object.getLayer();
Skip Non-Base Layer Objects: Only objects on the base layer (default layer) are processed.
if (objectLayer !== "") continue;
Skip Objects Excluded from Culling: If an object has a variable named ExcludeCull set to true, it is skipped.
- **Store Object Properties**: The script stores the object's properties (position, angle, layer, hidden state, variables, timers, and culling time) for later restoration.
```javascript
if (!runtimeScene.deletedObjects[objectName]) {
runtimeScene.deletedObjects[objectName] = [];
}
const objectProperties = {
x: object.getX(),
y: object.getY(),
zOrder: object.getZOrder(),
angle: object.getAngle(),
layer: objectLayer,
hidden: object.isHidden(),
variables: {},
timers: {},
cullTime: currentTime
};
// Store object variables
const objectVariablesList = variables._variables.items;
for (const variableName in objectVariablesList) {
if (objectVariablesList.hasOwnProperty(variableName)) {
objectProperties.variables[variableName] = objectVariablesList[variableName].getAsString();
}
}
// Store object timers
if (object._timers && object._timers.items) {
const timerItems = object._timers.items;
for (const timerName in timerItems) {
if (timerItems.hasOwnProperty(timerName)) {
objectProperties.timers[timerName] = timerItems[timerName].getTime() / 1000.0; // Store the timer value in seconds
console.log(`Stored timer ${timerName} for object ${objectName} with time ${objectProperties.timers[timerName]}`);
}
}
} else {
console.log(`Object ${objectName} does not support timers or has no timers.`);
}
runtimeScene.deletedObjects[objectName].push(objectProperties);
objectsToStoreAndDelete.push(object);
Delete Objects: The script deletes the objects that are marked for deletion.
for (let i = 0; i < objectsToDelete.length; i++) {
objectsToDelete[i].deleteFromScene(runtimeScene);
}
for (let i = 0; i < objectsToStoreAndDelete.length; i++) {
objectsToStoreAndDelete[i].deleteFromScene(runtimeScene);
}
Restoring Culled Objects
Get Current Time for Restoration: The script gets the current scene time to calculate the elapsed time since the objects were culled.
const restoreTime = runtimeScene.getTimeManager().getTimeFromStart() / 1000; // Time in seconds
Iterate Over Stored Objects: The script iterates over the stored objects to recreate them if they are within the visible boundaries.
Calculate Elapsed Time: The script calculates the elapsed time since the object was culled.
const elapsedTime = restoreTime - data.cullTime;
Recreate Object and Restore Properties: The script recreates the object and restores its properties including position, angle, layer, hidden state, variables, and timers (adjusting for elapsed time).
const newObject = runtimeScene.createObject(objectName);
newObject.setPosition(data.x, data.y);
newObject.setZOrder(data.zOrder);
newObject.setAngle(data.angle);
newObject.setLayer(data.layer);
// Restore optional properties
if (data.width !== undefined && newObject.setWidth) {
newObject.setWidth(data.width);
}
if (data.height !== undefined && newObject.setHeight) {
newObject.setHeight(data.height);
}
if (data.opacity !== undefined && newObject.setOpacity) {
newObject.setOpacity(data.opacity);
}
// Restore hidden state
if (data.hidden !== undefined) {
newObject.hide(data.hidden);
}
// Restore variables
const targetVariables = newObject.getVariables();
const sourceVariables = data.variables;
for (const variableName in sourceVariables) {
if (sourceVariables.hasOwnProperty(variableName)) {
targetVariables.get(variableName).setString(sourceVariables[variableName]);
}
}
// Restore timers and add elapsed time
const objectTimers = data.timers;
for (const timerName in objectTimers) {
const timerValue = objectTimers[timerName];
newObject.resetTimer(timerName); // Reset the timer with the same name
newObject._timers.get(timerName).setTime((timerValue + elapsedTime) * 1000); // Add elapsed time to the timer value
console.log(`Restored timer ${timerName} for object ${objectName} with time ${timerValue + elapsedTime} seconds`);
}
// Remove from the list after recreation
objectList.splice(i, 1);
Clean Up Empty Lists: After recreating objects, the script cleans up any empty lists to free memory.
if (objectList.length === 0) {
delete deletedObjects[objectName];
}
Summary of the Script
Initialization:
Sets up a storage object for deleted objects (runtimeScene.deletedObjects).
Retrieves camera position and size.
Calculates culling boundaries based on camera size and a buffer.
Processing Objects for Culling:
Processes objects in batches to avoid CPU spikes.
Skips objects on non-base layers and those marked as excluded from culling.
Checks if objects are outside the culling boundaries.
Stores properties of objects to be culled, including their position, variables, and timers.
Queues objects for deletion.
Deleting Culled Objects:
Deletes objects that are outside the culling boundaries.
Restoring Culled Objects:
Recreates objects that have moved back into the visible area.
Restores their properties, including position, variables, and timers (with elapsed time added).
Clean Up:
Cleans up the storage to free memory by removing empty lists.
How Storing and Restoring Timers Works
Storing Timers:
Before deleting an object, the script captures its timers and stores them in runtimeScene.deletedObjects.
It stores the elapsed time of each timer in seconds.
Restoring Timers:
When recreating an object, the script restores its timers by resetting them and setting their time to the stored value plus the elapsed time since culling.
Conclusion
This script efficiently manages the culling and restoration of objects to improve game performance in GDevelop 5. By carefully storing and restoring object properties and timers, it ensures a smooth and consistent game experience without losing the state of objects that move out of and back into the camera’s view.
Great
I can try to make it into an extension
When I’m less busy
But I don’t know how to setup extension icon so it will just have the gdevelop icon
If am done turning it into an extension I’ll send it to you
Great job!!!
Could you add the culling option following the layer in addition to the camera area?
And is it possible to change the position of deleted objects in memory?
Example: For a P2P game, the units of the different players are moving.
Great! You’re motivated
Be able to make a selection of objects to delete or spare, depending on the layer.
Ex: The 2 Fire objects are on a different layer.