Problem mit Spritebewegung
af2111
- css
- javascript
0 Rolf B
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?
Hallo af2111,
da ist einiges im Argen.
Eine Münze dürfte ein Kreis sein, hat also einen Mittelpunkt und einen Radius rM. Wenn dein Sprite ein Quadrat ist, kannst Du ihm einen Kreis einbeschreiben, der ebenfalls Mittelpunkt und Radius rS hat. Für einen Kollisionstest berechnest Du den Abstand der Mittelpunkte (siehe Pythagoras). Ist der kleiner als rM+rS, 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
Hi,
Eine Münze dürfte ein Kreis sein, hat also einen Mittelpunkt und einen Radius rM. Wenn dein Sprite ein Quadrat ist, kannst Du ihm einen Kreis einbeschreiben, der ebenfalls Mittelpunkt und Radius rS hat. Für einen Kollisionstest berechnest Du den Abstand der Mittelpunkte (siehe Pythagoras). Ist der kleiner als rM+rS, 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
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 (xK,yK) der Mittelpunkt eines Kreises mit Radius r.
Sei Q ein Quadrat mit diagonal gegenüberliegenden Eckpunkten (x1,y1),(x2,y2) und durch passende Sortierung sei x1<=x2 und y1<=y2. Dann muss man folgende Fälle unterscheiden:
xK+r<x1∨xK−r>x2 ⇒ Keine Kollision (Kreis weit genug links/rechts)
yK+r<y1∨yK−r>y2 ⇒ Keine Kollision (Kreis weit genug drüber/drunter)
xK≤x1∧yK≤y1∧||(xK,yK),(x1,y1)||>r ⇒ Keine Kollision (Abstand zur linken obere Ecke)
xK≥x2∧yK≤y1∧||(xK,yK),(x2,y1)||>r ⇒ Keine Kollision (Abstand zur rechten obere Ecke)
xK≤x1∧yK≥y2∧||(xK,yK),(x1,y2)||>r ⇒ Keine Kollision (Abstand zur linken unteren Ecke)
xK≥x2∧yK≥y2∧||(xK,yK),(x2,y2)||>r ⇒ Keine Kollision (Abstand zur rechten unteren Ecke)
SONST: Kollision.
Natürlich würde man für den Abstand nicht √(xK−x1)2+(yK−y1)2>r testen, sondern (xK−x1)2+(yK−y1)2>r2, 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
@@Rolf B
Natürlich würde man für den Abstand nicht √(xK−x1)2+(yK−y1)2>r testen, sondern (xK−x1)2+(yK−y1)2>r2, das geht schneller und man muss r² nur einmal berechnen.
TIL (naja, vor ein paar Tagen): Math.hypot()
LLAP 🖖
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