A little help with some JavaScript, please

How do I…

I have a script that should generate objects in a grid. The object type is “Sprite”, the name is “Sprocket”, and the layer to be generated on is “Sprockets”. I have three arrays, one for the X coordinates, one for the Y coordinates, and one for the angle of the sprocket. Each sprocket should be generated at a random angle picked from the angles array.

What is the expected result

When the game loads, the sprockets should generate in a grid, with each facing a random angle.

What is the actual result

I’m getting this error:

Uncaught exception: TypeError: Cannot read properties of undefined (reading ‘length’)

I’m pretty sure it’s from the following line, but I’m a novice JS programmer, so I’m not sure how to fix it.

const randomAngleIndex = Math.floor(Math.random() * angle.length);

Code

(function (runtimeScene, objects /*Sprocket*/) {
    if (runtimeScene.getTimeManager().isFirstFrame()) {
        // The sprockets' X coordinates
        const X = [
            28, 58, 88, 118, 148, 178, 208, 238, 268, 298, 328, 358, 388, 418, 448,
            478, 508, 538, 568, 598, 628, 658,
        ];
        // The sprockets' Y coordinates
        const Y = [
            128, 158, 188, 218, 248, 278, 308, 338, 368, 398, 428, 458, 488, 518,
            548, 578, 608, 638, 668, 698, 728, 758, 788, 818, 848, 878, 908, 938,
            968, 998, 1028, 1058, 1088, 1118, 1148, 1178, 1208,
        ];
        // The sprockets' angles in degrees
        const angle = [0, 90, 180, 270];

        // Set the object type
        const objectType = 'Sprite';

        // Set the object layer
        const layer = runtimeScene.getLayer('Sprockets');

        // Iterate through each coordinate in the X and Y arrays
        for (let i = 0; i < X.length; i++) {
            for (let j = 0; j < Y.length; j++) {

                // Get a random angle from the angle array
                const randomAngleIndex = Math.floor(Math.random() * angle.length);
                const randomAngle = angle[randomAngleIndex];

                // Create a Sprocket at the current coordinates with the random angle
                const sprite = new gdjs.RuntimeObject(runtimeScene, objectType);
                sprite.setX(X[i]);
                sprite.setY(Y[j]);
                sprite.setAngle(randomAngle);

                // Set the layer of the Sprite object
                sprite.setLayer(layer);
                
                // Add the Sprite object to the scene
                runtimeScene.addObject(sprite);
            }
        }

    }
})(runtimeScene, objects /*Sprocket*/);

Thanks.

IDK about the function part but this works for me as long as there’s a Sprocket object somewhere on the scene.

if (runtimeScene.getTimeManager().isFirstFrame()) {
    // The sprockets' X coordinates
    const X = [28, 58, 88, 118, 148, 178, 208, 238, 268, 298, 328, 358, 388, 418, 448,
        478, 508, 538, 568, 598, 628, 658];
    // The sprockets' Y coordinates
    const Y = [128, 158, 188, 218, 248, 278, 308, 338, 368, 398, 428, 458, 488, 518,
        548, 578, 608, 638, 668, 698, 728, 758, 788, 818, 848, 878, 908, 938,
        968, 998, 1028, 1058, 1088, 1118, 1148, 1178, 1208];
    // The sprockets' angles in degrees
    const angle = [0, 90, 180, 270];

    // Set the object layer
    const layer = runtimeScene.getLayer('Sprockets');

    // Iterate through each coordinate in the X and Y arrays
    for (let i = 0; i < X.length; i++) {
        for (let j = 0; j < Y.length; j++) {

            // Get a random angle from the angle array
            const randomAngleIndex = Math.floor(Math.random() * angle.length);
            const randomAngle = angle[randomAngleIndex];

            const newObj = runtimeScene.createObject("Sprockets");
            newObj.setPosition(X[i], Y[j]);

            newObj.setAngle(randomAngle);

            // Set the layer of the Sprite object
            newObj.setLayer(layer);
        }
    }
}

Hi Keith,

Thanks for the response. I pasted your code in, and I dragged a single sprocket instance onto the scene just above the grid area, and I get this error when the scene loads:

IDK what to say. I copied the code from this message and I pasted it into a new JavaScript event and it works. Make sure your object name is Sprockets.

edit: I think you might have had Sprocket for the object, I used Sprockets I think I confused the layer and object.

Ah, yeah, sorry, I should have caught that. Unfortunately, after updating the object name in the code, the error is gone, but it’s not generating the sprockets. It’s likely something I have set up differently than you, other than the code. I’ll investigate.

I just realized something… the sprockets’ layer masks can’t be overlapping when the sprockets are generated. This just got more complicated…

So, for reference, this is how the sprockets should be arranged, where none are touching.

image

I just discovered the “Create multiple copies of an object” extension, and I’m now using that to generate my grid of sprockets.

This is working perfectly, I just need to randomize the angles of the sprockets without having their collision masks touch. If anyone knows how to do that, please let me know.

Thanks.

1 Like

So, I’ve got the grid auto-generating, I just need to randomize the angles of the sprockets. I’m thinking a simple script should accomplish this, so I’ve been using ChatGPT to help me write it, but I’m having a hard time debugging it. This is what I currently have:

// Iterate through all objects of type "Sprocket" on the "Sprockets" layer
const sprockets = runtimeScene.getObjects("Sprocket");

// Loop through each object and set a random rotation angle
for (let i = 0; i < sprockets.length; ++i) {
    const sprocketObject = sprockets[i];

    // Set a random rotation angle (0, 90, 180, or 270 degrees)
    const randomRotation = Math.floor(Math.random() * 4) * 90;
    sprocketObject.getBehavior("Sprite").setAngle(randomRotation);
}

Any JavaScript gurus out there?

You are trying to create a grid of objects? There is a extension specifically for that. Take a look at this Create multiple copies of an object - GDevelop documentation.

Oh, I did not saw this message

1 Like

You don’t need to access the behavior to rotate it. It would be
sprocketObject.setAngle(randomRotation);

or you could skip const sprocketObject = sprockets[i]; and use sprockets[i].setAngle(randomRotation);

1 Like

Thanks, Keith. I tried this:

if (runtimeScene.getTimeManager().isFirstFrame()) {
    // Iterate through all objects of type "Sprocket" on the "Sprockets" layer
    const sprockets = runtimeScene.getObjects('Sprocket');

    // Loop through each object and set a random rotation angle
    for (let i = 0; i < sprockets.length; ++i) {

        // Set a random rotation angle (0, 90, 180, or 270 degrees)
        const randomRotation = Math.floor(Math.random() * 4) * 90;
        sprockets[i].setAngel(randomRotation);
    }
}

But I’m getting this:

sprockets[i].setAngel is not a function

I may have misunderstood and goofed the code.

oops, angle not angel. I’ll fix my post.

You could also use:

const sprockets = runtimeScene.getObjects("Sprocket");
for (const sprocket of sprockets) {
    sprocket.setAngle(Math.floor(Math.random() * 4) * 90);
}

D’oh! I didn’t catch the typo either. I fixed it, and it works! Thanks, Keith.

The only issue now is that some of the sprockets overlap. How hard would it be to have them check if masks are overlapping, and rotate them another 90 degrees? I’m thinking a while loop that would check, rotate, check, rotate, until there are no overlaps. I’m not sure how to access the masks in the script, though.

image

That part seems incredibly problematic. Not difficult, just time consuming. I don’t know how many times you would need to try to find valid sprockets to get a good mix.

I don’t know if you can rotate one sprocket without rotating several. It would almost have to play by itself for X number of moves without being animated.

You could also use a collection of already rotated external layouts. Or maybe, blocks of sprockets that all have the same border arrangement and can be placed in random blocks. IDK. I’m just talking out loud. It’s a bit like a seamless tile.

Yeah, I had a feeling it would be complicated. I was actually considering making a dozen or so custom layouts, and then having the game select one from random each play. Your custom blocks of sprockets is an interesting idea, too.

I’ll go with the custom prebuilt layouts for now. Shouldn’t take too long, and at least I know how to do it hehe.

Thanks for all your help, brother.

I have an idea. The only reason I don’t want the sprockets overlapping when the game loads is each overlap will be considered a collision, causing the sprockets to rotate without the player doing anything. Instead of trying to set it up so that no two sprockets overlap, how about simply distinguishing between 1) sprockets at rest overlapping and 2) sprockets overlapping due to legitimate collision? This way, the game should ignore overlapping sprockets unless one is hitting the other.

I’m thinking I just need to add an action here:

image

So it would be something like:

image

Or:

image

Unfortunately, both of those break even legit collisions. Do you know of an action that would work here?

Never mind, I got it! All I did was adjust the masks so that they overlap when hitting during rotation, but they don’t overlap when resting, and it works! w00t!

1 Like