How to Retrieve Object Timers using JavaScript?

Iv been going trough the documentation and iv found a bunch of stuff about timers and the timemanager and such, but nothing that works to retrieve object timers…

this is driving me nutts, can someone give any sort of example on how to do it or what methods to use?

Im trying to add timers to the culling script that im developing.

I can simply exclude objects that have timers and that would fix it in a simple manner, but i was trying to see if it was possible to bake into the original script.

Any help would be appreciated <3

1 Like

The place to start is the sprite runtime. I’ve never used timers in Javascript.

https://docs.gdevelop.io/GDJS%20Runtime%20Documentation/classes/gdjs.SpriteRuntimeObject.html

There are a lot of timer methods. Just click the method and make sure it’s not depreciated. I noticed a couple were.

These 2 seem to get the time. The difference is whether they return NaN or zero if a timer doesn’t exist.

getTimerElapsedTimeInSecondsOrNaN
getTimerElapsedTimeInSeconds

I’m not at a PC. It seems straightforward.
Remember to use Object[#] or something that goes through all instances. I’m still a little fuzy with the later.

I’m curious now. So, I might try it later.

1 Like

I worked it out just as you posted, but thanks a bunch anyways, your always a massive help!

Honestly… i got all the documentation i could, when i couldnt find it there, i went direct to the source… as it turns out, timer stuff was all organized next to each other on there, it was much easier to grab.

I got it working! If you want to geek on some code, you can be the first to see v4.0 of the Object Culling script :slight_smile:

  • It now also keeps track of object timers!
  • It stores the timer when the object is culled
  • When the object is recreated, it adds the timer back to the object
  • It checks the elapsed time between cull and recreating and adds it to the objects timer value
// Initialize the deletedObjects object if it doesn't exist
if (!runtimeScene.deletedObjects) {
    runtimeScene.deletedObjects = {};
}

// Get the camera position and size once
const cameraLayer = runtimeScene.getLayer("");
const cameraX = cameraLayer.getCameraX();
const cameraY = cameraLayer.getCameraY();
const cameraWidth = cameraLayer.getCameraWidth();
const cameraHeight = cameraLayer.getCameraHeight();

// Get the value of the CullBuffer scene variable once
const cullBuffer = runtimeScene.getVariables().get("CullBuffer").getAsNumber();

// Calculate the boundaries for object culling based on the CullBuffer variable once
const leftBoundary = cameraX - cameraWidth / 2 - cullBuffer;
const rightBoundary = cameraX + cameraWidth / 2 + cullBuffer;
const topBoundary = cameraY - cameraHeight / 2 - cullBuffer;
const bottomBoundary = cameraY + cameraHeight / 2 + cullBuffer;

// Get all instances of objects in the scene
const allObjectsInstances = runtimeScene.getAdhocListOfAllInstances();

// Arrays to hold objects for deletion and recreation
const objectsToDelete = [];
const objectsToStoreAndDelete = [];

// Process a subset of objects each frame to avoid spikes in CPU usage
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 the current scene time
const currentTime = runtimeScene.getTimeManager().getTimeFromStart() / 1000; // Time in seconds

// Iterate over a subset of objects in the scene
for (let i = startIndex; i < endIndex; i++) {
    const object = allObjectsInstances[i];
    const objectName = object.getName();
    const objectLayer = object.getLayer();

    // Only process objects on the base layer
    if (objectLayer !== "") continue;

    const variables = object.getVariables();

    // Check if the object has a variable named "ExcludeCull" and its value is true
    if (variables.has("ExcludeCull") && variables.get("ExcludeCull").getAsBoolean()) {
        continue; // Skip culling for this object if "ExcludeCull" is true
    }

    // Get the bounding box of the object
    const aabb = object.getAABB();
    const objectLeft = aabb.min[0];
    const objectRight = aabb.max[0];
    const objectTop = aabb.min[1];
    const objectBottom = aabb.max[1];

    // Check if the object is completely outside the culling boundaries
    if (objectRight < leftBoundary || objectLeft > rightBoundary || objectBottom < topBoundary || objectTop > bottomBoundary) {
        // Check if the object has a variable named "Cull" and its value is true
        if (variables.has("Cull") && variables.get("Cull").getAsBoolean()) {
            objectsToDelete.push(object); // Queue the object for direct deletion
        } else {
            // Store the position, name, properties, timers, and culling time to recreate later
            if (!runtimeScene.deletedObjects[objectName]) {
                runtimeScene.deletedObjects[objectName] = [];
            }

            // Common properties for all objects
            const objectProperties = {
                x: object.getX(),
                y: object.getY(),
                zOrder: object.getZOrder(),
                angle: object.getAngle(),
                layer: objectLayer,
                hidden: object.isHidden(), // Store hidden state
                variables: {},
                timers: {}, // Object to store timer names and elapsed time
                cullTime: currentTime // Store the current time when the object is culled
            };

            // Store object variables
            const objectVariablesList = variables._variables.items;
            for (const variableName in objectVariablesList) {
                if (objectVariablesList.hasOwnProperty(variableName)) {
                    objectProperties.variables[variableName] = objectVariablesList[variableName].getAsString();
                }
            }

            // Check for and store optional properties
            if (object.getWidth) {
                objectProperties.width = object.getWidth();
            }
            if (object.getHeight) {
                objectProperties.height = object.getHeight();
            }
            if (object.getOpacity) {
                objectProperties.opacity = object.getOpacity();
            }

            // Store object timers directly from _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);

            // Queue the object for deletion and storage
            objectsToStoreAndDelete.push(object);
        }
    }
}

// Perform deletion of objects that are fully culled
for (let i = 0; i < objectsToDelete.length; i++) {
    objectsToDelete[i].deleteFromScene(runtimeScene);
}

// Perform deletion of objects that are stored and then deleted
for (let i = 0; i < objectsToStoreAndDelete.length; i++) {
    objectsToStoreAndDelete[i].deleteFromScene(runtimeScene);
}

// Get the current scene time for restoring objects
const restoreTime = runtimeScene.getTimeManager().getTimeFromStart() / 1000; // Time in seconds

// Recreate objects within the boundaries
const deletedObjects = runtimeScene.deletedObjects;
for (let objectName in deletedObjects) {
    const objectList = deletedObjects[objectName];
    for (let i = objectList.length - 1; i >= 0; i--) {
        const data = objectList[i];
        const objectLeft = data.x;
        const objectRight = data.x + (data.width || 0);
        const objectTop = data.y;
        const objectBottom = data.y + (data.height || 0);

        // Check if the position is within the recreation boundaries
        if (objectRight >= leftBoundary && objectLeft <= rightBoundary && objectBottom >= topBoundary && objectTop <= bottomBoundary) {
            // Calculate the elapsed time since culling
            const elapsedTime = restoreTime - data.cullTime;

            // Recreate the object
            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);
        }
    }
}

// Cleanup empty lists to free memory
for (let objectName in deletedObjects) {
    if (deletedObjects[objectName].length === 0) {
        delete deletedObjects[objectName];
    }
}

1 Like