2 Stimmen

Wie kann eine große Leinwand einen animierten "Sichtbereich" haben?

Der Fragetitel kann ungenau sein. Stell dir im Grunde ein Rennspiel vor, das in Canvas erstellt wurde. Die Strecke nimmt 10.000 x 10.000 Pixel Bildschirmfläche ein. Allerdings ist das Browserfenster nur 500 x 500 Pixel groß. Das Auto sollte zentriert im Browser bleiben und der 'sichtbare' Bereich des 10.000 x 10.000 Canvas wird sich ändern. Ansonsten würde das Auto einfach über den Rand fahren und verschwinden.

Hat diese Technik einen Namen?

Was sind die Grundprinzipien, um dies zu erreichen?

2voto

pimvdb Punkte 146174

Wenn das Auto an derselben Position bleiben soll (relativ zur Position des Canvas), dann sollten Sie das Auto nicht bewegen. Verschieben Sie stattdessen das Hintergrundbild/die Strecke/die Karte auf die andere Seite.

Den Eindruck erwecken, dass das Auto nach rechts fährt, kann entweder durch das Bewegen des Autos nach rechts oder durch das Bewegen der Karte nach links erreicht werden. Die zweite Option scheint das zu sein, was Sie wollen, da sich das Auto nicht bewegen wird, während der sichtbare Bereich (d.h. die Karte) sich ändern wird.

Dies ist eine schnelle Demo von Grund auf: http://jsfiddle.net/vXsqM/.

Es kommt darauf an, die Position der Karte umzukehren:

$("body").on("keydown", function(e) {
    if(e.which === 37) pos.x += speed; // linke Pfeiltaste, also Karte nach rechts bewegen
    if(e.which === 38) pos.y += speed;
    if(e.which === 39) pos.x -= speed;
    if(e.which === 40) pos.y -= speed;

    // Stellen Sie sicher, dass Sie die Karte nicht zu weit bewegen können.
    // Die Klammerfunktion macht folgendes: wenn x < -250, dann return -250
    // wenn x >    0, dann return    0
    // andernfalls ist es erlaubt, also einfach x zurückgeben
    pos.x = clamp(pos.x, -250, 0);
    pos.y = clamp(pos.y, -250, 0);

    draw();
});

Dann können Sie die Karte mit der gespeicherten Position zeichnen:

ctx.drawImage(img, pos.x, pos.y);

Wenn Sie nach einer Möglichkeit suchen, das Auto tatsächlich zu bewegen, wenn die Karte nicht weiter bewegt werden kann (weil Sie das Auto nahe an einer Seite der Karte fahren), dann müssen Sie die Klammerfunktion erweitern und auch verfolgen, wann das Auto bewegt werden sollte und wie weit: http://jsfiddle.net/vXsqM/1/.

                              // für die x-Koordinate:
function clamp2(x, y, a, b) { // x = Auto x, y = Karte x, a = min Karten x, b = max Karten x
    return y > b ? -y : y < a ? a - y : x;
}

Die Positionsbeschränkung wird dann etwas komplexer:

// Berechnen, wie weit das Auto bewegt werden sollte
posCar.x = clamp2(posCar.x, posMap.x, -250, 0);
posCar.y = clamp2(posCar.y, posMap.y, -250, 0);

// Erlauben Sie auch nicht, dass das Auto von der Karte weg bewegt wird
posCar.x = clamp(posCar.x, -100, 100);
posCar.y = clamp(posCar.y, -100, 100);

// Berechnen, wo die Karte gezeichnet werden soll
posMapReal.x = clamp(posMap.x, -250, 0);
posMapReal.y = clamp(posMap.y, -250, 0);

// Verfolgen, wo sich die Karte virtuell befindet, um die Position des Autos zu berechnen
posMap.x = clamp(posMap.x, -250 - 100, 0 + 100);
posMap.y = clamp(posMap.y, -250 - 100, 0 + 100);
// die 100 steht dafür, dass das Auto (Kreis in der Demo) einen Radius von 25 hat und maximal 100 Pixel nach links und rechts bewegt werden kann (dann stößt es an die Seite)

2voto

ellisbben Punkte 6232

Zwei Dinge:

Methoden zur Transformation des Canvas

Zunächst sind die Methoden zur Transformation des Canvas (zusammen mit context.save() und context.restore() sind Ihre Freunde und werden die Mathematik, die benötigt wird, um einen Teil einer großen "Welt" anzuzeigen, erheblich vereinfachen. Wenn Sie diesen Ansatz verwenden, können Sie das gewünschte Verhalten einfach dadurch erzielen, dass Sie den sichtbaren Teil der Welt und die Weltkoordinaten von allem angeben, was Sie zeichnen möchten.

Dies ist nicht der einzige Weg, Dinge zu tun, aber die Transformationsmethoden sind dafür gedacht genau dieses Problem zu lösen. Sie können sie verwenden oder neu erfinden, indem Sie manuell verfolgen, wo Ihr Hintergrund gezeichnet werden sollte usw., usw.

Hier ist ein Beispiel, wie man sie verwendet, angepasst von einem meiner Projekte:

function(outer, inner, ctx, drawFunction) {
  //Zustand speichern, damit wir zu einer sauberen Transformationsmatrix zurückkehren können.
  ctx.save();

  //Beschränken, sodass wir außerhalb des Rechtecks, das von `outer` definiert wird, nicht zeichnen können.
  ctx.beginPath();
  ctx.moveTo(outer.left, outer.top);
  ctx.lineTo(outer.right, outer.top);
  ctx.lineTo(outer.right, outer.bottom);
  ctx.lineTo(outer.left, outer.bottom);
  ctx.closePath();

  //Zeichnen einer Umrandung vor dem Beschränken, damit wir unseren Ausschnitt sehen können
  ctx.stroke();
  ctx.clip();

  //Transformieren des Canvas, sodass das von `inner` definierte Rechteck das
  //von `outer` definierte Rechteck ausfüllt.
  var ratioWidth = (outer.right - outer.left) / (inner.right - inner.left);
  var ratioHeight = (outer.bottom - outer.top) / (inner.bottom - inner.top);
  ctx.translate(outer.left, outer.top);
  ctx.scale(ratioWidth, ratioHeight);
  ctx.translate(-inner.left, -inner.top);

  //hier gehe ich davon aus, dass Ihr Zeichnungscode eine Funktion ist, die den Kontext
  //nimmt und Ihre Welt hinein zeichnet. Aus Leistungsgründen sollten Sie
  //wahrscheinlich `inner` als Argument übergeben; wenn Ihre Zeichenfunktion weiß, was
  //Teil der Welt es zeichnet, kann es Dinge außerhalb dieses Bereichs ignorieren.
  drawFunction(ctx);

  //Zurück zum vorherigen Canvas-Zustand.
  ctx.restore();

};

Wenn Sie clever sind, können Sie dies nutzen, um mehrere Ansichten, Bild-im-Bild usw. verschiedener Größen zu erstellen und auf Dinge herein zu zoomen und heraus zu zoomen.

Leistung

Zweitens, wie ich im Code kommentiert habe, sollten Sie sicherstellen, dass Ihr Zeichnungscode weiß, welcher Teil Ihrer größeren 'Welt' sichtbar sein wird, damit Sie nicht viel Arbeit damit verschwenden, Dinge zu zeichnen, die nicht sichtbar sein werden.

Die Methoden zur Transformation des Canvas sind dafür gedacht genau dieses Problem zu lösen. Nutzen Sie sie!

*Sie werden wahrscheinlich Probleme haben, wenn Ihre Welt so groß ist, dass ihre Koordinaten nicht in einem geeigneten Integer passen. Sie werden dieses Problem ungefähr dann erreichen, wenn Ihre Welt eine Milliarde (10^9) oder eine lange Billion (10^18) Pixel in einer Dimension überschreitet, abhängig davon, ob die Integer 32- oder 64-Bit sind. Wenn Ihre Welt nicht in Pixeln, sondern in 'Welt-Einheiten' gemessen wird, stoßen Sie auf Probleme, wenn die Gesamtgröße Ihrer Welt und die kleinste Merkmalsgröße zu Ungenauigkeiten bei den Fließkommaberechnungen führen. In diesem Fall müssen Sie zusätzliche Arbeit leisten, um die Dinge im Auge zu behalten... aber Sie werden wahrscheinlich trotzdem die Methoden zur Transformation des Canvas verwenden wollen!

1voto

Aleks Punkte 1147

Mein allererstes Spiel war ein Rennspiel, bei dem ich den Hintergrund anstelle des Autos bewegte und obwohl ich jetzt denken möchte, dass ich meine Gründe hatte, es so zu machen... wusste ich es einfach nicht besser.

Es gibt einige Techniken, die du kennen musst, um dies gut umzusetzen.

  1. Gekachelter Hintergrund. Du musst deine Strecke aus kleineren gekachelten Stücken machen. Um beispielsweise eine 10.000 x 10.000 Pixel große Fläche zu zeichnen, benötigst du ein 100MPix Bild, normalerweise wird ein solches Bild eine Tiefe von 32 Bit (4 Bytes) haben, was letztendlich 400 MB im Speicher bedeutet. Komprimierungen wie PNG, JPEG werden dir nicht helfen, da sie dazu gemacht sind, Bilder zu speichern und zu übertragen. Sie können nicht gerendert werden, um eine Leinwand zu übertragen, ohne sie zu dekomprimieren.

  2. Bewege das Auto entlang deiner Strecke. Es gibt nichts Schlimmeres, als den Hintergrund unter dem Auto zu bewegen. Wenn du weitere Funktionen wie KI-Autos zu deinem Spiel hinzufügen musst... dann müssen diese entlang der Karte bewegen und um Autokollisionen zu implementieren, musst du etwas nicht schweres, aber eigenartiges räumliches Transformationen machen.

  3. Füge die Kamera-Entität hinzu. Die Kamera muss eine Position und eine Ansichtsgröße haben (das ist die Größe deiner Leinwand). Die Kamera wird dein Spiel machen oder es brechen. Dies ist die Entität, die dir das Geschwindigkeitsgefühl im Spiel vermitteln wird... Du kannst eine Kamerarüttelung für Kollisionen haben, wenn du Drifts in deinem Spiel hast, kann die Kamera über die gewünschte Position rutschen und sich wieder auf das Auto fokussieren, usw. Natürlich wird das wichtigste sein, das Auto zu verfolgen. Einige einfache Vorschläge, die ich dir geben kann, sind, das Auto nicht genau in die Mitte der Kamera zu setzen, sondern etwas dahinter, sodass du ein wenig mehr sehen kannst, was vor dir liegt. Je schneller sich das Auto bewegt, desto mehr sollte die Kamera versetzt werden. Außerdem kannst du nicht einfach die Position der Kamera berechnen, sondern die gewünschte Position berechnen und langsam pro Frame die aktuelle Kameraposition zur gewünschten Position bewegen.

  4. Und wenn du eine Kamera und eine große gekachelte Karte hast, musst du bei dem Zeichnen der Kacheln die Kameraposition abziehen. Du kannst auch berechnen, welche Kacheln nicht sichtbar sind und sie überspringen. Diese Technik ermöglicht es dir, dein Spiel mit noch größeren Karten zu erweitern, oder du kannst deine Karte streamen, wo du nicht alle Kacheln geladen hast und im Voraus im Hintergrund (AJAX) lädst, was bald sichtbar sein wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X