Blog posts about my javascript gamedev journey with ct.js

Pseudo isometric map and minimap with Perlin noise and ct.js

1) Generate the map

NB: Same as our previous article, we’ll use copies for the map, not tiles.

Our map will be a 2D array.
It has a width and a height.

var map = [];
var mapWidth = 40;
var mapHeight = 40;

We create it like this:

for (var y = 0; y < mapHeight; y++) {
    map[y] = new Array(mapWidth);
}

Now, we’ll fill our array thanks to maths!
Nah, don’t be afraid! ^^
You know what?
I’m bad with maths but hopefully, some people already did the job for us. ; )
One of that smart guys is Ken Perlin and he “Bring the Noise”!
If you want to read more about this, you can check wikipedia, it will be clearer than me.
Just keep in mind that it’s great to generate procedural textures, so maps.
You know what 2?
We are doubly lucky because ct.js has a module dedicated to Perlin noise (and simplex noise, its “successor”).
And you know what 3?
We are triple lucky because it’s very easy to use.
Enable the module and you are ready to use the power of maths!

// Initialization of the noise seed. As we leave the parenthesis empty, we'll get a random seed.
ct.noise.setSeed();
// Uneasy to explain, just play with that noiseScale and you'll get it ; )
var noiseScale = 30;
var value;
for (var x = 0; x < mapWidth; x++) {
    for (var y = 0; y < mapHeight; y++) {
        // We'll use Simplex, not Perlin.
        value = ct.noise.simplex2d(x / noiseScale, y / noiseScale);
        // It returns a value from -1 to 1, then it's up to you how you "distribute" the copy types: 
        if (value > -1 && value < -0.4) {
            value = 0;
        }
        else if (value > -0.4 && value < 0.1) {
            value = 1;
        }
        else if (value > 0.1 && value < 0.6) {
            value = 2;
        }
        else {
            value = 3;
        }
        map[y][x] = value;
    }
}

2) Display the map

Time to add some gfx!
I made 4 basic pseudo isometric tiles of 16x7 (24x12 would have been better but, I wanted to show more tiles).
If you want to know more about isometric graphics, again, wikipedia is a good start.

Add your textures to ct.js, create types from them and put their name in an array:

var copyTypes = [
    "water",// At index 0 put the name of the type you create for water
    "grass",// At index 1 put the name of the type you create for grass
    "soil", // At index 2 put the name of the type you create for soil
    "rock"  // At index 3 put the name of the type you create for rock
];

Thanks to that array, we can easily display the map without a list of “if” / “else if” / “else”.
Still, isometric maps are a bit more tricky to manage, but nothing hard really.
If you struggle to understand, I guess a practical way is to put some “tiles” one by one in your favorite graphics editor.
Also, of course, feel free to ask me questions on Discord or on the forum.

var copySize = 16;
var semiSize = copySize / 2;
var quarterSize = copySize / 4;
var xCopy = 0;
var yCopy = 0;

for (var y = 0; y < mapHeight; y++) {
    xCopy = (ct.camera.width / 2) - (semiSize * y) - semiSize;
    yCopy = quarterSize * (y + 1);
    for (var x = 0; x < mapWidth; x++) {
        ct.types.copy(copyTypes[map[y][x]], xCopy, yCopy);
        xCopy += semiSize;
        yCopy += quarterSize;
    } 
}

Now, you can “Launch” the project and see your map.
Each time you’ll restart the project, the map will be different and rather consistent, thanks to that “smart” mathematical noise.

3) Display the minimap

As you probably know, ct.js is built on Pixi.js, so we can use its large API.
To easily render our minimap, we’ll use PIXI.Graphics.
It allows to draw lines, rectangles, circles, etc., it’s very powerful. First, same as types, let’s store colors in an array:

var miniMapColors = [
    0x065AB5,//Water
    0x00B543,//Grass
    0xAB5236,//Soil
    0x83769C //Rock
];

Then, the code is similar to the map one, but this time, instead of adding a copy we draw a rectangle:

var tileValue;
xCopy = 0;
yCopy = 0;
// Declare a "graphic" where to draw
var graphics = new PIXI.Graphics();
for (var y = 0; y < mapHeight; y++) {
    xCopy = (mapWidth / 2) - y;
    yCopy = y;
    for (var x = 0; x < mapWidth; x++) {
        tileValue = map[y][x];
        // Draw a rectangle of the right color at a specified place for each "tile"
        graphics.beginFill(miniMapColors[tileValue]).drawRect(xCopy, yCopy, 2, 2);
        xCopy += 1;
        yCopy += 1;
    }
}
// Cache the whole for performance sake
graphics.cacheAsBitmap = true;
// Place it where you want
graphics.x = ct.camera.width - graphics.width;
graphics.y = ct.camera.height - graphics.height - 20;
// Add it to the room
this.addChild(graphics);

Conclusion

I don’t know about you, but for me, it’s very satisfying to click on the map to see a new one. ^^
Anyway, I hope it will give you some game ideas because that’s why we are here hehe:
Making games!

Have a good gamedev!

Published on 24-04-2021 by Thomas