af2111: Problem mit Spritebewegung

Hallo,

ich möchte für ein Spiel eine Figur Münzen einsammeln lassen. Die Figur lässt sich mit den Pfeiltasten steuern, und genau bei der Steuerung liegt das Problem. Ich kriege es einfach nicht hin, die Figur so zu bewegen, dass die dynamisch erstellte Position der Münzen noch gespeichert wird. Leider gibt mir auch die Console keine Fehlermeldung. Hier ist der Code:

HTML:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Sammle die Münzen</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="SpielMitMünzen.css" />

    <script src="Jquery/jquery-3.3.1.js"></script>
</head>

<body>
    <div class="playground">
        <img class="sprite" src="Player.png">


    </div>
    </body>

</html>

JS:


            let coins = [];
            let sprite = $(".sprite");
            let spritePosX = sprite.css("left");
            let spritePosY = sprite.css("top");
            let playground = $(".playground");



            let i = 0;
            while (i <= 20) {
                let img = $("<img class=\"coinimg\" src=\"Coin.png\">");

                coins.push[img];
                img.attr("id", "coin" + i);



                let min = 0;
                let max = 300;
                let positionCoinX = (Math.random() * (max - min) + min) + "px";
                let MinY = 0;
                let MaxY = 200;
                let positionCoinY = (Math.random() * (MaxY - MinY) + MinY) + "px";

                img.css({
                    left: positionCoinX,
                    top: positionCoinY
                });



                playground.prepend(img);
                i++;


                function coordinates() {
                    return [positionCoinX, positionCoinY, spritePosX, spritePosY];
                }


            }




            let coords = coordinates();
            console.log(coords);

            $(document).keydown(function (event) {

                switch (event.key) {
                    case "ArrowUp":

                        coords[3] -= 10;
                        sprite.animate({
                            top: coords[3]

                        }, 25);

                        if (coords[0] && coords[1] === coords[2] && coords[3]) {
                            alert("berührt");
                        }
                        break;
                    case "ArrowLeft":


                        coords[2] -= 10;
                        sprite.animate({
                            left: coords[2]
                        }, 25);

                        if (coords[0] && coords[1] === coords[2] && coords[3]) {
                            alert("berührt");
                        }
                        break;

                    case "ArrowRight":

                        coords[2] = parseInt(coords[2]);
                        coords[2] += 10;
                        coords[2] + "px";
                        sprite.animate({
                            left: coords[2]
                        }, 25);

                        if (coords[0] && coords[1] === coords[2] && coords[3]) {
                            alert("berührt");
                        }
                        break;
                    case "ArrowDown":
                        coords[3] = parseInt(coords[3]);
                        console.log(coords[3]);
                        coords[3] += 10;


                        sprite.animate({
                            top: coords[3]
                        }, 25);

                        if (coords[0] && coords[1] === coords[2] && coords[4]) {
                            alert("berührt");
                        }
                        break;


                }
            });
        });

Hat jemand eine Idee, wie die Steuerung funktionieren köntte?

  1. Hallo af2111,

    da ist einiges im Argen.

    • wenn du 20 Münzen hast, musst Du auch die Positionen aller Münzen überprüfen. Deine coordinates() Funktion liefert aber nur die Position der zuletzt generierten Münze. Du hast dein coins Array. Das musst Du nach jeder Positionsveränderung des Sprite durchlaufen und prüfen, ob das Sprite eine Münze berührt.
    • Dein coords Array bringt deswegen nicht viel. Du kannst statt coords[3] und coords[4] genauso gut spritePosX und spritePosY updaten und das zum Vergleich verwenden.
    • Die Frage, ob das Sprite die Münze berührt, beantwortet sich nicht durch den Vergleich der Koordinaten einer Ecke. Vor allem deshalb nicht, weil dein Sprite in Zehnpixel-Schritten springt, deine Münzen aber pixelgenau verteilt werden. Das Sprite hat eine Fläche, eine Münze hat eine Fläche. Je nach Form von Münze und Sprite ist ein echter Berührtest relativ aufwändig. Die komplett richtige Lösung für beliebige Formen besteht in einem Pixel-für-Pixel Vergleich, ob sich an einer Koordinate die Pixel von Münze und Sprite überschneiden. In den meisten Fällen kann man vereinfachen.

    Eine Münze dürfte ein Kreis sein, hat also einen Mittelpunkt und einen Radius $$r_M$$. Wenn dein Sprite ein Quadrat ist, kannst Du ihm einen Kreis einbeschreiben, der ebenfalls Mittelpunkt und Radius $$r_S$$ hat. Für einen Kollisionstest berechnest Du den Abstand der Mittelpunkte (siehe Pythagoras). Ist der kleiner als $$r_M+r_S$$, liegt eine Kollision vor bzw. das Sprite hat die Münze berührt. Wenn Sprite und Münze gleich groß sind, kannst Du Dir die Mittelpunktberechnung sogar schenken und einfach die Abstände von (spriteX,spriteY) und (coinX,coinY) - für alle Coins - berechnen.

    Ist dein Sprite kein Quadrat, oder ist seine Form einem Kreis sehr unähnlich, musst Du entweder Ungenauigkeiten bei der Kollisionsberechnung hinnehmen oder Dir einen Algorithmus überlegen, wie Du eine Überlappung von Sprite und Münze berechnen kannst.

    Rolf

    --
    sumpsi - posui - clusi
    1. Hi,

      Eine Münze dürfte ein Kreis sein, hat also einen Mittelpunkt und einen Radius $$r_M$$. Wenn dein Sprite ein Quadrat ist, kannst Du ihm einen Kreis einbeschreiben, der ebenfalls Mittelpunkt und Radius $$r_S$$ hat. Für einen Kollisionstest berechnest Du den Abstand der Mittelpunkte (siehe Pythagoras). Ist der kleiner als $$r_M+r_S$$, liegt eine Kollision vor bzw. das Sprite hat die Münze berührt.

      Die Ecke des Quadrats ist vom Mittelpunkt aber weiter entfernt als der Radius des einbeschriebenen Kreises. Die Ecke kollidiert mit dem Kreis deutlich früher - wenn der Kreismittelpunkt auf der Verlängerung der Diagonale des Quadrats liegt etwa 1,4 mal früher ( sqrt(2) ).

      cu,
      Andreas a/k/a MudGuard

      1. Hallo Andreas,

        die Reduktion des Sprites auf einen Kreis ist natürlich eine vereinfachende Approximation, die die Programmierung erleichtert und die Laufzeit reduziert.

        Die Prüfung, ob ein Kreis ein Quadrat berührt, muss anders programmiert werden. Aus der Hüfte geschossen würde ich sagen, das geht so:

        Sei $$(x_K,y_K)$$ der Mittelpunkt eines Kreises mit Radius r.
        Sei Q ein Quadrat mit diagonal gegenüberliegenden Eckpunkten $$(x_1,y_1), (x_2, y_2)$$ und durch passende Sortierung sei $$x_1<=x_2$$ und $$y1<=y2$$. Dann muss man folgende Fälle unterscheiden:

        $$x_K+r \lt x_1 \vee x_K-r \gt x_2$$ ⇒ Keine Kollision (Kreis weit genug links/rechts)
        $$y_K+r \lt y_1 \vee y_K-r \gt y_2$$ ⇒ Keine Kollision (Kreis weit genug drüber/drunter) $$x_K \le x_1 \land y_K \le y_1 \land ||(x_K,y_K),(x_1,y_1)|| > r$$ ⇒ Keine Kollision (Abstand zur linken obere Ecke)
        $$x_K \ge x_2 \land y_K \le y_1 \land ||(x_K,y_K),(x_2,y_1)|| > r$$ ⇒ Keine Kollision (Abstand zur rechten obere Ecke)
        $$x_K \le x_1 \land y_K \ge y_2 \land ||(x_K,y_K),(x_1,y_2)|| > r$$ ⇒ Keine Kollision (Abstand zur linken unteren Ecke)
        $$x_K \ge x_2 \land y_K \ge y_2 \land ||(x_K,y_K),(x_2,y_2)|| > r$$ ⇒ Keine Kollision (Abstand zur rechten unteren Ecke)
        SONST: Kollision.

        Natürlich würde man für den Abstand nicht $$\sqrt{(x_K-x_1)^2+(y_K-y_1)^2} > r$$ testen, sondern $$(x_K-x_1)^2+(y_K-y_1)^2 > r^2$$, das geht schneller und man muss r² nur einmal berechnen.

        Also: Viel mühsamer und langsamer als die Kollision zweier Kreise. Ob meine Approximation trägt, hängt von der Form des Sprites ab. Ob man einen Test Kreis vs Quadrat machen muss, ebenfalls. Effiziente Kollisionserkennung ohne Hardwareunterstützung ist eine Wissenschaft für sich.

        Rolf

        --
        sumpsi - posui - clusi
        1. @@Rolf B

          Natürlich würde man für den Abstand nicht $$\sqrt{(x_K-x_1)^2+(y_K-y_1)^2} > r$$ testen, sondern $$(x_K-x_1)^2+(y_K-y_1)^2 > r^2$$, das geht schneller und man muss r² nur einmal berechnen.

          TIL (naja, vor ein paar Tagen): Math.hypot()

          LLAP 🖖

          --
          „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
          1. Hallo Gunnar,

            ich habe mal handgemachten Pythagoras (mit und ohne Wurzelziehen) und Math.hypot gegeneinander gestellt.

            Math.hypot enthält einen Überlaufschutz. Der macht es extrem lahm.

            https://jsfiddle.net/ymp8vdcj/1/ - Ergebnisse in der Console.

            Rolf

            --
            sumpsi - posui - clusi