Jak w Pong obliczyć kierunek piłki, która odbija się od wiosła?


28

Staram się owijać wokół tego bardzo Hello-y problem z tworzeniem gier. Stworzyłem grę w kółko i krzyżyk w XNA, więc sądzę, że następnym krokiem będzie klon Breakout .

Należy pamiętać, że nie mam żadnej wiedzy na temat programowania gier lub nawet to, co matematyka powinienem ubiegać się gdzie. Dlatego zadaję to pytanie.


Na pytanie: jak ustalić, gdzie piłka powinna się odbić, gdy uderza w wiosło u dołu ekranu?

Wyobrażam sobie, że byłoby to coś takiego:

  1. Uchwyć prędkość i kąt z nadchodzącej piłki.
  2. Wykryj, gdzie dotknął paska (daleko w lewo, daleko w prawo, środek) i zgodnie z tym nadaj mu większą prędkość, jeśli dotknie zewnętrznych obszarów.
  3. Tu utknąłem. Hehe

Jakieś spostrzeżenia? Zdaję sobie sprawę, że nie jest to proste pytanie typu odpowiedź, ale jestem pewien, że w pewnym momencie wszyscy mają do czynienia.

Czytam książkę Algebra liniowa, która była zalecana na tej stronie, ale nadal nie mam pojęcia, czy powinnam ją tutaj zastosować.


Napisz ponga przed wybiciem, możesz następnie wyeksportować klasy piłki, ściany i wiosła i rozszerzyć je tak, aby działały z różnymi rodzajami cegieł i ulepszeń. Ponadto uważam, że pong jest prostszy niż wybicie.
Incognito,

Odpowiedzi:


30

Oto odpowiednia logika, której użyłem w pongu na mojej stronie głównej : (proszę, zagraj w nią przed przeczytaniem, abyś wiedział, jaki efekt osiągam dzięki poniższemu kodowi)

Zasadniczo, gdy piłka zderza się z łopatką, jej kierunek jest całkowicie ignorowany; otrzymuje nowy kierunek w zależności od tego, jak daleko od środka wiosła się zderzyło. Jeśli piłka uderzy w wiosło w środku, zostanie odesłana dokładnie poziomo; jeśli trafi w krawędź, odlatuje pod ekstremalnym kątem (75 stopni). I zawsze jedzie ze stałą prędkością.

var relativeIntersectY = (paddle1Y+(PADDLEHEIGHT/2)) - intersectY;

Weź środkową wartość Y wiosła i odejmij przecięcie Y piłki. Jeśli wiosło ma 10 pikseli wysokości, liczba ta będzie wynosić od -5 do 5. Nazywam to „przecięciem względnym”, ponieważ znajduje się teraz w „przestrzeni wiosła”, przecięciu piłki względem środka wiosła.

var normalizedRelativeIntersectionY = (relativeIntersectY/(PADDLEHEIGHT/2));
var bounceAngle = normalizedRelativeIntersectionY * MAXBOUNCEANGLE;

Weź względne skrzyżowanie i podziel je przez połowę wysokości łopatki. Teraz nasza liczba od -5 do 5 jest liczbą dziesiętną od -1 do 1; jest znormalizowany . Następnie pomnóż go przez maksymalny kąt, o który chcesz odbić piłkę. Ustawiłem na 5 * Pi / 12 radianów (75 stopni).

ballVx = BALLSPEED*Math.cos(bounceAngle);
ballVy = BALLSPEED*-Math.sin(bounceAngle);

Na koniec obliczyć nowe prędkości kuli za pomocą prostej trygonometrii.

Może to nie do końca efekt, którego szukasz, lub możesz również określić prędkość, mnożąc znormalizowane przecięcie względne przez prędkość maksymalną; sprawiłoby to, że piłka leciałaby szybciej, jeśli uderzyłaby w pobliżu brzegu wiosła, lub wolniej, gdyby uderzyła w pobliżu środka.


Chciałbym prawdopodobnie trochę kodu na temat tego, jak wyglądałby wektor lub w jaki sposób mogę zapisać zmienną wektora, w którym znajdują się kule (prędkość i kierunek).

Wektor zawiera zarówno prędkość, jak i kierunek. Przechowuję mój wektor jako „vx” i „vy”; to znaczy prędkość w kierunku x i prędkość w kierunku y. Jeśli nie odbyłeś wstępnego kursu fizyki, może ci się to wydawać obce.

Powodem, dla którego to robię, jest to, że zmniejsza to konieczne obliczenia dla poszczególnych klatek; każda klatka, którą właśnie robisz x += vx * time;i y += vy * time;gdzie czas jest czasem od ostatniej klatki, w milisekundach (dlatego prędkości są w pikselach na milisekundę).


Jeśli chodzi o wdrożenie zdolności do zakrzywiania piłki:

Przede wszystkim musisz znać prędkość wiosła w momencie uderzenia piłki; co oznacza, że ​​musisz śledzić historię wiosła, abyś mógł poznać jedną lub więcej wcześniejszych pozycji wiosła, abyś mógł porównać je z bieżącą pozycją, aby sprawdzić, czy się poruszył. (zmiana pozycji / zmiana czasu = prędkość; więc potrzebujesz 2 lub więcej pozycji i czasów tych pozycji)

Teraz musisz także śledzić prędkość kątową piłki, która praktycznie reprezentuje krzywą, po której się porusza, ale jest równoważna obrotowi piłki w świecie rzeczywistym. Podobnie do interpolacji kąta odbicia od względnej pozycji piłki podczas zderzenia z łopatką, należy również interpolować tę prędkość kątową (lub obrót) od prędkości łopatki podczas zderzenia. Zamiast po prostu ustawić obrót, jak w przypadku kąta odbicia, możesz chcieć dodać lub odjąć istniejący obrót piłki, ponieważ zwykle działa to dobrze w grach (gracz może zauważyć, że piłka obraca się i powoduje jej obrót jeszcze bardziej dziko lub przeciwdziałaj spinowi, próbując sprawić, by podróż przebiegła prosto).

Zauważ jednak, że chociaż jest to najbardziej zdrowy rozsądek i prawdopodobnie najłatwiejszy sposób na jego wdrożenie, rzeczywista fizyka odbicia nie zależy wyłącznie od prędkości uderzanego obiektu; obiekt bez prędkości kątowej (bez spinu), który uderzy w powierzchnię pod kątem, będzie miał nadany spin. Może to prowadzić do lepszej mechaniki gry, więc możesz przyjrzeć się temu, ale nie jestem pewien fizyki stojącej za tym, więc nie zamierzam tego wyjaśniać.


Właśnie tego zamierzam; większa prędkość, gdy uderza o krawędzie paska. Wielkie dzięki za poświęcenie czasu na napisanie tego. Mam jednak problem ze zrozumieniem kilku rzeczy; na przykład, w pierwszym snajperie czym jest „przecięcie”? Czy „paddle1Y” ma również prawidłową wysokość paska?

przecięcie to pozycja piłki, w której przecina ona wiosło. Wykonuję skomplikowane obliczenia, których szczerze nawet teraz nie rozumiem, ale w gruncie rzeczy jest to wartość Y piłki w momencie jej zderzenia. paddle1Y to wartość Y wiosła z góry ekranu; PADDLEHEIGHT to wysokość wiosła („bar”).
Ricket

Co musisz dodać, aby umożliwić kulki „zakrzywione”? Na przykład, gdy piłka ma uderzyć w wiosło, poruszasz wiosłem, aby zakrzywić piłkę. Coś takiego: en.wikipedia.org/wiki/Curve_ball
Zolomon

Zobacz edycję i daj mi znać, co myślisz (jeśli potrzebujesz więcej informacji o czymś, nie dostaniesz czegoś itp.)
Ricket

Dzięki! Znakomita odpowiedź, zawsze zastanawiałem się, jak osiągnąć ten efekt!
Zolomon,

8

Minęło trochę czasu, odkąd to zrobiłem, ale myślę, że mam rację.

Przy idealnym zderzeniu kąt odbicia jest równy kątowi padania.

Znasz normalną pozycję swojej wiosła (zakładając płaską powierzchnię): N Znasz swoją pierwotną pozycję piłki (na początku pomiaru czasu): P Znasz swoją nową pozycję piłki (na końcu pomiaru czasu): P 'Znasz swój punkt zderzenia: C Zakładając, że obliczyłeś, że segment P -> P' przechodzi przez wiosło, twoja nowa pozycja odbita (P '') byłaby:

P '+ 2 * (N * (P' kropka -N))

Podwyrażenie N * (P 'kropka-N) oblicza głębokość wzdłuż normalnej kolizji, którą przebyła piłka. Znak minus ma poprawić fakt, że sprawdzamy głębokość przeciwną do kierunku normalnego.

P '+ 2 * część podwyrażenia przesuwa piłkę z powrotem ponad płaszczyznę zderzenia o 2-krotność głębokości zderzenia.

Jeśli chcesz mniej niż idealnej kolizji, zmień współczynnik 2 na (1 + (1-k)), gdzie k jest współczynnikiem tarcia. Idealne zderzenie ma wartość ak wynoszącą 0, co powoduje, że kąt odbicia jest dokładnie taki, jak kąt wejściowy. Wartość k równa 1 powoduje zderzenie, w którym kula pozostanie na powierzchni płaszczyzny zderzenia.

Twój nowy wektor prędkości, V '', miałby kierunek P '' - C. Znormalizuj go i pomnóż przez swoją prędkość wejściową, a uzyskana wielkość prędkości będzie taka sama, ale w nowym kierunku. Możesz małpować z tą prędkością, mnożąc ją przez współczynnik l, który albo zwiększy (l> 1), albo zmniejszy (l <1) uzyskaną prędkość.

Podsumowując:

P '' = P '+ (1-k) * (N * (kropka-N)) V' '= l * V * ((P' '- C) / | P' '- C |)

Gdzie k i l to wybrane przez ciebie współczynniki.


5

Odbicie można wykonać „dobrze” lub „łatwo”.

„Właściwy” sposób polega na obliczeniu wektorów prostopadłych do ścian. W 2D jest to dość łatwe i prawdopodobnie można je po prostu na stałe zakodować. Następnie krok odbicia zasadniczo pozostawia nienaruszony „równoległy” komponent ruchu i odwraca „prostopadły” komponent. W sieci są prawdopodobnie szczegółowe informacje na ten temat, może nawet w MathWorld.

„Prostym” sposobem jest po prostu zanegowanie ruchu X lub Y po uderzeniu w ścianę. Jeśli uderzysz w boczne ściany, negujesz X. Jeśli uderzysz w górę, negujesz Y. Jeśli chcesz przyspieszyć piłkę, po prostu zwiększ cokolwiek chcesz; możesz przyspieszyć w bieżącym kierunku, mnożąc prędkości X i Y lub możesz przyspieszyć tylko na jednej osi.


Czy „łatwy” i „właściwy” sposób opisany powyżej nie jest zasadniczo taki sam?
Tom

Są dokładnie takie same, jeśli ściany znajdują się wzdłuż głównych osi. Jeśli ściany nie są wzdłuż osi X, Y i Z, to nie, obie są całkowicie różne.
dash-tom-bang

5

Sam tworzę grę arkanoidalną i myślę, że rozwiązanie, w jaki sposób powinna zachowywać się piłka po uderzeniu w wiosło, jest znacznie prostsze i szybsze niż podejście do grzechu / cos ... działa dobrze dla celów taka gra. Oto co robię:

  • Oczywiście, ponieważ prędkość piłki rośnie w czasie interpoluję kroki przed / po x, y, aby zachować dokładne wykrywanie kolizji, zapętlając wszystkie „stepX” i „stepY”, które są obliczane dzieląc każdy składnik prędkości przez moduł utworzonego wektora przez obecne i przyszłe pozycje piłki.

  • Jeśli dojdzie do kolizji z łopatką, dzielę prędkość Y przez 20. To „20” jest najwygodniejszą wartością, jaką znalazłem, aby uzyskać wynikowy maksymalny kąt, gdy piłka uderza w boki łopatki, ale możesz zmienić ją na cokolwiek Twoje potrzeby są, po prostu graj z niektórymi wartościami i wybierz dla siebie coś lepszego. Dzieląc, powiedzmy, prędkość 5, która jest moją początkową prędkością gry przez tę liczbę (20), otrzymuję „współczynnik odbicia” wynoszący 0,25. Obliczenia te utrzymują moje kąty dość proporcjonalne, gdy prędkość rośnie w czasie do mojej maksymalnej wartości prędkości, która na przykład może wynosić 15 (w tym przypadku: 15/20 = 0,75). Biorąc pod uwagę, że moje wiosła x, y struny są w połowie (xiy przedstawiają środek wiosła), następnie mnożę ten wynik przez różnicę między pozycją piłki a pozycją wiosła. Im większa różnica, greatear wynikowy kąt. Poza tym za pomocą padliny ze środkowym uchwytem otrzymujesz prawidłowy znak przyrostu x w zależności od strony uderzenia piłki, bez obawy o obliczenie środka. W pseudokodzie:

Dla n = 0 do modułu ...

jeśli wykryto kolizję, to speedX = - (speedY / 20) * (paddleX - ballX); speedY = -speedY;
wyjście; koniec jeśli

...

x = x + stepX; y = y + krok Y;

koniec dla

Pamiętaj, zawsze staraj się zachować prostotę. Mam nadzieję, że to pomoże!


4

Wiosło w Breakout, gdy podąża za opisanym stylem, jest zwykle modelowane jako zakrzywiona powierzchnia. Kąt padania zmienia się w zależności od tego, gdzie uderza wiosło. W martwym punkcie linia styczna do łuku jest całkowicie pozioma, a kula odbija zgodnie z oczekiwaniami. W miarę oddalania się od środka styczna do łuku staje się coraz bardziej nachylona, ​​w wyniku czego kula odbija się inaczej.

Kluczową kwestią jest to, że zmienia się kąt odbicia, a nie prędkość piłki. Prędkość piłki ogólnie po prostu powoli rośnie z czasem.


1
Mówisz jako zakrzywiona powierzchnia, co brzmi logicznie w mojej głowie, ale kiedy próbuję myśleć o tym w kategoriach kodu, rzeczy szybko się rozmywają. Jak mogłem nawet zadeklarować to w kodzie jako zmienną czy coś takiego.

Coś w rodzaju angle = 1 - 2 * (ball.x - paddle.left) / paddle.widthda ci liczbę od 1 do -1; to (razy pewna wartość poprawiona dla twojej mechaniki gry) jest nachyleniem linii stycznej w punkcie, w którym uderzyła piłka. Odbij się od tej linii, a nie od linii poziomej.

4

Nolan Bushnell wygłosił przemówienie na SIEGE w miniony weekend i mówił o podobnym problemie z oryginalnym pongiem. Nie musisz wykonywać skomplikowanych obliczeń. Jeśli trafisz w lewą część panelu, wyślij piłkę w lewo. Zrób to samo dla prawej strony.

Na początek możesz ustawić kąt dla lewej i prawej strony 45 stopni. Po zakończeniu gry możesz wrócić i uczynić to bardziej skomplikowanym, ale uczyń to tak prostym, jak to tylko możliwe.


1
Widziałem też tę myśl przewodnią, miał na myśli, że była to decyzja projektowa, a nie matematyczna: „kąt padania = kąt odbicia” byłby poprawny, ale powodowałby słabą rozgrywkę. Dodatkowo, w oryginalnym Pong and Breakout, prędkość była funkcją liczby kolizji piłki / łopatki (więc przyspiesza z czasem). Zmniejszyli również rozmiar wiosła po pewnej liczbie trafień. Unikałbym jednak, aby piłka podniosła się prosto w górę, wtedy możesz zostawić tam wiosło na czas nieokreślony.
Ian Schreiber

4

Breakout to klasyczna praca dla początkujących, która pozwala zanurzyć się w świecie programowania gier opartych na fizyce. Zasadniczo piłka odskakuje, gdy uderza o ścianę. Jak sugerował ktoś powyżej, kąt padania jest równy kątowi odbicia. Ale kiedy weźmiesz pod uwagę piłkę uderzającą w wiosło. Logika podzielona jest na 3 sekcje. 1.) Piłka uderza w środkową część wiosła. 2.) Piłka uderza w lewą część wiosła. 3.) Piłka uderza w prawą pozycję wiosła.

Gdy weźmiesz pod uwagę środkową część: nie musisz różnicować efektu odbicia od tego, który jest stosowany podczas uderzenia piłki. Piłka po prostu odchyla się normalnie, ale gdy uderzy się w którymkolwiek kierunku, sprawa jest inna.

Gdy piłka zostanie uderzona w lewą stronę, tzn. Weź pod uwagę piłkę wychodzącą z lewej strony ekranu i zbliżasz się z łopatką z prawej strony. Następnie, gdy uderzasz piłkę lewą częścią, piłka powinna odbijać się w kierunku, w którym pochodzi z .ie. W lewo, pod tym samym kątem, z którego pochodzi. To samo dotyczy odwrotnie. W części prawej obowiązuje to samo.

Ten ruch piłki podczas uderzenia w lewo lub w prawo czyni ją bardziej wiarygodną.

Mam nadzieję, że masz pomysł, przynajmniej logicznie. Dziękuję


1

Wyobraź sobie, że obliczasz odległość między środkiem wiosła a punktem, w którym uderzyła piłka Y i ją wołasz d. Załóżmy, że dma wartość dodatnią, gdy piłka uderzy powyżej środka wiosła. Możesz teraz zwiększyć d * -0.1prędkość Y swojej piłki, a zacznie ona zmieniać kierunek. Oto przykład w javascript, który można łatwo przetłumaczyć na c #!

var canvas = document.querySelector('canvas');
var resize = function () {
  canvas.width = innerWidth;
  canvas.height = innerHeight;
};
resize();
var ctx = canvas.getContext('2d');
var ball = {
  size: 3,
  x: 1,
  y: canvas.height/2,
  vx: 2,
  vy: 0
};
var paddle = {
  height: 40,
  width: 3,
  x: canvas.width/2,
  y: canvas.height/2
};
addEventListener('mousemove', function (e) {
  paddle.y = e.clientY - (paddle.height/2);
});
var loop = function () {
  resize();
  ball.x += ball.vx;
  ball.y += ball.vy;
  if (ball.x > canvas.width || ball.x < 0) ball.vx *= -1; // horiz wall hit
  if (ball.y > canvas.height || ball.y < 0) ball.vy *= -1; // vert wall hit
  if (ball.x >= paddle.x && ball.x <= paddle.x + paddle.width && ball.y >= paddle.y && ball.y <= paddle.y + paddle.height) {
    // paddle hit
    var paddleCenter = paddle.y + (paddle.height/2);
    var d = paddleCenter - ball.y;
    ball.vy += d * -0.1; // here's the trick
    ball.vx *= -1;
  }
  ctx.fillRect(ball.x,ball.y,ball.size,ball.size);
  ctx.fillRect(paddle.x,paddle.y,paddle.width,paddle.height);
  requestAnimationFrame(loop);
};
loop();
body {overflow: hidden; margin: 0}
canvas {width: 100vw; height: 100vh}
<canvas></canvas>



0

Cześć. Ostatnio próbowałem stworzyć grę w piłkę i wymyśliłem rozwiązanie tego problemu. Więc co zrobiłem: wiosło porusza się podczas gry. Mój układ współrzędnych jest taki, jaki jest, lewy górny punkt płótna to 0,0. Łopatka porusza się w tym układzie współrzędnych. Oś x wskazuje od 0 do szerokości obszaru roboczego, a oś y wskazuje od 0 do wysokości obszaru roboczego. Stworzyłem wiosło o stałej szerokości 100 i wysokości 20. A potem narysuję wokół niego wyobrażony okrąg. Kiedy piłka uderza w wiosło, obliczam punkt środkowy wiosła

double paddleCenter=Squash.paddle.getXpos()+Squash.paddle.getPaddleWidth()/2;

Następnie odejmuję środek od aktualnej pozycji piłki w ten sposób, że układ współrzędnych znajdzie się na środku łopatki, ballCenter to punkt, w którym piłka uderzyła w łopatkę (- (szerokość wiosła + r) .. 0 .. (szerokość wiosła + r )) to nic innego jak przeskalowanie punktu uderzenia na wiosle

double x0 = ballCenterX-paddleCenter;

obliczyć punkt przecięcia okręgu za pomocą punktu uderzenia piłki (x0) jest to ponowne obliczenie, prosimy o współrzędną y na okręgu o znanej już współrzędnej x0 i potrzebne było odwrócenie osi y

double y0 = -Math.sqrt(paddleRadius*paddleRadius-x0*x0);

obliczyć pochodną równania normalnego koła zdefiniowanego wokół łopatki za pomocą łopatki Radis Radius f (x, y) = x ^ 2 + y ^ 2-r ^ 2

double normalX=2*x0;
double normalY=2*y0;

znormalizować wektor N, aby uzyskać wektor jednostkowy normalnej powierzchni

double normalizer=Math.sqrt(normalX*normalX + normalY*normalY);
normalX=normalX/normalizer;
normalY=normalY/normalizer;

teraz mamy znormalizowane (jednostkowe) normalne powierzchni wiosła. Oblicz nowy kierunek za pomocą tych normalnych powierzchni, zostanie to obliczone za pomocą wzoru wektora odbicia: new_direction = old_direction-2 * dot (N, old_direction) * N, ale zamiast tego z normalną powierzchnią skierowaną zawsze w górę, normalna będzie zmieniać się z miejsca na punkt, w którym piłka uderza w wiosło

double eta=2; //this is the constant which gives now perfect reflection but with different normal vectors, for now this set to 2, to give perfect reflection
double dotprod=vX*normalX+vY*normalY;
vX=vX-eta*dotprod*normalX;//compute the reflection and get the new direction on the x direction
vY=-vY;//y direction is remain the same (but inverted), as we just want to have a change in the x direction

Opublikowałem swoje rozwiązanie tego problemu. Aby uzyskać więcej informacji i pełną grę, możesz zobaczyć moje repozytorium github:

https://github.com/zoli333/BricksGame

napisane w języku Java z zaćmieniem. Istnieje inne rozwiązanie tego skomentowane w Ball.java, w którym przeskalowanie nie występuje. Nie przesuwam układu współrzędnych do punktu środkowego łopatki, zamiast tego obliczam wszystkie powyższe z górnego układu współrzędnych 0,0 względem punkt środkowy wiosła. Działa to również.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.