Sonntag, 31. März 2013

Html5 Canvas - Element um Mittelpunkt rotieren

Vielleicht habt ihr schon mal die Funktion "rotate" im 2d Kontext des Canvas Elements bemerkt.
Bei erster Verwendung scheint das Ergebnis ziemlich seltsam zu sein:


<!DOCTYPE html>

<head>
    
</head>
<body>
    
    <canvas id ="Game", width="300", height="200"></canvas>

    <script>

        var canvas = document.getElementById("Game");
        var ctx = canvas.getContext("2d");

        setInterval(update, 1000 / 60);

        function update() {
            ctx.fillStyle = "#000000";
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            ctx.rotate(0.1);

            ctx.fillStyle = "#ff0000";
            ctx.fillRect(40, 40, 20, 20);
        }

    </script>

</body>
</html>

Dies liegt daran, dass die Funktion "rotate" immer um den Ursprung (0,0) rotiert.
Wir müssen folglich alles so verschieben, dass die Mitte des Rechtecks genau oben links ist.
Danach führen wir die Rotation aus und verschieben alles zurück. Jetzt wird das Rechteck gezeichnet.
Nun müssen wir alles nochmal machen, mit negativer Rotation, um alles wieder zurückzusetzen.
Ich würde folgendes Schema empfehlen:


<!DOCTYPE html>

<head>
    
</head>
<body>
    
    <canvas id ="Game", width="300", height="200"></canvas>

    <script>

        var canvas = document.getElementById("Game");
        var ctx = canvas.getContext("2d");

        var rot = 0;

        var x = 40;
        var y = 40;
        var w = 20;
        var h = 20;


        setInterval(update, 1000 / 60);

        function update() {


            ctx.fillStyle = "#000000";
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            rot += 0.01;

            beginDraw(rot);
            ctx.fillStyle = "#00ff00";
            ctx.fillRect(x, y, w, h);
            endDraw(rot);
           
        }

        function beginDraw(r) {
            ctx.translate(x + w / 2, y + h / 2);
            ctx.rotate(r);
            ctx.translate(-(x + w / 2), -(y + h / 2));
        }

        function endDraw(r) {
            ctx.translate(x + w / 2, y + h / 2);
            ctx.rotate(-r);
            ctx.translate(-(x + w / 2), -(y + h / 2));
        }

    </script>

</body>
</html>






Javascript Rechteck Kollision für Html5 Canvas

Durch das neue Canvas Element, das in Html5 vorhanden ist, stellt die Programmiersprache Javascript im Bereich Spieleprogrammierung Adobes Technologie "Flash" eine berechtigte Konkurrenz dar. Doch leider ist Javascript an sich für Spiele nicht ausgelegt und liefert deshalb zum Beispiel keine Standard Objekte für Kollisionen mit.

Anstatt uns von irgendwelchen JS-Libraries zu bedienen, erstellen wir für die Kollision von Rechtecken, was die wichtigste Grundform für 2D Spiele ist, uns einfach selbst eine js - Datei, die diese Kollisionen berechnet.


<!DOCTYPE html>

<head>
    
</head>
<body>
    
    <canvas id ="Game", width="800", height="600"></canvas>

    <script>

        var canvas = document.getElementById("Game");
        var ctx = canvas.getContext("2d");

        setInterval(update, 1000 / 60);

        function update() {
            ctx.fillStyle = "#000000";
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }

    </script> 

</body>
</html>

Ok, dieser Code sollte euch bereits geläufig sein:
Er holt sich als erstes Zugriff auf das Canvas Element und dann auf den 2d Kontext, mit dem wir Objekte,
wie Rechtecke zeichnen können. Danach wird die Funktion update mit einer Framerate von 60 fps aufgerufen. Bei jedem neuen Frame wird der Bildschirm schwarz eingefärbt.

Als nächstes erstellen wir in einem externen Javascript Dokument ein Objekt, das ein Rechteck repräsentiert.
(Ich nenne die Datei Rectangle.js)



function Rectangle(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
}



Typischerweise wird ein Rechteck durch die Ecke oben links angegeben (x, y) und durch eine Höhe und Breite. Ich persönlich erstelle mir drei Funktionen:


  • intersects: nimmt ein zweites Rechteck als Parameter und gibt als Boolean zurück, ob eine Kollision stattgefunden hat
  • containsPoint: nimmt einen Punkt (angegeben durch x und y) und überprüft, ob dieser Punkt sich im Rechteck befindet (Beispiel: Ist die Maus gerade über einen Button)
  • draw: nimmt als Parameter eine Farbe an und zeichnet das Rechteck entsprechend mithilfe des 2d-Kontext

Die ganze Klasse sieht dann folgendermaßen aus:


function Rectangle(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w= w;
    this.h= h;

    this.containsPoint = function (px, py) {
        return (px > this.x && px < (this.x + this.w)
        && py > this.y && py < (this.y + this.h));
    };

    this.intersects = function (r) {
        return ((r.x + r.w) > this.x && (r.x < (this.x + this.w))                       && ((r.y + r.h) > this.y) && (r.y < (this.y + this.h)));
    };

    this.draw = function (c) {
        ctx.fillStyle = c;
        ctx.fillRect(this.x, this.y, this.w, this.h);
    };
}


Die Funktionen sind eigentlich selbsterklärend, da sie nur mit den < und > Operatoren arbeiten. Durch Überlegen und ein Blatt Papier kann man sich die Funktionsweise noch klar machen.

Zum Schluss werde ich noch die Einbindung und Anwendung zeigen.


<!DOCTYPE html>

<head>
    
</head>
<body>
    
    <canvas id ="Game", width="800", height="600"></canvas>

    <script src="Rectangle.js"></script>

    <script>

        var canvas = document.getElementById("Game");
        var ctx = canvas.getContext("2d");

        var rechteck1 = new Rectangle(0, 0, 20, 20);
        var rechteck2 = new Rectangle(40, 0, 20, 20);

        setInterval(update, 1000 / 60);

        function update() {
            ctx.fillStyle = "#000000";
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            rechteck1.x++;

            rechteck1.draw("#00ff00");
            rechteck2.draw("#0000ff");

            if (rechteck1.intersects(rechteck2)) {
                ctx.fillStyle = "#ff0000";
                ctx.fillText("Intersection", 400, 400);
            }
        }

    </script> 

</body>
</html>


Ok, ich hoffe ich konnte euch weiter helfen!
Das nächste mal mache ich mit Kreis-Kollision weiter ;D