Autor Wątek: Arkanoid - dziwne zachowanie kolizji  (Przeczytany 2524 razy)

Offline Arthes

  • Użytkownik
    • Gromaniak

# Lipiec 30, 2014, 23:03:26
Cześć.
Jestem w trakcie pisania prostego arkanoida, i natrafiłem na dość specyficzny problem.
Kolizje. Zaobserwowałem bardzo dziwne zachowanie piłki. Niby odbija się normalnie od bloczków, ale czasami potrafi się w nie "wbić", przelecieć przez nie (zwłaszcza przez miejsce gdzie jest luka w środku), i takie tam. Można to zaobserwować grając chwilkę w aktualną wersję:
https://dl.dropboxusercontent.com/u/40352279/arkanoid/index.html

Zamieszczam kilka kawałków kodu, być może one coś więcej powiedzą.
funkcja na wykrywanie kolizji prostokąt-prostokąt:
function rectCollision(object1, object2) {
if (object1.x < object2.x + object2.width  && object1.x + object1.width  > object2.x &&
object1.y < object2.y + object2.height && object1.y + object1.height > object2.y) {
return true;
}
return false;
}

Samo wykrywanie w grze:
for (var i in current_level.blocks) {
for (var j in balls) {
if (balls[j].rectCollision(current_level.blocks[i])) {

current_level.blocks[i].hitPoints -= 1;
balls[j].vy *= -1;

}
}
}

Główna pętla gry:
var now = 0,
dt = 0,
last = (new Date).getTime(),
step = 1/60.0;

function run() {
fpsmeter.tickStart();
now = (new Date).getTime();
dt = dt + Math.min(1, (now - last) / 1000);

while (dt > step) {
dt = dt - step;
update(step);
draw(ctx);
}

last = now;

requestAnimationFrame(run);
fpsmeter.tick();
}

run();

Co tu może być nie tak? To chyba nie wina prędkości piłki, bo na ponad dwukrotnie mniejszej można zaobserwować identyczne zjawisko... :(

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Xirdus

  • Redaktor

# Lipiec 30, 2014, 23:25:15
Spróbuj zamienić w rectCollision < na <=.

Offline Arthes

  • Użytkownik
    • Gromaniak

# Lipiec 30, 2014, 23:31:39
Niestety - dalej to samo.

Offline Oti

  • Użytkownik

  • +1
# Lipiec 30, 2014, 23:55:30
Sam miałem niedawno podobny problem-powód jest prosty.

Kuleczka może wpaść za głęboko do środka klocka(bądź zupełnie przeskoczyć kolizję, np. na rogu klocka). Jak wpadnie za głęboko, samo odwrócenie kierunku prędkości nic tu nie da. Trzeba zlikwidować kolizję-tak przesunąć kuleczkę, by nie była już w środku klocka.

Byle jakie rozwiązanie:
Zakładając, że normalny ruch kulki to coś w stylu:
balls[i].x+=balls[i].vx*dt;
balls[i].y+=balls[i].vy*dt;

to przy wykryciu kolizji 'cofasz' ją o ten jeden krok, czyli:
balls[i].x-=balls[i].vx*dt;
balls[i].y-=balls[i].vy*dt;
(oczywiście jeszcze przed odwróceniem prędkości)

Powinno działać lepiej, aczkolwiek nie jest to idealne rozwiązanie. Niestety nie mam teraz czasu się rozpisywać. :)

Offline Xirdus

  • Redaktor

  • +2
# Lipiec 31, 2014, 00:09:53
Tak patrzę i patrzę, i mnie olśniło.

Problem występuje tylko i wyłącznie wtedy, gdy piłka wpadnie na klocka od boku - a to dlatego, że nie sprawdzasz z której strony kolizja nastąpiła. Musisz odwracać odpowiednią składową prędkości zależnie, czy kolizja jest pionowo (wtedy odwracasz y), czy poziomo (wtedy odwracasz x) - a ty robisz zawsze y.

To co mówi Oti generalnie jest dobre, ale w tym konkretnym przypadku nie pomaga, a wręcz zmniejsza precyzję ruchu piłki.

Offline Arthes

  • Użytkownik
    • Gromaniak

# Lipiec 31, 2014, 02:04:55
Dzięki, to był dobry trop.
Jednakże dalej mi coś tu nie gra.
https://dl.dropboxusercontent.com/u/40352279/arkanoid/index.html

W losowych momentach po prostu piłka wbija się w klocki. Można to zaobserwować po kilku chwilach gry.
Sprawdzanie kierunku, z jakiego nastąpiła kolizja zrobiłem w bardzo prymitywny sposób - po prostu dodałem cztery bounding-boxy - jeden z lewej, z góry, z prawej, i dolnej strony. No i po sprawdzeniu kolizji piłki z klockiem sprawdzam, który z tych bounding-boxów złapał kolizję. Jest to dobry sposób, czy można to zrobić lepiej?

if (ball.rectCollision(block)) {
if (ball.top_bb.rectCollision(block))
ball.vy *= -1;
if (ball.bottom_bb.rectCollision(block))
ball.vy *= -1;
if (ball.right_bb.rectCollision(block)) {
ball.vx *= -1;
}
if (ball.left_bb.rectCollision(block)) {
ball.vx *= -1;
}
}

Aa, no i co klatkę dla piłki liczone są te nowe bounding boxy:
//przypisanie pomocniczych bounding-boxów
this.left_bb = new Rect(this.x - this.width, this.y, this.width, this.height);
this.top_bb = new Rect(this.x, this.y - this.height, this.width, this.height);
this.right_bb = new Rect(this.x + this.width, this.y, this.width, this.height);
this.bottom_bb = new Rect(this.x, this.y + this.height, this.width, this.height);

Kurcze, nawet z prostym Arkanoidem jest trochę zachodu :P

Offline Oti

  • Użytkownik

  • +1
# Lipiec 31, 2014, 06:51:11
Cytuj
Jest to dobry sposób, czy można to zrobić lepiej?
Jest to IMO kiepski pomysł-piłeczka lecąca w kant klocka(zależnie od tego jak te 4 recty tam rozmieściłeś) albo trafi w 2 recty naraz(czyli odbije się o 180 stopni), albo trafi nie w ten rect co trzeba i odbije się nie ta składowa prędkości-w efekcie kulka może wlecieć do środka.


Jeśli zostajesz przy kolizjach z prostokątem, to proponuję wrócić dla jednego recta na klocek i rozbić ruch kulki na dwa niezależne przesunięcia, tzn:

-przesuwasz wzdłuż osi X
-sprawdzasz kolizję
-przesuwasz wzdłuż osi Y
-sprawdzasz kolizję

Jeśli po ruchu wzdłuż osi X jest kolizja, to oczywiście pozbywasz się kolizji-przesuwasz kulkę w bezpieczne miejsce.
Jeśli v.x>0, to znaczy uderzyłeś w klocek od lewej strony, to
srodekKulki.x=lewyBokProstokata.x-promienKulki //ewentualnie -(promienKulki+0.1), tak dla pewności

analogicznie dla v.x<0. I wtedy odwracasz v.x.

Potem dla osi Y robisz dokładnie to samo, w ten sposób praktycznie nie ma możliwości, żeby występowały jakieś błędy z kolizjami. Chyba, że te recty faktycznie masz tragicznie małe-wtedy możesz zawsze zwiększyć timestep symulacji, ale to ślepa uliczka.


Offline mosowski

  • Użytkownik

# Lipiec 31, 2014, 10:58:45
Spróbuj uprościć problem. Zamiast badać kolizję ruszającej się piłki i wielokąta, "rozszerz" wielokąt o piłkę w każdym jego punkcie, a piłkę uprość do punktu. Wtedy problem staje się prostszy - badasz kolizję odcinka (ruchu piłki między klatkami), a wielokątem o miękkich narożnikach.

Offline Arthes

  • Użytkownik
    • Gromaniak

# Lipiec 31, 2014, 14:59:29
Dzięki wielkie wszystkim, śmiga aż miło :)
https://dl.dropboxusercontent.com/u/40352279/arkanoid/index.html

To teraz mogę skupić się na samym gameplayu. Może w końcu uda się coś oddać na WSOC'a... :)