Tofu
Tofu

Reputation: 3613

Isometric grid coordinates

I'm trying to do an isometric projection in HTML5 and the closest I can get is by scaling and rotating the viewport

window.addEventListener('DOMContentLoaded', (event) => {
    class Point {
        constructor() {
            this.x = 0;
            this.y = 0;
        }
    }
    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    function tick(time) {
        ctx.save();

        ctx.translate(250,0);

        ctx.scale(1, 0.5);
        ctx.rotate(45 * Math.PI /180);
        let p = new Point();
        p.x = 4*32;
        p.y = 2*32;

        ctx.fillStyle = 'green';
        ctx.fillRect(p.x,p.y,32,32);
        for (var x = 0; x < 10; ++x) {
            for (var y = 0; y < 10; ++y) {
                ctx.strokeStyle = 'black';
                ctx.strokeRect(x*32, y*32, 32, 32);
            }
        }

        ctx.restore();

        requestAnimationFrame(tick);
    }
    requestAnimationFrame(tick);
});

And it does work..but this isn't a practical solution because then when I try to draw isometric sprites within that scaled and rotated view, it looks really distorted. So I'd rather not go that route unless I can get it to work without distorting my sprites.

I also tried this website https://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-primer-for-game-developers-updated--cms-28392

Which provided an equation for generating isometric grids. I used their cartesianToIsometric function to try and make an iso grid

window.addEventListener('DOMContentLoaded', (event) => {
    class Point {
        constructor() {
            this.x = 0;
            this.y = 0;
        }
    }
    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    function cartesianToIsometric(cartPt) {
        var tempPt = new Point();
        tempPt.x = cartPt.x - cartPt.y;
        tempPt.y = (cartPt.x + cartPt.y) / 2;
        return (tempPt);
    }
    function tick(time) {

        for (var x = 0; x < 50; ++x) {
            for (var y = 0; y < 50; ++y) {
                let p = new Point();
                p.x = x * 32;
                p.y = y * 32;
                let iso = cartesianToIsometric(p);

                ctx.strokeStyle = 'black';
                ctx.strokeRect(iso.x, iso.y, 32, 32);
            }
        }
        requestAnimationFrame(tick);
    }
    requestAnimationFrame(tick);
});

but it still looked wrong Unless I rotate or scale the viewport to correct it. enter image description here

So my question is..how can I draw an isometric grid without scaling and rotating my viewport(if possible)

If I have to scale my viewport like the first example, how can I do it without distorting my sprites.... Sorry if this question is confusing to read my knowledge on this is iffy..

Here's my html file index.html

<html>
    <head>
        <script src="index.js"></script>
    </head>
    <body>
        <canvas id="canvas"></canvas>
    </body>
</html>

Upvotes: 2

Views: 1880

Answers (1)

Yavuz Tas
Yavuz Tas

Reputation: 354

Looks like you are so close to the solution but a little bit confused. Let me explain:

Normally, you don't have to rotate and scale your canvas viewport. This is something you do when you create your isometric sprites. You should already have some isometric sprites and all you have to do just to put them in correct isometric coordinates.

Your second approach will do exactly what I mean, calculating the isometric points by using cartesian coordinates. It should work, no need to draw rectangles just place the images on the isometric coordinates.

The only trick here you should place your isometric sprites from their bottom center points:

for (var x = 0; x < 50; ++x) {
    for (var y = 0; y < 50; ++y) {
        let p = new Point();
        p.x = x * 32;
        p.y = y * 32;
        let iso = cartesianToIsometric(p);

        // Apply offset to place each isometric image from its bottom center. 
        // The default pivot point (top left) won't do good 
        // because we need to stack them up according to their heights.
        let offset = {x: floorImageWidth/2, y: floorImageHeight}
        ctx.drawImage(floor, iso.x - offset.x, iso.y - offset.y);
    }
}

I also suggest using a separate function to draw iso points (not the rectangles) so that you can debug your positions:

let debug = true;

function debugDraw(time) {
    for (var x = 0; x < 50; ++x) {
        for (var y = 0; y < 50; ++y) {
            let p = new Point();
            p.x = x * tileWidth;
            p.y = y * tileWidth;
            let iso = cartesianToIsometric(p);

            // draw pivot point for each tile
            ctx.fillStyle = 'yellow';
            ctx.fillRect(iso.x - 5, iso.y - 5, 10, 10);
        }
    }
}

if(debug){
    requestAnimationFrame(debugDraw)
}

Plus, here is a working demo for you to experiment further: https://codepen.io/justintc/pen/eYpMabx

Hope it helps, cheers!

Upvotes: 1

Related Questions