Помощь - Поиск - Пользователи - Календарь
Полная версия: Супер бильярд
Форум «Всё о Паскале» > Pascal, Object Pascal > Написание игр
VelarThind
Решил тут я бильярд на паскале написать. Ну кий там, шарик ведущий сделал, всё пока работает(кий толкает шарик, он катиться, замедляет ход), но необходимо и другие шары в игру(какой же это будет бильярд с одним шариком), а вот соударение шариков друг с другом, что-то не осилил. Подскажите, пожалуйста!!!! Заранее спасибо.
Altair
Вот этот пример думаю будет полезен. Код очень хорошо прокоментирован!
По экрану катаются шарики и сталкиваются.
Эмитация броуновского движения.
Код

Uses Graph,Crt;

const MaxShar=10;
type shar = object
 { переменные }
 x,y:integer; { текущие координаты шара }
 r:word;      { радиус }
 color:byte;  { цвет шара }
 dx,dy:shortint; { скорость. задается значениями
                   перемещения на каждом шаге (в точках) }
 hits:set of byte; { множество шаров с которыми данный шар уже
 столкнулся и с которыми не надо обсчитывать столкновение еще раз }
 { методы }
 constructor initShar(x0,y0:integer; r0:word; color0:byte; dx0,dy0:shortint);
 procedure Move; { двигаем }
 procedure Show; { показываем }
 procedure Hide; { прячем }
 procedure CheckBorder; { проверяем на выход за границы экрана }
 procedure CheckHit(k:byte); { проверяем на столкновение с другим шаром }
 procedure revertXDirection; { поменять Х/Y-составляющую скорости }
 procedure revertYDirection; { на противоположную }
 procedure TurnAfterHit(k:byte); { вычислить новые значения
              скорости двух шаров после удара. Первый шар - шар
              данного объекта, второй - с индексом k в массиве }
 function Value:real; { вычислить объем шара. Нужно для вычисления
      скорости. Вообще-то там масса нужна, но учитывая, что плотность
      все-равно сократиться при делении, используем объем }
 {procedure PrintState;} { отладочные печати }
 end;
var bgColor,i:byte;
   x,y,dx,dy,ErrCode:integer;
   r:word;
   shars: array[1..MaxShar] of shar; { массив шаров }
   sh:shar;
   f:text; { файл для отладочных печатей }
Procedure shar.Move;
 begin
 x:=x+dx;
 y:=y+dy;
 end;
Procedure shar.Show;
 begin
 setColor(color);
 circle(x, y, r);
 {PutPixel(x,y,color);} { уберите комментарий чтобы увидеть траекторию }
 end;
Procedure shar.Hide;
 begin
 setColor(bgColor);
 circle(x, y, r);
 end;
procedure shar.revertXDirection;
 begin
 dx:=-dx;
 x:=x+dx;
 end;
procedure shar.revertYDirection;
 begin
 dy:=-dy;
 y:=y+dy;
 end;
Constructor shar.initShar(x0, y0 :integer; r0 :word; color0 :byte; dx0, dy0 :shortint);
 begin
 x:=x0;
 y:=y0;
 r:=r0;
 color:=color0;
 dx:=dx0;
 dy:=dy0;
 hits:=[];
 end;
procedure shar.CheckBorder;
 begin
 if (r+x>=GetMaxX-1) or (x-r<=1) then revertXDirection;
 if (y+r>=GetMaxY-1) or (y-r<=1) then revertYDirection
 end;
{procedure shar.PrintState;
 begin
 writeln(f,'x=',x:3,' y=',y:3,' r=',r:3,' dx=',dx:3,' dy=',dy:3);
 end;}
procedure shar.TurnAfterHit(k:byte);
{ формулы для движения шаров взяты здесь:
 http://ferro.phys.msu.ru/prak/PDF/01-mechanics/07.pdf }
var m1, m2 :real;
   vx10,vy10,vx20,vy20:integer;
 begin
 m1 := Value;            { массы }
 m2 := shars[k].Value;
 vx10 := dx;             { начальные значения скоростей }
 vy10 := dy;
 vx20 := shars[k].dx;
 vy20 := shars[k].dy;
 { скорость первого шара после удара }
 dx := round((2*m2*vx20 + (m1-m2)*vx10)/(m1+m2));
 dy := round((2*m2*vy20 + (m1-m2)*vy10)/(m1+m2));
 { скорость второго шара после удара }
 shars[k].dx := round((2*m1*vx10 + (m2-m1)*vx20)/(m1+m2));
 shars[k].dy := round((2*m1*vy10 + (m2-m1)*vy20)/(m1+m2));
 end;
function shar.Value:real;
 begin { возвращает объем шара }
 Value:=4*Pi*r*r*r/3;
 end;
procedure shar.CheckHit(k:byte);
var i1:byte;
   dist:longint;
 begin
 for i1:=1 to MaxShar do if ((i1<>k) and not(k in hits)) then
   begin
   shars[i1].hits:=shars[i1].hits+[k];
   dist:=round(sqrt(sqr(1.0*x-shars[i1].x)+sqr(1.0*y-shars[i1].y)));
   if (dist<r+shars[i1].r) then
     begin
     TurnAfterHit(i1);

     CheckBorder;
     Move;
     shars[i1].CheckBorder;
     shars[i1].Move;

     { скорость задается значениеми dx и dy - шагами движения. Таким
     образом она дискретна. Момент столкновения определяется по
     расстоянию между шарами. Но запросто может случиться, что шары
     на каком-то шаге пересекутся и в этом случае необходимо их
     развести в разные стороны перед тем как высчитывать расстояние на
     следуещем шаге, иначе они могут пребывать в состоянии постоянного
     столкновения }
     dist:=round(sqrt(sqr(1.0*x-shars[i1].x)+sqr(1.0*y-shars[i1].y)));
     while (dist<r+shars[i1].r) do
       begin
       CheckBorder;
       Move;
       shars[i1].CheckBorder;
       shars[i1].Move;
       dist:=round(sqrt(sqr(1.0*x-shars[i1].x)+sqr(1.0*y-shars[i1].y)));
       end;
     end;
   end;
 end;

function initializeGraph:integer;
var grDriver : Integer;
     grMode : Integer;
 begin
 grDriver:=Detect;
 InitGraph(grDriver, grMode, '');
 initializeGraph:=GraphResult;
 end;

begin
ErrCode:=initializeGraph;
if ErrCode <> grOk then
  WriteLn('Ошибка инициализации графики:', GraphErrorMsg(ErrCode))
else
 begin
 assign(f,'log.txt');
 rewrite(f);
 bgColor:=0;
 rectangle(1,1,GetMaxX-1,GetMaxY-1);

 {shars[1].initShar(100,100,50,3,6,-3);
 shars[2].initShar(100,200,40,4,5,3);
 shars[3].initShar(200,200,20,5,-7,-4);
 shars[4].initShar(400,300,25,2,-9,-1);
 shars[5].initShar(500,200,35,14,-5,-8);
 if (MaxShar>5) then}

 { инициализация шаров случайным образом }
 for i:=1 to MaxShar do
   begin
   r:=20+random(10);
   shars[i].initShar(1+r+random(GetMaxX-2*r),
     1+r+random(GetMaxY-2*r),r,1+random(GetMaxColor-1),
     random(15)-7,random(12)-6);
   end;
 { обсчитываем движение пока не нажата какая-либо клавиша }
 repeat
   { показываем все шары }
   for i:=1 to MaxShar do
     shars[i].Show;
   { временная задержка - установите свое значение для вашего компьютера }
   Delay(580);
   { убираем с экрана все шары }
   for i:=1 to MaxShar do
     shars[i].Hide;
   { просчитываем следующий шаг }
   for i:=1 to MaxShar do
     begin
     shars[i].Move;
     shars[i].CheckBorder;
     shars[i].CheckHit(i);
     end;
   for i:=1 to MaxShar do
     shars[i].hits:=[];
 until keyPressed;
 CloseGraph;
 close(f);
 end
end.

mithquessir
Светлик, вот нашел pdfник описания какой-то лабораторной изучения закона сохранения импульса, там есть формулы...
AlienEmperor
А почему бы не представить игровое поле в виде массива (матрица X * Y из нулей, шарик - единица)... У каждого шарика перед движением просчитывается точка, в которой он должен оказаться... Если эта точка занята другим шаром, то меняем направление, если нет, то стираем единицу в предыдущем поле и переставляем шар на новое (двигаем шар)... Дешево и сердито...
Бродяжник
С матричным полем возможны такие траблы:
1. Шары не точечные. Поэтому если мы разобьем поле на клетки и будем просчитывать только нахождение шара в клетке, нам придется траекторию движения шара значительно загрублять. А если шар будет занимать не одну, а несколько точек, то есть представлять собой растр... не замучаемся ли мы с этим?
2. Как быть с изменением скорости? Если один шар движется вдвое быстрее другого, то значит ли это, что в течение того времени, когда более быстрый шар переходит с одной точки на соседнюю, более медленный шар должен тупо стоять на месте?
3. Допустим, два шара с одинаковой скоростью катятся навстречу друг другу. Перед очередным просчетом они находятся в двух соседних клетках. Если мы перед началом движения просчитаем будущее местоположение обоих шаров, то окажется, что они попадут в разные клетки, то есть столкновения как бы и не было.
По-моему, проще все-таки вычислять расстояния между центрами шаров.
Извините, что встряю, но проблема просчета коллизий меня самого всегда интриговала. Я ее так и не решил.
AlienEmperor
На самом деле я как раз недавно скидывал альфу игры (в теме "тест движка 2") в котором все столкновения (а точнее, проверка соприкосновения с пов-тью и т.д.) проводятся именно через матрицу. Никаких проблем!
Atos
4. А если столкнутся больше двух шаров? smile.gif

Сейчас юзаем Winapi на C++ angry.gif За пару надо написать и сдать несложную прогу. Очередная была - сымитировать движение молекул. Вроде бы всё уже отладил, но когда препод стал тестировать, три молекулы столкнулись в центре экрана .. и "слиплись" :D Правда, потом на них налетела четвёртая, и исправила дело.
Бродяжник
AlienEmperor
Поделитесь алгоритмом - как нужно обрабатывать следующую ситуацию?
Поле:
Цитата
000
102
000

Объект "1" движется вправо, объект "2" - влево. Через такт движения они оба должны были бы оказаться в точке по центру. Но по правилам игры вместо этого должно произойти взаимное отталкивание. Какой должна быть последовательность проверок? Я не говорю, что этого сделать нельзя; я просто не знаю как, и хочу узнать. И что изменится, если поле будет выглядеть так (направление движения прежнее):
Цитата
000
012
000
VelarThind
Тут ещё какой вопрос. Нужно то не просто их оттолкнуть, да ещё под разными углами. То есть решить как минимум пару сотен задачь. Ведь при разных соотнашениях скоростей и траекторий, разных точках соприкосновения они поползут в разные в определённые стороны. Вот в этом проблема. Конечно можно решить столько задочь. Потом их както сгрупировать, а в зависимости от условий применять, но что-то не хочется. Ведь задачки кик минимум на час, а то и полтора решения.
AlienEmperor
Цитата
Через такт движения они оба должны были бы оказаться в точке по центру. Но по правилам игры вместо этого должно произойти взаимное отталкивание. Какой должна быть последовательность проверок?


На самом деле, в данном случае, придется немного попариться... Вообще решать данную проблему можно так:
а) через матрицу (только единица матрицы по размерам должна быть МЕНЬШЕ размеров шара, т.е. шар включает в себя несколько единиц), если поле достаточно большое, то можно работать с битами, в результате чего размер матрицы сократится в 8 раз... Чем больше единиц будет в шаре, тем менее заметно будет вхождение друг в друга при столкновении...
б) проверять координаты всех шаров, учитывая их радиус (или проще: вписать шары в квадрат, но при этом загрубление... (хотя можно сделать так: вписать шары в квадрат, а при проверке столкновений, в случае вхождения одного в другой проверять чем-нибудь вроде pixelcheck'а)). В данном случае лучше всего шары представлять в виде массива (как это сделано в примере).

Мне вот другое интересно... Все это хорошо, я думаю, если посидеть немного, то можно сделать приемлемо... А вот хотел бы я на алгоритм Lemming'ов посмотреть... smile.gif

Ладно, пойду-ка я к лекции готовиться...

P.S.
Цитата
Объект "1" движется вправо, объект "2" - влево. Через такт движения они оба должны были бы оказаться в точке по центру. Но по правилам игры вместо этого должно произойти взаимное отталкивание.


Я вообще не понимаю, в чем проблема ?
000
102
000
Ну хорошо, предположим... Но шары-то двигаются НЕ ОДНОВРЕМЕННО - это же иллюзия... Тот шар, который в цикле имеет высший приоритет, просто запишет единицу в данную клетку ПЕРВЫМ, а второй, при проверке "свободна ли клетка дальше" получит -"нет" и, соответственно, должен будет оттолкнуться (а вот для того, чтобы и первый шар снизил скорость и поменял траекторию, надо чтобы второй "сообщил" ему об этом (я бы использовал массив указателей на шары, правда, при таком раскладе нужно еще идентифицировать нужный шар, но это тоже можно сделать). Вообще, такие штуки под DelphiX за пол-часа пишутся... smile.gif, но это халява. smile.gif
volvo
Цитата(AlienEmperor @ 13.04.05 10:15)
Вообще, такие штуки под DelphiX за пол-часа пишутся... smile.gif, но это халява. smile.gif

Ребята, вы вопрос внимательно читали?
Цитата(VelarThind @ 11.04.05 19:38)
Решил тут я бильярд на паскале написать.

Мало ли что под DelphiX за полчаса пишется. Может, используя другие пакеты, можно это одним вызовом функции реализовать. Вся проблема в том, что человек хочет сделать это самостоятельно.
AlienEmperor
Volvo, ну так я же не спорю... И вариант решения предлагаю по мере возможностей в виде алгоритма... Насчет DelphiX это я так, к слову... Хотя почему бы не ознакомиться с его исходным кодом и не попробовать его части использовать в 16-битном Пасе ?
VelarThind
Олег, спасибо за код, как раз то что нужно. Осталось только разобрать на куски и в мои исходники вставить.
Shura
Процедура TurnAfterHit неправильная!!!. Я писал на днях такую же прогу, тока ещё сила тяжести действует и потеря энергии. Так я тоже сначала просто менял составляющие скоростей шариков. Ну вот пример: первый шарик изначально покоится, а второй висит над ним, но не ровно над ним, а со смещением, скажем в пол-радиуса. После соударения они должны коряво в разные скорости разлететься, а с использованием той процедуры первый шарик просто прыгает вверх-вниз, а второй так вообще не двигаетса - беспредел! Уже второй день в голове крутится... Мне кажется, что надо посчитать угол между осью абсцисс и касательной к шарикам в точке соударения и как-то его использовать. Вот код. В чём ошибка?

Var
x1, y1, x2, y2: Real; {координаты центров}
r1, r2: Integer; {радиусы}
vx1, vy1, vx2, vy2: Real; {составляющие скоростей}

Procedure Bum;
var
l: Real; {Тот самый угол}
vx, vy: Real; {Дополнительные переменные}
begin
l:=Arctan((y2-y1)/(x2-x1)) - PI/2;
vx:=vx1;
vy:=vx2;
vx1:=(vx2+vy2)*cos(l);
vy1:=(vx2+vy2)*sin(l);
vx2:=(vx+vy)*cos(l+PI);
vy2:=(vx+vy)*sin(l+PI)
{то, что ось Y в Паскале вниз направлена, я не учитывал}
end;
Altair
Цитата
Процедура TurnAfterHit неправильная!!!.

А в чем заключается неправильность?
Втом, что не учитывается гравитация? smile.gif
или то, что отскок шариков не в точности с физическими законами?
Это неточность физической модели описанной алгоритмом, а не ошибка алгоритма.
Программаработает, и создается впечателение правильного отскакивания ...
Что еще нужно?

p.s. все программы корыея выкладываю, проверенны....
Shura
Цитата(Oleg_Z @ 22.04.05 21:11)
А в чем заключается неправильность?

Ну я ж привёл пример. Вот когда они в воздухе сталкиваются, то кажется, что правильно. А вот в крайних случаях... Не может же быть, чтобы не учитывались координаты точки соударения относительно координат самих шариков?
Altair
Если не нравится, исправь!
Shura
Цитата(Oleg_Z @ 22.04.05 21:26)
Если не нравится, исправь!

Хех, так если я б знал как, я бы не спрашивал - я же помощи прошу!
Altair
а понял smile.gif
насчет неправильности...
l:=Arctan((y2-y1)/(x2-x1)) - PI/2;

уверен?
что-то оущениечто надо перевоить в грудусы...потмоу что возвращается в радианах...
Shura
Да не - нам то в радианах потом и нужно будет. Это "l" для касательной, а надо для прямой соединяющей коорд. первого шарика и точки касания - т.е. надо без "- PI/2". Но дело не в этом, всё таки в самих формулах скоростей чо-то... Может откомпилируешь тот код вместе с моими формулами, и если надо, изменишь что-то - всё-тки посмотреть на это надо (попробуешь тот случай, что я описывал, проверить).
-Aleks-
Цитата(Shura @ 22.04.2005 20:58) *

Да не - нам то в радианах потом и нужно будет. Это "l" для касательной, а надо для прямой соединяющей коорд. первого шарика и точки касания - т.е. надо без "- PI/2". Но дело не в этом, всё таки в самих формулах скоростей чо-то... Может откомпилируешь тот код вместе с моими формулами, и если надо, изменишь что-то - всё-тки посмотреть на это надо (попробуешь тот случай, что я описывал, проверить).

Можно эту програму для lazarys переделать?
Archon
Дак она и так под lazarus работает не плохо. Что переделывать-то?
TarasBer
Для шаров одной массы при идеально упругом столкновении всё весьма просто:
При столкновении двух шаров у нас есть линия центров и общая касательная, они взаимно перпендикулярны. Надо импульсы спроецировать на эти линии. Тогда проекции на касательную не изменятся, а проекции на линию центров обменяются друг с другом.
То есть если есть векторы скорости v1 и v2, и есть вектора x и y, на которые мы проецируем, то тогда
v1 = x<x,v1>/sqrx + y<y,v1>/sqry
v2 = x<x,v2>/sqrx + y<y,v2>/sqry

После удара:

v1 = x<x,v1>/sqrx + y<y,v2>/sqry
v2 = x<x,v2>/sqrx + y<y,v1>/sqry
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.