Як у Понгу, як обчислити напрямок кулі, коли вона відскакує від весла?


28

Я намагаюся обернути голову навколо цієї проблеми Hello World-y в розвитку ігор. Я створив гру TicTacToe в XNA, тому, мабуть, наступним кроком буде клей Breakout .

Майте на увазі, що я не маю знань щодо програмування ігор і навіть до того, яку математику я повинен застосувати до де. Ось чому я задаю це питання.


На питання: Як я можу визначити, куля куля повинна відскочити, коли вона потрапляє на весло в нижній частині екрана?

Я думаю, це буде щось на кшталт:

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

Будь-які уявлення? Я усвідомлюю, що це не простий варіант відповіді, але я впевнений, що це одна людина, з якою стикаються в якийсь момент.

Я читаю книгу Лінійна алгебра, яку рекомендували на цьому веб-сайті, але досі не маю уявлення, чи варто її застосовувати тут.


Напишіть понг перед проривом, ви зможете експортувати класи з м'ячів, стін та весла та розширити їх таким чином, щоб вони працювали з різними типами цегли та бонусами. Плюс, я вважав би понг простішим, ніж прорив.
інкогніто

Відповіді:


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 * Pi / 12 радіанів (75 градусів).

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

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

Це може бути не зовсім ефектом, на який ви збираєтесь, або ви можете також визначити швидкість шляхом множення нормованого відносного перетину на максимальну швидкість; це зробило би м'яч швидше, якщо він вдариться біля краю весла, або повільніше, якщо б'є біля центру.


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

Вектор містить і швидкість, і напрямок, неявно. Я зберігаю свій вектор як "vx" і "vy"; тобто швидкість у напрямку x та швидкість у напрямку y. Якщо ви не пройшли вступний курс з фізики, це може здатися вам дещо чужим.

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


Щодо реалізації можливості кривої кулі:

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

Тепер вам також потрібно відстежити кутову швидкість кулі, яка практично представляє криву, по якій вона рухається, але еквівалентна реальному віджиманню кулі. Подібно до того, як ви би інтерполювали кут відскоку від відносного положення кулі при зіткненні з веслом, вам також знадобиться інтерполювати цю кутову швидкість (або віджимання) від швидкості весла при зіткненні. Замість того, щоб просто встановити віджимання, як ви робите з кутом відскоку, ви можете додати або відняти до існуючого віджимання м'яча, тому що це, як правило, добре працює в іграх (гравець може помітити, що м'яч крутиться, і змусить його крутитися ще дикіше чи протидіяти віджимання в спробі змусити його рухатися прямо).

Однак зауважте, що хоча це найпоширеніший сенс і, мабуть, найпростіший спосіб його втілення, фактична фізика відмов не покладається лише на швидкість об'єкта, в який він потрапляє; об’єкт без кутової швидкості (без віджиму), який потрапляє на поверхню під кутом, матиме на собі віджимання. Це може призвести до кращого ігрового механіка, тож, можливо, ви захочете розібратися в цьому, але я не впевнений у фізиці, що стоїть за ним, тому я не збираюся намагатися пояснити це.


Цей ефект - це те, за що я йду; тим швидша швидкість, як вона потрапляє на краї смуги. Дякую купу, що знайшли час, щоб написати це. Я маю деякі проблеми з розумінням кількох речей; наприклад, у першому снайпері що таке "перетинання"? Крім того, "paddle1Y" правильна висота планки?

intersectY - це положення кулі, де воно перетинає весло. Я роблю надто складний розрахунок, якого я, чесно кажучи, навіть зараз не розумію, але по суті це значення Y кулі в момент його зіткнення. paddle1Y - значення Y весла, починаючи з верхньої частини екрана; PADDLEHEIGHT - це висота весла ("бар").
Ricket

Що б вам довелося додати, щоб дозволити «криві» кулі? Наприклад, коли м'яч збирається вдарити весло, ви переміщуєте весло, щоб зробити кулю кривою. Щось подібне: en.wikipedia.org/wiki/Curve_ball
Золомон

Перегляньте редагування і дайте мені знати, що ви думаєте (якщо вам потрібна додаткова інформація про щось, не отримуйте щось тощо)
Ricket

Спасибі! Чудова відповідь, я завжди цікавився, як досягти цього ефекту!
Золомон

8

Минув час, коли я це зробив, але я думаю, що я маю це правильно.

За умови ідеального зіткнення кут відбиття дорівнює куту падіння.

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

P '+ 2 * (N * (P' крапка -N))

Сублексія N * (P 'крапка -N) обчислює глибину вздовж норми зіткнення, яку пройшов куля. Знак мінус - це виправити той факт, що ми перевіряємо глибину, протилежну напрямку від норми.

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

Якщо ви хочете менше, ніж ідеальне зіткнення, змініть коефіцієнт 2 на (1 + (1-k)), де k - ваш коефіцієнт тертя. Ідеальне зіткнення має значення ak 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, то ні, дві абсолютно різні.
Даш-Том-Банг

5

Я теж влаштовую гру з арканоїдом і думаю, що рішення щодо того, як м'яч повинен вести себе при ударі в весло, є досить простим і швидшим, ніж потрапляти в підхід гріх / соз ... він працює чудово для цілей така гра. Ось що я роблю:

  • Звичайно, оскільки швидкість кулі збільшується в часі, я інтерполюю до / після х, y кроки для точного виявлення зіткнення, перебираючи всі "stepX" та "stepY", які обчислюються діленням кожної складової швидкості на модуль вектора, що утворився за нинішнім та майбутнім положенням кулі.

  • Якщо трапляється зіткнення проти весла, я ділю швидкість Y на 20. Це "20" - це найзручніше значення, яке я знайшов, щоб отримати отриманий максимальний кут, коли куля потрапляє на сторони весла, але ви можете змінити його на будь-який ваші потреби - просто пограйте з деякими цінностями і виберіть краще для вас. Розділивши, скажімо, швидкість 5, яка є моєю початковою швидкістю гри на це число (20), я отримую "коефіцієнт відскоку" 0,25. Цей розрахунок тримає мої кути досить пропорційними, коли швидкість збільшується в часі до мого максимального значення швидкості, яке, наприклад, може бути 15 (у такому випадку: 15/20 = 0,75). Враховуючи те, що мої весла лопатки x, y є середніми (x і y представляють центр весла), я потім помножую цей результат на різницю між положенням кулі та положенням весла. Чим більша різниця, мастило отриманого кута. Крім того, використовуючи середньогранну колоду, ви отримуєте правильний знак для збільшення х, залежно від того, в яку сторону потрапляє м'яч, не турбуючись про обчислення центру. У псевдокоді:

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

якщо зіткнення_визначено, тоді speedX = - (speedY / 20) * (paddleX - ballX); speedY = -швидкість;
Вхід; закінчується, якщо

...

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

кінець для

Запомни, завжди намагайся тримати речі ПРОСТО. Я сподіваюся, що це допомагає!


4

Лопатка в Breakout, коли вона відповідає стилю, який ви описуєте, зазвичай моделюється як вигнута поверхня. Кут падіння змінюється залежно від того, куди по веслу він потрапляє. На мертвому центрі дотична лінія до кривої абсолютно горизонтальна, а куля відображається так, як очікувалося. Коли ви відходите від центру, дотична до кривої стає все більш кутовою, а куля в результаті відбивається інакше.

Ключовим моментом є те, що змінюється кут відображення, а не швидкість кулі. Швидкість кулі, як правило, з часом повільно зростає.


1
Ви говорите як про вигнуту поверхню, і це звучить логічно в моїй голові, але як тільки я намагаюся продумати це з точки зору коду, речі швидко розмиваються. Як я могла навіть оголосити це в коді як змінну чи щось таке.

Щось подібне angle = 1 - 2 * (ball.x - paddle.left) / paddle.widthдасть вам число від 1 до -1; Це (разів деяке значення, яке змінено для вашої ігрової механіки) - це нахил дотичної лінії в точці, в яку зіткнувся м'яч. Відображайте цю лінію, а не строго горизонтальну.

4

У минулі вихідні Нолан Бушнелл виступив з доповіддю в SIEGE і розповів про подібну проблему з оригінальним понг. Вам не доведеться робити багато складних обчислень. Якщо ви натиснете на ліву частину панелі hte, відправте кульку вліво. Зробіть те ж саме для правого боку.

Для початку можна зробити кут для лівої та правої сторони 45 градусів. Як тільки ви закінчите гру, ви зможете, якщо захочете повернутися назад, і зробити це складніше, але зробити це так просто, наскільки це можливо.


1
Я також бачив цю основну зауваження, його думка полягала в тому, що це дизайнерське рішення, а не математичне рішення: "кут падіння = кут відображення" був би правильним, але призначав би слабкий геймплей. Крім того, в оригінальних понгах та проривах швидкість була функцією кількості зіткнень кулі / весла (тому вона з часом прискорюється). Вони також зменшили розмір весла після певної кількості ударів. Я б не допустив, щоб м'яч ходив прямо вгору, тоді ви могли б залишити там весло на невизначений час.
Ян Шрайбер

4

Прорив - класична робота для початківців для того, щоб почати занурюватися у світ ігрового програмування на основі фізики. В основному м'яч має рух відскоку при ударі по стіні. Як хтось із припущених вище, кут падіння дорівнює куту відбиття. Але коли ви розглядаєте, як м'яч бив весло. Логіка поділена на 3 розділи. 1.) М'яч, що вдаряється про центральну частину весла. 2.) М'яч, що вдаряється про ліву частину весла. 3.) М'яч, що вдаряється в праве положення весла.

Якщо врахувати центральну частину: Вам не потрібно відрізняти ефект відскоку від того, який застосовується при ударі по м'ячу. М'яч просто відхиляється нормально. Але, коли вдаряється будь-який напрямок, справа відрізняється.

Коли м'яч вдаряється по лівій стороні, тобто вважайте, що м'яч йде з лівої сторони екрана, і ви йдете з лопаткою з правого боку. Тоді, коли ви вдаряєте м'яч лівою частиною, куля повинна відображатись у напрямку, з якого вона вийшла .ie.leftwards, під тим же кутом, з якого вийшла. Те саме відбувається і навпаки. У правій частині також застосовується те саме.

Цей рух м'яча в напрямку вліво або вправо при його ударі робить його більш правдоподібним.

Сподіваюся, у вас виникла ідея, принаймні, логічно розумна. Дякую


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

це допоможе http://www-classes.usc.edu/engr/ee-s/477p/s00/pong.html ви просто інвертуєте x і y залежно від місця потрапляння на екран. якщо вона піднімається вгору і потрапляє на верхню частину, вона інвертує швидкість y, зберігаючи швидкість x


0

Привіт. Нещодавно я нещодавно намагався зробити гру з м'ячем і придумав рішення для цього. Отже, що я зробив: весло рухається, поки ми граємо в гру. Моя система координат залишилася такою, якою вона є, верхня ліва точка полотна - 0,0. Лопатка рухається в цій системі координат. Вісь x вказує від 0 до ширини полотна, а вісь y вказує на 0 до висоти полотна. Я створив весло з розміром фіксації 100 шириною та 20 висотою. І тоді я малюю навколо нього уявне коло. Коли м'яч потрапляє в весло, я обчислюю центральну точку весла

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

Тоді я віднімаю центр від поточного положення кулі таким чином система координат буде в центрі весла, ballCenter - це точка, куля куля потрапила в лопатку (- (ширина paddlewry + r) .. 0 .. (paddlewwith + r )) це не що інше, як змінити точку удару на весла

double x0 = ballCenterX-paddleCenter;

обчислити точку перетину кола за допомогою точки влучення кулі (x0). Це перерахунок, ми просимо координати y на колі з уже відомою координатою x0, і потрібен був фліп для осі y

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

обчислити похідне нормального рівняння кола, яке визначається навколо весла з радісним 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

написана в яві з затемненням. Існує ще одне рішення для цього, прокоментованого в Ball.java, де масштабування не відбудеться, я не переміщую систему координат координат до центральної точки весла, замість цього я обчислюю все вищезгадане з системи координат топлефта 0,0 відносно центральна точка весла Це також працює.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.