В Pong, как вы рассчитываете направление мяча, когда он отскакивает от ракетки?


28

Я пытаюсь решить эту самую проблему Hello World-y в разработке игр. Я создал игру TicTacToe в XNA, поэтому я думаю, что следующим шагом будет клон Breakout .

Имейте в виду, что у меня нет знаний о программировании игр и даже о том, какую математику я должен применять, где. Вот почему я задаю этот вопрос.


На вопрос: как я могу определить, где мяч должен подпрыгивать, когда он ударяет по веслу в нижней части экрана?

Я полагаю, это будет что-то вроде:

  1. Скорость захвата и угол от входящего шара.
  2. Определите, где он коснулся планки (крайний левый, крайний правый, центральный) и в соответствии с этим увеличьте скорость, если он коснулся внешних областей.
  3. Вот где я застрял. Хехе.

Есть идеи? Я понимаю, что это не простой вопрос типа ответа, но я уверен, что это тот вопрос, с которым каждый сталкивается в какой-то момент.

Я читаю книгу « Линейная алгебра», которая была рекомендована на этом сайте, но я до сих пор не представляю, стоит ли мне применять ее здесь.


Напишите pong перед прорывом, затем вы можете экспортировать классы ball, wall и paddle и расширить их так, чтобы они работали с различными типами кирпичей и бонусов. Плюс, я бы посчитал понг проще, чем прорыв.
Инкогнито

Ответы:


30

Вот соответствующая логика, которую я использовал на понге на моей домашней странице : (пожалуйста, поиграйте в нее перед чтением, чтобы вы знали эффект, которого я достигаю с помощью следующего кода)

По существу, когда мяч сталкивается с веслом, его направление полностью игнорируется; ему дается новое направление в зависимости от того, как далеко от центра весла оно столкнулось. Если мяч попадает в лопатку прямо в центре, он удаляется точно горизонтально; если он ударяет прямо по краю, он улетает под крайним углом (75 градусов). И он всегда путешествует с постоянной скоростью.

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

Возьмите среднее значение Y весла и вычтите пересечение Y шара. Если весло имеет высоту 10 пикселей, это число будет между -5 и 5. Я называю это «относительным пересечением», потому что оно сейчас находится в «пространстве весла», пересечении мяча относительно середины весла.

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

Возьмите относительное пересечение и разделите его на половину высоты весла. Теперь наше число от -5 до 5 является десятичным от -1 до 1; это нормализовано . Затем умножьте его на максимальный угол, на который вы хотите, чтобы мяч отскочил. Я установил его на 5 * Пи / 12 радиан (75 градусов).

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

Наконец, рассчитайте новые скорости мяча, используя простую тригонометрию.

Это может быть не совсем тот эффект, к которому вы стремитесь, или вы можете также определить скорость, умножив нормализованное относительное пересечение на максимальную скорость; это заставило бы мяч двигаться быстрее, если он ударяется о край весла, или медленнее, если он ударяется о центр.


Возможно, я хотел бы получить некоторый код о том, как будет выглядеть вектор или как я могу сохранить переменную вектора, имеющую шары (скорость и направление).

Вектор содержит не только скорость, но и направление. Я храню свой вектор как "vx" и "vy"; то есть скорость в направлении х и скорость в направлении у. Если вы не прошли начальный курс по физике, это может показаться вам чуждым.

Причина, по которой я это делаю, заключается в том, что это уменьшает необходимые расчеты для каждого кадра; каждый кадр, который вы только что делаете, x += vx * time;и y += vy * time;где время - это время с последнего кадра, в миллисекундах (следовательно, скорости в пикселях на миллисекунду).


Что касается реализации способности изгибать мяч:

Прежде всего, вам нужно знать скорость весла в момент удара мяча; Это означает, что вам нужно отслеживать историю весла, чтобы вы могли знать одну или несколько из прошлых позиций весла, чтобы вы могли сравнить их с его текущим положением, чтобы увидеть, перемещалось ли оно. (изменение в положении / изменение во времени = скорость; вам нужно 2 или более позиций и время этих позиций)

Теперь вам также необходимо отслеживать угловую скорость шара, которая практически представляет собой кривую, по которой он движется, но эквивалентна реальному вращению шара. Аналогично тому, как вы будете интерполировать угол отскока от относительного положения мяча при столкновении с веслом, вам также необходимо будет интерполировать эту угловую скорость (или вращение) от скорости движения весла при столкновении. Вместо того, чтобы просто устанавливать вращение, как вы делаете с углом отскока, вы можете добавить или вычесть существующее вращение мяча, потому что это хорошо работает в играх (игрок может заметить, что шар вращается, и заставить его вращаться). еще более дико или противостоять вращению в попытке заставить его двигаться прямо).

Обратите внимание, однако, что, хотя это самый здравый смысл и, возможно, самый простой способ его реализации, реальная физика отскока не зависит исключительно от скорости объекта, по которому он попадает; объект без угловой скорости (без вращения), который ударяется о поверхность под углом, получит вращение на нем. Это может привести к лучшей игровой механике, так что вы можете захотеть разобраться в этом, но я не уверен в физике, стоящей за этим, поэтому я не буду пытаться объяснить это.


Этот эффект - то, для чего я иду; чем быстрее скорость, когда он достигает краев бара. Спасибо большое за то, что нашли время написать это. У меня возникли проблемы с пониманием некоторых вещей, хотя; например, в первом фрагменте, что такое «пересечение»? Кроме того, 'paddle1Y' является правильной высотой стержня?

Пересечение - это положение мяча, в котором он пересекает весло. Я делаю слишком сложный расчет, который я, честно говоря, даже сейчас не понимаю, но по сути это значение Y мяча в момент его столкновения. paddle1Y - это значение Y весла в верхней части экрана; PADDLEHEIGHT - это высота весла («штанга»).
Рикет

Что бы вы добавили, чтобы разрешить «кривые» шары? Например, когда мяч вот-вот ударится о весло, вы перемещаете весло, чтобы сделать изгиб шара. Примерно так: en.wikipedia.org/wiki/Curve_ball
Zolomon

Посмотрите изменения и дайте мне знать, что вы думаете (если вам нужно больше информации о чем-то, не получите что-то и т. Д.)
Ricket

Благодарность! Прекрасный ответ, я всегда задавался вопросом, как добиться этого эффекта!
Zolomon

8

Прошло много времени с тех пор, как я это сделал, но я думаю, что понял это правильно.

При идеальном столкновении угол отражения равен углу падения.

Вы знаете нормальное положение вашего весла (предполагая плоскую поверхность): N Вы знаете свое первоначальное положение мяча (в начале своего временного шага): P Вы знаете свое новое положение мяча (в конце временного шага): P 'Вы знаете свою точку столкновения: C Предполагая, что вы рассчитали, что сегмент P -> P' проходит через ваше весло, ваше новое отраженное положение (P '') будет:

P '+ 2 * (N * (P' точка -N))

Подвыражение N * (P 'dot -N) вычисляет глубину вдоль нормали столкновения, пройденного мячом. Знак минус исправляет тот факт, что мы проверяем глубину, противоположную направлению нормали.

P '+ 2 * часть подвыражения перемещает шар обратно над плоскостью столкновения в 2 раза глубиной столкновения.

Если вы хотите, чтобы столкновение было менее совершенным, измените коэффициент 2 на (1 + (1-k)), где k - ваш коэффициент трения. Идеальное столкновение имеет значение k, равное 0, в результате чего угол отражения точно совпадает с углом отражения. Значение k, равное 1, вызывает столкновение, при котором шарик остается на поверхности плоскости столкновения.

Ваш новый вектор скорости, V '', направление будет P '' - C. Нормализовать его и умножить на вашу входящую скорость, и результирующая величина скорости будет такой же, но в новом направлении. Вы можете изменить эту скорость, умножив ее на коэффициент l, который либо увеличит (l> 1), либо уменьшит (l <1) результирующую скорость.

Обобщить:

P '' = P '+ (1-k) * (N * (P точка -N)) V' '= l * V * ((P' '- C) / | P' '- C |)

Где k и l - коэффициенты по вашему выбору.


5

Отражение может быть сделано «правильно» или «легко».

«Правильный» способ - вычислить векторы, перпендикулярные стенам. В 2D это довольно просто, и вы, вероятно, можете просто жестко их кодировать. Затем этап отражения по существу оставляет «параллельный» компонент движения нетронутым и обращает «перпендикулярный» компонент. Вероятно, в Интернете для этого есть подробная информация, возможно, даже на MathWorld.

«Легкий» способ - просто отменить движение X или Y при ударе о стену. Если вы ударите по боковым стенкам, вы отрицаете X. Если вы ударите по вершине, вы отрицаете Y. Если вы хотите ускорить мяч, просто увеличьте все, что вы хотите; Вы можете ускорить его в текущем направлении, умножив скорости X и Y, или вы можете ускорить только по одной оси.


Разве «легкий» и «правильный» способ, описанный выше, не являются ли по существу одинаковыми ??
Том

Они точно такие же, если стены вдоль главных осей. Если стены не все вдоль осей X, Y и Z, то нет, эти две совершенно разные.
dash-tom-bang

5

Я сам тоже играю в арканоид, и думаю, что решение о том, как должен вести себя мяч при ударе по веслу, намного проще и быстрее, чем попадание в подход sin / cos ... оно отлично работает в целях игра такая Вот что я делаю:

  • Конечно, поскольку скорость мяча увеличивается во времени, я интерполирую шаги до, после x, y, чтобы сохранить точное обнаружение столкновения, проходя по всем «stepX» и «stepY», которые вычисляются, деля каждый компонент скорости на модуль вектора текущими и будущими позициями мяча.

  • Если происходит столкновение с веслом, я делю скорость Y на 20. Это «20» - наиболее удобное значение, которое я нашел, чтобы получить мой результирующий максимальный угол, когда мяч ударяет по сторонам весла, но вы можете изменить его на любой ваши потребности, просто поиграйте с некоторыми ценностями и выберите лучшее для вас. Разделив, скажем, скорость 5, которая является моей начальной скоростью игры, на это число (20), я получаю «коэффициент отскока», равный 0,25. Этот расчет сохраняет мои углы достаточно пропорциональными, когда скорость увеличивается во времени до моего максимального значения скорости, которое, например, может быть 15 (в этом случае: 15/20 = 0,75). Учитывая, что мои координаты x, y весла находятся в середине (x и y представляют центр весла), я затем умножаю этот результат на разницу между положением шара и положением весла. Чем больше разница, Greatear результирующий угол. Кроме того, используя срединный манипулятор, вы получаете правильный знак для приращения x в зависимости от того, с какой стороны ударяет мяч, не беспокоясь о расчете центра. В псевдокоде:

Для n = 0 до модуля ...

если collision_detected, то speedX = - (speedY / 20) * (paddleX - ballX); speedY = -speedY;
выход; конец, если

...

х = х + stepX; y = y + stepY;

конец для

Помните, всегда старайтесь держать вещи простыми. Я надеюсь, что это помогает!


4

Весло в Breakout, когда оно следует описанному вами стилю, обычно моделируется как криволинейная поверхность. Угол падения изменяется в зависимости от того, где он находится на весле. На мертвой точке касательная к кривой абсолютно горизонтальна, и шар отражается, как и ожидалось. При смещении от центра касательная к кривой становится все более наклонной, и в результате шар отражается по-разному.

Ключевым моментом является то, что меняется угол отражения, а не скорость мяча. Скорость мяча обычно медленно увеличивается со временем.


1
Вы говорите, как изогнутую поверхность, и это звучит логично в моей голове, но как только я пытаюсь думать об этом с точки зрения кода, вещи быстро размываются. Как я мог даже объявить это в коде как переменную или что-то еще.

Нечто подобное angle = 1 - 2 * (ball.x - paddle.left) / paddle.widthдаст вам число от 1 до -1; это (иногда какое-то значение, измененное для вашей игровой механики) - это наклон касательной в точке, где столкнулся мяч. Отражайте от этой линии, а не от строго горизонтальной.

4

Нолан Бушнелл выступил с речью на SIEGE в прошедшие выходные и рассказал о схожей проблеме с оригинальным понгом. Вам не нужно делать много сложных расчетов. Если вы попадаете в левую часть панели, отправьте мяч влево. Сделайте то же самое для правой стороны.

Для начала можно сделать угол для левой и правой сторон 45 градусов. Как только вы закончите игру, вы можете, если хотите, вернуться и сделать это более сложным, но сделайте это настолько простым, насколько это возможно для начала.


1
Я также видел этот лейтмотив, его мысль заключалась в том, что это было дизайнерское решение, а не математическое решение: «угол падения = угол отражения» был бы верным, но компенсировал слабый игровой процесс. Кроме того, в оригинальном понге и прорыве скорость зависела от того, сколько было столкновений мяча / весла (так что оно со временем ускоряется). Они также уменьшили размер весла после определенного количества попаданий. Хотя я бы не позволил мячу идти прямо вверх, тогда вы могли бы оставить ракетку на неопределенное время.
Ян Шрайбер

4

Breakout - это классическая работа для начинающих, чтобы начать погружаться в мир программирования игр на основе физики. По сути, у мяча есть отскок при ударе о стену. Как кто-то выше предположил, угол падения равен углу отражения. Но если учесть мяч, попавший в ракетку. Логика разделена на 3 раздела. 1.) Мяч попал в центральную часть лопатки. 2.) Мяч попал в левую часть весла. 3.) Мяч попал в правое положение весла.

Когда вы рассматриваете центральную часть: вам не нужно различать эффект отскока от того, который применяется при ударе по мячу. Мяч просто отклоняется в обычном режиме. Но когда происходит удар в любом направлении, дело обстоит иначе.

Когда мяч попал в левую сторону, то есть рассмотрим мяч, идущий с левой стороны экрана, и вы идете с веслом с правой стороны. Затем, когда вы ударяете по мячу левой частью, мяч должен отражаться в направлении, откуда он пришел, т.е. с левой стороны под тем же углом, с которого он пришел. То же самое, и наоборот. В правой части также применяется то же самое.

Это движение мяча в направлении влево или вправо при ударе делает его более правдоподобным.

Надеюсь, у вас есть идея, по крайней мере, логическая мудрость. Спасибо


1

Представьте, что вы рассчитываете расстояние между центром весла и точкой, в которую попал мяч Y, и называете это d. Предположим, dимеет положительное значение, когда мяч ударил выше центра весла. Теперь вы можете добавить d * -0.1к скорости Y вашего шара, и он начнет менять направление. Вот пример в javascript, который можно легко перевести на 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

Привет, я недавно пытался сделать игру с мячом и нашел решение для этого. Итак, что я сделал: весло движется, пока мы играем в игру. Моя система координат оставлена ​​как есть, верхняя левая точка холста - 0,0. Весло движется в этой системе координат. Ось x указывает от 0 до ширины холста, а ось y указывает от 0 до высоты холста. Я создал весло с фиксированным размером 100 шириной и 20 высотой. А потом я рисую воображаемый круг вокруг него. Когда мяч попадает на весло, я вычисляю центральную точку весла

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

Затем я вычитаю центр из текущей позиции мяча, таким образом, система координат будет находиться в центре лопатки, а ballCenter - это точка, в которой мяч касается лопатки (- (ширина лопатки + r) .. 0 .. (ширина лопатки + r )) это не что иное, как масштабирование точки удара на весле

double x0 = ballCenterX-paddleCenter;

вычислить точку пересечения окружности с помощью точки удара шара (x0), это пересчет, мы запрашиваем координату y на окружности с уже известной координатой x0, и для оси y потребовалось переворот

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

вычислить производную от нормального уравнения круга, которое определено вокруг весла с помощью radis paddleRadius f (x, y) = x ^ 2 + y ^ 2-r ^ 2

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

нормализовать вектор N, чтобы получить единичный вектор для нормали поверхности

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

Теперь у нас есть нормализованные (единичные) поверхностные нормали для весла. Вычислите новое направление с этими нормалями поверхности, это будет вычислено с помощью формулы вектора отражения: new_direction = old_direction-2 * точка (N, old_direction) * N, но вместо этого, если нормаль поверхности всегда направлена ​​вверх, воля нормали меняться от точки к точке, где мяч попадает на весло

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

Я опубликовал свое решение этой проблемы. Для более подробной информации и полной версии игры вы можете увидеть мой репозиторий github:

https://github.com/zoli333/BricksGame

написано в Java с затмением. Для этого есть другое решение, закомментированное в Ball.java, где изменение масштаба не происходит. Я не перемещаю систему координат в центральную точку весла, вместо этого я вычисляю все вышеперечисленное из системы координат 0,0 верхнего элемента относительно центральная точка весла. Это работает также.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.