Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

Is there a simple way to get a true isometric projection with the HTML5 canvas element?

share|improve this question
    
Are you looking for libraries or how-to render using the Html5 Canvas? –  FxIII Jul 12 '11 at 20:17
add comment

2 Answers 2

One of the properties of an isometric projection (and all axonometric projections) is that objects in the distance do not grow smaller.

Therefore, trivially, HTML5 supports isometric projections because you can just draw all your art assets that way, and overlay them to create the depth.

share|improve this answer
add comment

Edit: Revised the answer so it is more clear.

Here is a quick note on how to modify something simple like a square so that it is isometric. What I do here is run 3 draw routines. First, I draw a normal square who's center origin is the middle of the canvas (but your origin could be anywhere). Then, I make some basic calculations to turn it into an isometric polygon. This isn;t the "right" way to do this, but it should help you understand what the math in the next draw routine is doing. I then re-draw the polygon using a isometric transform, which is a short cut of sorts for doing what we did manually in the previous example.

   <!DOCTYPE html>
<html lang="en">
<head>
    <title></title>

    <script type="text/javascript" src="..\tccore\jquery.min.js"></script>

 <script type="text/javascript">

    var cellWidth = 30;
    var cellHeight = 30;
    var context;
    function getCellBoundaries(theCellX, theCellY) {

        //right
        var aOffset = { "offsetX": (cellWidth * -1) / 2, "offsetY": (cellHeight * -1) / 2 };
        var aCell = { "x": theCellX, "y": theCellY };
        var p1 = getScreenCoords(aCell, aOffset);

        //bottom
        aOffset = { "offsetX": (cellWidth) / 2, "offsetY": (cellHeight * -1) / 2 };
        var p2 = getScreenCoords(aCell, aOffset);

        //left
        aOffset = { "offsetX": (cellWidth) / 2, "offsetY": (cellHeight) / 2 };
        var p3 = getScreenCoords(aCell, aOffset);

        //top
        aOffset = { "offsetX": (cellWidth * -1) / 2, "offsetY": (cellHeight) / 2 };
        var p4 = getScreenCoords(aCell, aOffset);

        return { "point1": p1, "point2": p2, "point3": p3, "point4": p4 };
    }
    function getScreenCoords(Cell, offset) {


        var posX = Cell.x * cellWidth + offset.offsetX;
        var posZ = Cell.y * cellHeight - offset.offsetY;

        var xCart = (posX - posZ)
        var yCart = (posX + posZ) / 2;


        var rX = -xCart + 400;
        var rY = +yCart + 300;

        return { "x": Math.floor(rX), "y": Math.floor(rY) };


    }


    function drawIsoCellBorders (cellX, cellY) {

        var cellPoints = getCellBoundaries(cellX, cellY);
        context.beginPath();
        context.strokeStyle = "red";
        context.moveTo(cellPoints.point1.x, cellPoints.point1.y);   
        context.lineTo(cellPoints.point2.x, cellPoints.point2.y);   
        context.lineTo(cellPoints.point3.x, cellPoints.point3.y);   
        context.lineTo(cellPoints.point4.x, cellPoints.point4.y);
        context.lineTo(cellPoints.point1.x, cellPoints.point1.y);

        console.log("Translated:");
        console.log("Bottom: " + cellPoints.point2.x+","+ cellPoints.point2.y);         
        console.log("Right:" + cellPoints.point1.x +","+ cellPoints.point1.y);  
        console.log("Top:" + cellPoints.point4.x+","+ cellPoints.point4.y);
        console.log("Left: " + cellPoints.point3.x+","+ cellPoints.point3.y);   


        context.stroke();
        context.closePath();

    }

    $(document).ready(function () {

        context = $("#gamescreen")[0].getContext('2d');

        /*let's saw we want to draw an isometric square in the center of the page. 
        Before we do that though, let;s draw a normal square in the center of the page 
        so you know where some of these wild assumptions come from. To do that, we need to know 
        where the square should go. We know our canvas is 800 wide and our cells are 30 wide so
        we can assume that our canvas can fit around 800/30 cells.*/
        var cells_wide = 800/cellWidth;
        var cells_high = 600/cellHeight;
        //half are our centers positions for 2d
        var xCenter = cells_wide/2 * cellWidth;
        var yCenter = cells_high/2 * cellWidth;

        var centerCellNumberX = cells_wide/2;
        var centerCellNumberY = cells_high/2;




        //to draw a square in the center of the canvas, we just draw the points.
        context.beginPath();
        context.strokeStyle = "blue";
        context.moveTo(xCenter-cellWidth/2,yCenter-cellHeight/2); //move to top left corner
        context.lineTo(xCenter+cellWidth/2, yCenter-cellHeight/2); //move to top right corner
        context.lineTo(xCenter+cellWidth/2, yCenter+cellHeight/2); //move to bottom right corner
        context.lineTo(xCenter-cellWidth/2, yCenter+cellHeight/2); //move to bottom left corner
        context.lineTo(xCenter-cellWidth/2,yCenter-cellHeight/2); //move back to top left corner
        context.stroke(); //draw and yay - we've got a little gray square in the center of our canvas
        context.closePath();

        //now, give this an isometric perspective is pretty easy, we essentially tilt the square.
        //a sort of simplified way to do this is as follows - 
        //It means that we essentially draw a diamond instead of a square, and then halve the height
        //and double the width.
        //the first point, (the top of the diamond) is 1/2 the width of the square from where the top left
        //corner of a normal square would be, to the right
        //to Draw: first clear our other rect
        //context.clearRect(0, 0, 800, 600);
        context.beginPath();
        context.strokeStyle = "gray";
        console.log("Hacked:");
        context.moveTo(xCenter, yCenter+cellHeight/2); //bottom of diamond
        console.log("Bottom: " + xCenter + "," + (yCenter+cellHeight/2)); //bottom of diamond

        context.lineTo(xCenter+cellWidth, yCenter); //right of diamond
        console.log("Right:" + (xCenter+cellWidth) + "," + yCenter); //right of diamond

        context.lineTo(xCenter,yCenter-cellHeight/2); //top of diamond
        console.log("Top:" + xCenter + "," + (yCenter-cellHeight/2)); //top of diamond

        context.lineTo(xCenter-cellWidth, yCenter); //left of diamond
        console.log("Left: " + (xCenter-cellWidth) + "," +yCenter); //left of diamond

        context.lineTo(xCenter, yCenter+cellHeight/2); //back to bottom of diamond
        context.stroke(); //boom, we have an isotile and isopositioning based on some 2s cell-grid coord 
        context.closePath();

        //now let's draw the same exact iso polygon but use our transform   
        drawIsoCellBorders(0,0);

    });
</script>
</head>
<body>
    <canvas id="gamescreen" width="800" height="600" style="border-style: dotted; float: left;">

    </canvas>
</body>
</html>

An explanation of how this code works can be found here (I just found this today): http://www.kirupa.com/developer/actionscript/isometric_transforms.htm. This explains a bit about a coordinate system similar to what we used. Unfortunately, it doesn't really explain the math, which I will do shortly (need to run).

In addition as mentioned in the previous post, some of this code is taken from http://github.com/j03m/trafficcone which is an isometric game engine for html5 that I wrote. To see how this code is used to create a full world, see Engine.js and GameWorldModelIso.js. Engine.js shows how a full grid can be drawn (drawMock3dGrid) and GameWorldModelIso.js shows how to build on the basic forumla used above to help you navigate the gameworld, place things in cells etc without having to think about the angles and such.

share|improve this answer
    
It would be better if you pointed out specific examples (files/functions/etc) instead of linking to your whole project. –  Tetrad Jan 20 '12 at 20:11
    
understood, revised. –  j03m Jan 20 '12 at 20:34
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.