À Pong, comment calculez-vous la direction de la balle lorsqu'elle rebondit sur la raquette?

28

J'essaie de comprendre ce problème très Hello World-y dans le développement de jeux. J'ai créé un jeu TicTacToe dans XNA, donc je suppose que la prochaine étape serait un clone de Breakout .

Gardez à l'esprit que je n'ai aucune connaissance de la programmation de jeux ni même des mathématiques à appliquer où. C'est pourquoi je pose cette question.


À la question: Comment puis-je déterminer où la balle doit rebondir lorsqu'elle frappe la pagaie au bas de l'écran?

J'imagine que ce serait quelque chose comme:

  1. Capturez la vitesse et l'angle de la balle entrante.
  2. Détecter l'endroit où il a touché la barre (extrême gauche, extrême droite, centre) et selon cela lui donner une vitesse plus élevée s'il a touché les zones extérieures.
  3. C'est là que je suis coincé. Hehe.

Des idées? Je me rends compte que ce n'est pas une question de type de réponse simple, mais je suis sûr que tout le monde est confronté à un moment donné.

Je lis le livre Algèbre linéaire qui a été recommandé sur ce site Web, mais je n'ai toujours aucune idée si je dois l'appliquer ici.

Anko
la source
Écrivez pong avant le breakout, vous pouvez ensuite exporter les classes de balle, de mur et de pagaie et les étendre de manière à ce qu'elles fonctionnent avec les différents types de briques et de bonus. De plus, je considérerais le pong plus simple que l'évasion.
Incognito

Réponses:

30

Voici la logique pertinente que j'ai utilisée sur le pong sur ma page d'accueil : (allez jouer avant de lire, afin que vous sachiez l'effet que j'obtiens avec le code suivant)

Essentiellement, lorsque le ballon entre en collision avec la raquette, sa direction est complètement ignorée; on lui donne une nouvelle direction selon la distance du centre de la pagaie à laquelle il est entré en collision. Si la balle frappe la raquette en plein centre, elle est renvoyée exactement à l'horizontale; s'il frappe directement sur le bord, il s'envole à un angle extrême (75 degrés). Et il voyage toujours à vitesse constante.

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

Prenez la valeur Y du milieu de la raquette et soustrayez l'intersection Y de la balle. Si la palette mesure 10 pixels de haut, ce nombre sera compris entre -5 et 5. J'appelle cela "l'intersection relative" car elle se trouve maintenant dans "l'espace de la palette", l'intersection de la balle par rapport au milieu de la palette.

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

Prenez l'intersection relative et divisez-la par la moitié de la hauteur de la palette. Maintenant, notre nombre de -5 à 5 est une décimale de -1 à 1; c'est normalisé . Multipliez-la ensuite par l'angle maximum selon lequel vous voulez que la balle rebondisse. Je l'ai réglé sur 5 * Pi / 12 radians (75 degrés).

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

Enfin, calculez les nouvelles vitesses de balle, en utilisant une trigonométrie simple.

Ce n'est peut-être pas tout à fait l'effet recherché, ou vous pouvez également déterminer une vitesse en multipliant l'intersection relative normalisée par une vitesse maximale; cela rendrait le ballon aller plus vite s'il frappe près du bord d'une pagaie, ou plus lentement s'il frappe près du centre.


J'aimerais peut-être un peu de code sur ce à quoi ressemblerait un vecteur ou comment je pourrais enregistrer la variable du vecteur que les balles ont (vitesse et direction).

Un vecteur contient à la fois la vitesse et la direction, implicitement. Je stocke mon vecteur en tant que "vx" et "vy"; c'est-à-dire la vitesse dans la direction x et la vitesse dans la direction y. Si vous n'avez pas suivi de cours d'introduction à la physique, cela peut vous sembler quelque peu étranger.

La raison pour laquelle je fais cela est parce qu'elle réduit les calculs par image nécessaires; chaque image, il suffit de le faire x += vx * time;et y += vy * time;où le temps est le temps écoulé depuis la dernière image, en millisecondes (donc les vitesses sont en pixels par milliseconde).


Concernant la mise en œuvre de la capacité à courber le ballon:

Tout d'abord, vous devez connaître la vitesse de la raquette au moment où la balle frappe; ce qui signifie que vous devez garder une trace de l'historique de la pagaie, afin de pouvoir connaître une ou plusieurs positions passées de la pagaie afin de pouvoir les comparer à sa position actuelle pour voir si elle a bougé. (changement de position / changement de temps = vitesse; vous avez donc besoin de 2 positions ou plus, et les heures de ces positions)

Vous devez maintenant également suivre une vitesse angulaire de la balle, qui représente pratiquement la courbe le long de laquelle elle se déplace, mais est équivalente à la rotation réelle de la balle. Semblable à la façon dont vous interpoleriez l'angle de rebond à partir de la position relative de la balle lors de la collision avec la palette, vous devrez également interpoler cette vitesse angulaire (ou rotation) à partir de la vitesse de la palette lors de la collision. Plutôt que de simplement régler le spin comme vous le faites avec l'angle de rebond, vous voudrez peut-être ajouter ou soustraire au spin existant de la balle, car cela a tendance à bien fonctionner dans les jeux (le joueur peut remarquer que la balle tourne et la faire tourner) encore plus follement, ou contrer le spin pour tenter de le faire voyager droit).

Notez, cependant, que bien que ce soit le sens le plus commun et probablement le plus facile à mettre en œuvre, la physique réelle d'un rebond ne repose pas uniquement sur la vitesse de l'objet qu'il frappe; un objet sans vitesse angulaire (sans rotation) qui frappe une surface à un angle, se verra conférer une rotation. Cela pourrait conduire à un meilleur mécanisme de jeu, donc vous voudrez peut-être examiner cela, mais je ne suis pas certain de la physique derrière, donc je ne vais pas essayer de l'expliquer.

Ricket
la source
Cet effet est ce que je veux; la vitesse plus rapide car elle frappe les bords de la barre. Merci beaucoup d'avoir pris le temps d'écrire ceci. J'ai du mal à comprendre certaines choses cependant; par exemple, dans le premier snipper, qu'est-ce que «intersectY»? En outre, «paddle1Y» est-il correct?
intersectY est la position de la balle à son intersection avec la raquette. Je fais un calcul trop compliqué que je ne comprends honnêtement même pas en ce moment, mais c'est essentiellement la valeur Y de la balle au moment où elle entre en collision. paddle1Y est la valeur Y de la palette, depuis le haut de l'écran; PADDLEHEIGHT est la hauteur de la palette ("barre").
Ricket
Qu'auriez-vous à ajouter pour permettre des boules "courbes"? Par exemple, lorsque la balle est sur le point de frapper la pagaie, vous déplacez la pagaie afin de faire tourner la balle. Quelque chose comme ça: en.wikipedia.org/wiki/Curve_ball
Zolomon
Voir le montage et faites-moi savoir ce que vous en pensez (si vous avez besoin de plus d'informations sur quelque chose, n'obtenez rien, etc.)
Ricket
Merci! Superbe réponse, je me suis toujours demandé comment obtenir cet effet!
Zolomon
8

Cela fait un moment que je n'ai pas fait ça, mais je pense que j'ai bien compris.

Étant donné une collision parfaite, l'angle de réflexion est égal à l'angle d'incidence.

Vous connaissez la normale de votre pagaie (en supposant une surface plane): N Vous connaissez votre position d'origine de votre balle (au début de votre pas de temps): P Vous connaissez votre nouvelle position de la balle (à la fin du pas de temps): P 'Vous connaissez votre point de collision: C En supposant que vous avez calculé que le segment P -> P' passe à travers votre palette, votre nouvelle position réfléchie (P '') serait:

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

La sous-expression N * (P 'dot -N) calcule la profondeur le long de la normale de collision de la balle. Le signe moins est de corriger le fait que nous vérifions la profondeur opposée à la direction de la normale.

Le P '+ 2 * la partie de la sous-expression ramène la balle au-dessus du plan de collision de 2 fois la profondeur de la collision.

Si vous voulez une collision moins que parfaite, changez le facteur 2 pour qu'il soit (1 + (1-k)) où k est votre coefficient de frottement. Une collision parfaite a une valeur k de 0, ce qui fait que l'angle de réflexion est exactement celui de l'angle entrant. Une valeur k de 1 provoque une collision où la balle reste à la surface du plan de collision.

Votre nouveau vecteur de vitesse, V '', direction serait P '' - C. Normalisez-le et multipliez par votre vitesse entrante et votre amplitude de vitesse résultante serait la même, mais dans la nouvelle direction. Vous pouvez singer avec cette vitesse en la multipliant par un coefficient, l, qui augmenterait (l> 1) ou diminuerait (l <1) la vitesse résultante.

Résumer:

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

Où k et l sont les coefficients de votre choix.

nan0meter
la source
5

La réflexion peut être effectuée «correctement» ou «facilement».

La "bonne" façon est de calculer des vecteurs perpendiculaires aux murs. En 2D, c'est assez facile et vous pourriez probablement les coder en dur. Ensuite, l'étape de réflexion laisse essentiellement intacte la composante "parallèle" du mouvement et inverse la composante "perpendiculaire". Il y a probablement des informations détaillées sur le Web pour cela, peut-être même chez MathWorld.

La manière "simple" consiste à simplement annuler le mouvement X ou Y lorsque vous frappez un mur. Si vous frappez les parois latérales, vous nieriez X. Si vous frappez le haut, vous niez Y. Si vous voulez accélérer la balle, augmentez simplement ce que vous voulez; vous pouvez l'accélérer dans sa direction actuelle en multipliant les vitesses X et Y ou vous ne pouvez accélérer que sur un axe.

dash-tom-bang
la source
La manière "facile" et la "bonne" méthode décrite ci-dessus ne sont-elles pas essentiellement les mêmes ??
Tom
Ils sont exactement les mêmes si les murs sont le long des grands axes. Si les murs ne sont pas tout le long des axes X, Y et Z, alors non, les deux sont complètement différents.
dash-tom-bang
5

Je fais moi-même un jeu arkanoid-ish et je pense que la solution sur la façon dont la balle devrait se comporter lorsque vous frappez la pagaie est assez simple et plus rapide que d'entrer dans l'approche sin / cos ... cela fonctionne bien pour les besoins d'un jeu comme ça. Voici ce que je fais:

  • Bien sûr, étant donné que la vitesse de la balle augmente dans le temps, j'interpole les étapes avant / après x, y pour garder une détection de collision précise, en parcourant tous les "stepX" et "stepY" qui sont calculés en divisant chaque composante de la vitesse par le module du vecteur formé par les positions actuelles et futures du ballon.

  • Si une collision contre la pagaie se produit, je divise la vitesse Y par 20. Ce "20" est la valeur la plus pratique que j'ai trouvée pour obtenir mon angle maximal résultant lorsque la balle frappe sur les côtés de la pagaie, mais vous pouvez la changer comme bon vous semble vos besoins sont, jouez avec quelques valeurs et choisissez le meilleur pour vous. En divisant, disons une vitesse de 5, qui est ma vitesse de jeu initiale par ce nombre (20), j'obtiens un "facteur de rebond" de 0,25. Ce calcul maintient mes angles assez proportionnels lorsque la vitesse augmente dans le temps jusqu'à ma valeur de vitesse maximale qui, par exemple, pourrait être de 15 (dans ce cas: 15/20 = 0,75). Sachant que mes coordonnées x, y sont au milieu (x et y représentent le centre de la palette), je multiplie ensuite ce résultat par la différence entre la position de la balle et la position de la palette. Plus la différence est grande, le grandear l'angle résultant. De plus, en utilisant un padlle à manche moyen, vous obtenez le signe correct pour l'incrément x en fonction du côté que la balle frappe sans avoir à vous soucier du calcul du centre. En pseudo-code:

Pour n = 0 au module ...

si collision_detected alors speedX = - (speedY / 20) * (paddleX - ballX); speedY = -speedY;
sortie; fin si

...

x = x + stepX; y = y + stepY;

fin pour

Rappelez-vous, essayez toujours de garder les choses SIMPLES. J'espère que ça aide!

co_Opernicus
la source
4

La palette dans Breakout, lorsqu'elle suit le style que vous décrivez, est généralement modélisée comme une surface courbe. L'angle d'incidence change en fonction de l'endroit où la pagaie frappe. Au point mort, la tangente à la courbe est absolument horizontale et la balle se reflète comme prévu. Lorsque vous vous éloignez du centre, la tangente à la courbe devient de plus en plus angulaire et la balle se reflète différemment en conséquence.

Le point clé est que c'est l'angle de réflexion, et non la vitesse de la balle, qui change. La vitesse de la balle augmente généralement lentement au fil du temps.


la source
1
Vous dites que c'est une surface incurvée et cela semble logique dans ma tête, mais une fois que j'essaie de penser en termes de code, les choses deviennent floues rapidement. Comment pourrais-je même déclarer cela dans le code comme une variable ou quelque chose.
Quelque chose comme angle = 1 - 2 * (ball.x - paddle.left) / paddle.widthvous donnera un nombre compris entre 1 et -1; ceci (fois une valeur modifiée pour vos mécanismes de jeu) est la pente de la ligne tangente au point où la balle est entrée en collision. Réfléchissez sur cette ligne plutôt que sur la ligne strictement horizontale.
4

Nolan Bushnell a prononcé un discours d'ouverture au SIEGE le week-end dernier et a parlé d'un problème similaire avec le pong d'origine. Vous n'avez pas à faire beaucoup de calculs compliqués. Si vous frappez vers la partie gauche du panneau, envoyez la balle vers la gauche. Faites de même pour le côté droit.

Pour commencer, vous pouvez faire un angle de 45 degrés pour les côtés gauche et droit. Une fois que vous avez terminé le jeu, vous pouvez, si vous le souhaitez, revenir en arrière et rendre cela plus compliqué, mais le plus simple possible pour commencer.

lathomas64
la source
1
J'ai également vu ce discours, son point était que c'était une décision de conception et non une décision mathématique: "angle d'incidence = angle de réflexion" serait correct mais rendrait le gameplay faible. De plus, dans la version originale de Pong and Breakout, la vitesse était fonction du nombre de collisions balle / pagaie (donc elle s'accélère avec le temps). Ils ont également réduit la taille des palettes après un certain nombre de coups. J'éviterais cependant de laisser le ballon monter tout droit, alors vous pourriez y laisser la pagaie indéfiniment.
Ian Schreiber
4

Breakout est un travail classique pour débutants pour commencer à plonger dans le monde de la programmation de jeux basée sur la physique. Fondamentalement, la balle a un mouvement de rebond lorsqu'elle frappe le mur. Comme quelqu'un ci-dessus l'a suggéré, l'angle d'incidence est égal à l'angle de réflexion. Mais quand on considère la balle frapper la pagaie. La logique est divisée en 3 sections. 1.) La balle frappe la partie centrale de la pagaie. 2.) La balle frappe la partie gauche de la raquette. 3.) La balle frappant la position droite de la raquette.

Lorsque vous considérez la partie centrale: vous ne devez pas différer l'effet de rebond de celui qui est appliqué lorsque vous frappez la balle. La balle est juste déviée normalement, mais lorsque l'une ou l'autre direction est touchée, le cas est différent.

Lorsque le ballon est frappé sur le côté gauche, c'est-à-dire, considérez le ballon venant du côté gauche de l'écran et vous venez avec la pagaie du côté droit. Ensuite, lorsque vous frappez la balle avec la partie gauche, la balle doit se refléter dans la direction d'où elle vient. À gauche avec le même angle d'où elle vient. Il en va de même inversement. Dans la partie droite, la même chose s'applique également.

Ce mouvement de la balle vers la gauche ou la droite lorsqu'elle est frappée la rend plus crédible.

J'espère que vous avez eu l'idée, au moins du point de vue logique. Merci

Vishnu
la source
1

Imaginez que vous calculez la distance entre le centre de la raquette et le point où la balle Y a frappé et appelez-la d. Supposons dque la valeur soit positive lorsque la balle frappe au-dessus du centre de la raquette. Vous pouvez maintenant augmenter d * -0.1la vitesse Y de votre balle et elle commencera à changer de direction. Voici un exemple en javascript qui peut être facilement traduit en 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>

rafaelcastrocouto
la source
0

Salut, j'ai récemment essayé de faire un jeu de balle et j'ai trouvé une solution pour cela. Alors ce que j'ai fait: la pagaie bouge pendant que nous jouons. Mon système de coordonnées est laissé tel quel, le point en haut à gauche du canevas est 0,0. La palette se déplace dans ce système de coordonnées. L'axe x pointe de 0 à la largeur du canevas et l'axe y pointe sur 0 à la hauteur du canevas. J'ai créé une pagaie avec une taille fixe de 100 largeur et 20 hauteur. Et puis je dessine un cercle imaginé autour de lui. Lorsque la balle frappe la pagaie, je calcule le point central de la pagaie

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

Ensuite, je soustrais le centre de la position actuelle de la balle de cette façon, le système de coordonnées sera au centre de la palette, ballCenter est le point où la balle a frappé la palette (- (largeur de palette + r) .. 0 .. (largeur de palette + r )) ce n'est rien d'autre que de redimensionner le point de frappe sur la pagaie

double x0 = ballCenterX-paddleCenter;

calculer le point d'intersection du cercle à l'aide du point de frappe de la balle (x0) c'est un recalcul, nous demandons la coordonnée y sur le cercle avec la coordonnée x0 déjà connue et il fallait un flip pour l'axe y

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

calculer la dérivée de l'équation normale du cercle qui est définie autour de la palette avec radis paddleRadius f (x, y) = x ^ 2 + y ^ 2-r ^ 2

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

normaliser le vecteur N, pour obtenir un vecteur unitaire pour la surface normale

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

nous avons maintenant les normales de surface normalisées (unitaires) pour la palette. Calculez la nouvelle direction avec ces normales de surface, cela sera calculé à l'aide de la formule du vecteur de réflexion: new_direction = old_direction-2 * point (N, old_direction) * N, mais avec la surface normale pointant toujours vers le haut, la volonté de la normale changer d'un point à un autre où la balle touche la raquette

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

J'ai publié ma solution à ce problème. Pour plus de détails et pour le jeu complet, vous pouvez voir mon dépôt github:

https://github.com/zoli333/BricksGame

écrit en java avec éclipse. Il y a une autre solution pour cela commentée dans Ball.java, où le redimensionnement ne se produit pas, je ne déplace pas le système de coordonnées au point central de la palette, au lieu de cela je calcule tout cela ci-dessus à partir du système de coordonnées topleft 0,0 par rapport à le point central de la pagaie. Cela fonctionne aussi.

zoli333
la source