coding

Hiding parts of the map on Phaser JS - Game Devlog #21

Written in July 6, 2021 - 🕒 3 min. read

For those of you that don’t know, Metroidvania is probably my favorite game genre, so I decided to add a feature from Metroidvania games to Super Ollie, hidden stage parts that show up when you get close to them. Check the gif below.

Hidden wall

For this, I will need 2 new layer properties on Tiled, one called hideOrShowOnContact to flag if that layer should be hidden/displayed when the player interacts with it, and one called isHidden to flag if that layer should start hidden or not.

Show or hide layer on Tiled
Show or hide layer on Tiled

Now I need to loop through all my map layers to check if any of them contains these properties, something like:

// dynamicLayers is an array of Phaser.GameObjects.Group
const { dynamicLayers } = this.mapData;
Object.values(dynamicLayers).forEach((dynamicLayerGroup) => {
    const layers = dynamicLayerGroup.getChildren();
    layers.forEach((layer) => {
        // Check layers that should be hidden/shown on contact
        if (layer.hideOrShowOnContact) {
            // TODO
        }
    });
});

With that in place, I need to create a trigger to hide/show that layer, and the best way to do it is when creating the map and the custom colliders, which I explained in my devlog #3, and since I want to create the stages on Tiled and let the code figure out where these colliders need to be, I will create a loop that goes through all tiles to create colliders for the entire area of the layer, like the image below.

Layer area colliders
Layer area colliders

There are many tutorials on how to do this, just search for JavaScript get area 2d array and you will see some results. After creating these custom colliders, I will save them into a new property in the layer called hideOrShowOnContactTriggers so I can use them later in the game scene.

dynamicLayer.layer.properties.forEach((property) => {
    const { value, name } = property;
    const customColliders = generateCustomCollidersForAreaOf2DLayer(dynamicLayer);
    dynamicLayer.hideOrShowOnContactTriggers = new GameGroup({
        scene,
        children: customColliders,
        name: dynamicLayer.layer.name,
    });

    // if the layer is supposed to start the game hidden, then set it's alpha to 0
    if (name === 'isHidden') {
        dynamicLayer.setAlpha(0);
    }
});

With that logic in place, I can go back to my main loop and start the logic of the collision with the hero. First I will get all the tiles from the layer that is in the same position as the hero, if the hero is actually in contact with any of these tiles, it means that I need to trigger the layer to be hidden if the hero isn’t in contact with any of the layer’s tile, then the layer will be displayed.

// dynamicLayers is an array of Phaser.GameObjects.Group
const { dynamicLayers } = this.mapData;
Object.values(dynamicLayers).forEach((dynamicLayerGroup) => {
    const layers = dynamicLayerGroup.getChildren();
    layers.forEach((layer) => {
        // Check layers that should be hidden/shown on contact
        if (layer.hideOrShowOnContact) {
            this.physics.add.overlap(layer.hideOrShowOnContactTriggers, this.player, () => {
                const tiles = layer.getTilesWithinWorldXY(
                    this.player.body.x,
                    this.player.body.y,
                    this.player.body.width,
                    this.player.body.height
                );

                const tile = tiles.find((t) =>
                    t?.index >= 0
                    && t.layer.name === layer.layer.name);

                if (tile) {
                    this.tweens.add({
                        targets: layer,
                        alpha: 0,
                        duration: 300,
                        ease: 'Power2',
                    });
                } else {
                    this.tweens.add({
                        targets: layer,
                        alpha: 1,
                        duration: 300,
                        ease: 'Power2',
                    });
                }
            });
        }
    });
});

That’s pretty much it. You can tweak the tween numbers to your liking, but the result with these numbers is quite good I think.

I’m on a perfect development spree lately, full of good ideas for game features, but I’m struggling big time into making a nice level design, please let me know if you have resources on how to get good at level design because damn, it’s hard.

See you in the next one.

Tags:


Post a comment

Comments

No comments yet.