Gibt es eine einfache Möglichkeit, eine echte isometrische Projektion mit einer HTML5-Zeichenfläche durchzuführen?

7

Gibt es eine einfache Möglichkeit, mit dem HTML5-Canvas-Element eine echte isometrische Projektion zu erhalten?

gma
quelle
Suchen Sie nach Bibliotheken oder Anleitungen zum Rendern mit dem HTML5-Canvas?
FxIII
Hier ist ein erstaunliches Tutorial für zukünftige Leser: youtube.com/watch?v=go1qrWFw_bs Es erklärt die Mathematik hinter der isometrischen Projektion und berührt sowohl 2D als auch 3D.
DUUUDE123

Antworten:

13

Eine der Eigenschaften einer isometrischen Projektion (und aller axonometrischen Projektionen) ist, dass Objekte in der Ferne nicht kleiner werden.

Daher unterstützt HTML5 trivialerweise isometrische Projektionen, da Sie einfach alle Ihre Kunstobjekte auf diese Weise zeichnen und überlagern können, um die Tiefe zu erzeugen.


quelle
1

Bearbeiten: Die Antwort wurde überarbeitet, damit sie klarer ist.

Hier ist ein kurzer Hinweis, wie Sie etwas Einfaches wie ein Quadrat so ändern können, dass es isometrisch ist. Was ich hier mache, sind 3 Zeichenroutinen. Zuerst zeichne ich ein normales Quadrat, dessen zentraler Ursprung die Mitte der Leinwand ist (aber Ihr Ursprung könnte überall sein). Dann mache ich einige grundlegende Berechnungen, um daraus ein isometrisches Polygon zu machen. Dies ist nicht der "richtige" Weg, dies zu tun, aber es sollte Ihnen helfen zu verstehen, was die Mathematik in der nächsten Zeichenroutine tut. Anschließend zeichne ich das Polygon mithilfe einer isometrischen Transformation neu. Dies ist eine Art Abkürzung für das, was wir im vorherigen Beispiel manuell getan haben.

   <!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>

Eine Erklärung, wie dieser Code funktioniert, finden Sie hier (ich habe ihn heute gerade gefunden): http://www.kirupa.com/developer/actionscript/isometric_transforms.htm . Dies erklärt ein wenig über ein Koordinatensystem ähnlich dem, was wir verwendet haben. Leider erklärt es nicht wirklich die Mathematik, was ich in Kürze tun werde (muss laufen).

Wie im vorherigen Beitrag erwähnt, stammt ein Teil dieses Codes von http://github.com/j03m/trafficcone , einer isometrischen Spiel-Engine für HTML5, die ich geschrieben habe. Informationen dazu, wie dieser Code zum Erstellen einer vollständigen Welt verwendet wird, finden Sie unter Engine.js und GameWorldModelIso.js. Engine.js zeigt, wie ein vollständiges Raster gezeichnet werden kann (drawMock3dGrid), und GameWorldModelIso.js zeigt, wie Sie auf den oben verwendeten grundlegenden Foren aufbauen, um in der Spielwelt zu navigieren, Dinge in Zellen zu platzieren usw., ohne über die Winkel und dergleichen nachdenken zu müssen.

j03m
quelle
Es wäre besser, wenn Sie auf bestimmte Beispiele (Dateien / Funktionen / usw.) hinweisen würden, anstatt auf Ihr gesamtes Projekt zu verlinken.
Tetrad
verstanden, überarbeitet.
j03m