Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Столкновения прямоугольников. [D7, WinXP]


Страницы:   1 2

IPranker ©   (2011-06-05 20:16:00) 

Всем привет! :)

Вот никак не могу написАть програмку столкновения 2-х прямоугольников. Т.е. передвигаем один прямоугольник, а как
столкнулись с другим (преграда), то не даем ему проехать сквозь него. Наш прямоугольник должен в него какбы упереться.

P.S. Можете "протестить" данный код, надо только положить на форму TTimer с интервалом в 1.
P.S.S. Это нужно для скромной игры.


var
 FR    : TRect;                     // Местоположение преграды.
 FX, FY: LongWord;                  // Наши координаты.
 FW, FH: LongWord;                  // Наши ширина и высота.
 FKeys : array [0..255] of boolean; // Массив клавиш клавиатуры.

function KDown(Key: byte): boolean;  // Проверка нажатой клавиши.
begin
 Result:= FKeys[Key];
end;

procedure ObjectMove(OffSet: LongWord); // Передвигаемся.
begin
 If KDown(VK_UP)    then Dec(FY, OffSet) else
 If KDown(VK_DOWN)  then Inc(FY, OffSet) else
 If KDown(VK_LEFT)  then Dec(FX, OffSet) else
 If KDown(VK_RIGHT) then Inc(FX, OffSet);
end;

function IsCollide: boolean;            // Функция проверяет пересечение с преградой.
var
 R: TRect;
begin
 Result:= InterSectRect(R, Rect(FX, FY, FX + FW, FY + FH), FR);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
 ObjectMove(10);   // Двигаемся со скоростью в 10 единиц.

 If IsCollide then // Проверяем столкновение с преградой.
 begin
   Caption:= '1';

 end else
   Caption:= '0';

 Invalidate;
end;

procedure TForm1.FormPaint(Sender: TObject);
begin
 // Очищаем фон.
 Canvas.Brush.Color:= clWhite;
 Canvas.FillRect(Canvas.ClipRect);

 // Рисуем наш прямоугольльник.
 Canvas.Brush.Color:= clRed;
 Canvas.Rectangle(Rect(FX, FY, FX + FW, FY + FH));

 // Рисуем преграду.
 Canvas.Brush.Color:= clBlue;
 Canvas.Rectangle(FR);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 // Задаём наше местоположение и размеры.
 FX:= ClientWidth  div 2;
 FY:= ClientHeight div 2;
 FW:= 50;
 FH:= 50;

 // Задаём местоположение и размеры преграды.
 SetRect(FR, 100, 100, 356, 567);

 DoubleBuffered:= True;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
 FKeys[Key]:= True;
end;

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
 FKeys[Key]:= False;
end;


sniknik   (2011-06-15 10:02:00) [1]

> Но зная уравнение движения можно легко определить точку пересечения.
не нужно знать, у него там все уже рассчитано, есть "прямоугольник пересечения", крайняя грань которого, по ходу движения и есть точка до которой можно "безболезненно прыгнуть".


Dimka Maslov   (2011-06-15 09:49:00) [2]


> Servy ©   (15.06.11 04:22) [35]

Это называется "метод половинного деления". Но зная уравнение движения можно легко определить точку пересечения. На любом шаге.


Servy   (2011-06-15 04:22:00) [3]


> Но он упирается в нескольких пикселях от преграды, а должен
> ПЛОТНО упираться.


> А если прямоугольников несколько, то как правильно проверять
> и относительно каких сторон. Вдруг наш прямоугольник столкнулся
> с неколькими преградами одновременно. Что тогда?


Есть предложение решать данную задачу "в лоб":

1) Пробуем сдвинуться на 10. Если обнаружено столкновение, то оно, очевидно, происходит где-то в промежутке между сдвигом на 0 и сдвигом на 10, для краткости обозначим такой отрезок [0, 10].
2) Если имеется столкновение с любым из препятствий, откатываем координаты назад и пробуем сдвинуться на 5.
3) Если все еще имеется столкновение с любым из препятствий, значит точка пересечения находится в отрезке [0, 5]. Соответственно, пробуем 2.5 и снова сужаем отрезок вдвое. Если же при сдвиге на 5 столкновение исчезло, то новый отрезок [5, 10] и стоит пробовать сдвиг на 7.5.
4) Так и ищем точку пересечения методом деления пополам пока не достигнем требуемой точности.

Если требуется решение с целыми координатами, можно тупо перебрать все возможные сдвиги от 0 до 10 и найти на каком начинает появляться столкновение и использовать предыдущий.

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


Inovet   (2011-06-13 11:42:00) [4]

> [30] IPranker ©   (11.06.11 20:08)
> Вот смотрите, передвигаемся со скоростью 4 единицы, отъезжаем с 2 к примеру.
> Наш прямоугольник в таком случаем будет проходишь все равно сквозь преграду.

Как он будет проходить, когда ты решаешь уравнение движения для t=n и t=n+d которое не имеет решений за краницей преграды.


sniknik   (2011-06-12 09:52:00) [5]

> Ну чисто теоретически (при дискретном движении) такое возможно по трем направлениям. Так что учесть всё-таки нужно.
направление движения в коде одно, столкновение тоже будет одно, то что из-за дискретности его можно "перепрыгнуть" и "вляпаться" еще во что-то, ничего не значит, зачем их учитывать если значимо, и останавливаться нужно перед первым же?


Германн   (2011-06-12 01:51:00) [6]


> > Вдруг наш прямоугольник столкнулся с неколькими преградами
> одновременно. Что тогда?
> петь "алилуя" и славить господа за чудо... т.к. твой код
> допускает движение только в одном направлении, и значит
> пересекаться может только с 1 преградой за раз.

Ну чисто теоретически (при дискретном движении) такое возможно по трем направлениям. Так что учесть всё-таки нужно.
Но сам топик меня весьма смешит. :)


sniknik   (2011-06-11 23:24:00) [7]

> Спасибо, я это уже понял. :)
и как так получается "уже" понимать только после того как разжаловано...

> Вот смотрите ...
ну блин, прям теорема ферма в программировании... два прямоугольника рядом нарисовать после срабатывания какого то условия.

> Просьба показать и объяснить мне на примере.
это ты так свой код "написал" кто то "объяснил" на примере... не до конца. теперь еще "примеры" нужны т.к. сам даже элементарное как 2+2 осилить не можешь.

> А если прямоугольников несколько
по очереди.

> то как правильно проверять
правильно, будет так как будет работать.  

> Вдруг наш прямоугольник столкнулся с неколькими преградами одновременно. Что тогда?
петь "алилуя" и славить господа за чудо... т.к. твой код допускает движение только в одном направлении, и значит пересекаться может только с 1 преградой за раз. 2 -чудо, 3 - двойное чудо.
как же ты этого не знаешь если "сам писал"?

> Все расчеты идут до процедуры оттображения Инвалидат.
и что? все одно рывки, дискретность... трудно представить "подъезд" к препятствию с уменьшенным интервалом чтобы он был равномерным?
хотя, имхо, лишнее, т.к. не настолько заметно визуально, при заметном усложнении логики, усилий на выполнение.


IPranker   (2011-06-11 20:08:00) [8]


> KSergey ©   (08.06.11 12:33) [28]


Спасибо, я это уже понял. :)
Вот смотрите, передвигаемся со скоростью 4 единицы, отъезжаем с 2 к примеру.
Наш прямоугольник в таком случаем будет проходишь все равно сквозь преграду.

Просьба показать и объяснить мне на примере.


> если врезались, то положение красного нужно указывать относительно
> синего с учетом его ширины и высоты, т.е. впритык.


А если прямоугольников несколько, то как правильно проверять и относительно каких сторон. Вдруг наш прямоугольник столкнулся с неколькими преградами одновременно. Что тогда?


> Для гладкости рендеринга граничные состояния надо предсказывать,
>  то есть считать на шаг вперед...


Все расчеты идут до процедуры оттображения Инвалидат.


Inovet   (2011-06-08 12:40:00) [9]

> [28] KSergey ©   (08.06.11 12:33)
> и получается. что "не можем приблизиться вплотную"

И не надо. Есть интервал времени, есть скорость, куда тут воткнёшь полное сближение, всё равно будет усреднение. Только интервал времени уменьшить для плавности.


KSergey   (2011-06-08 12:33:00) [10]

IPranker, тебе вот что пытаются втолковать, если на пальцах.
Я понял! :)

Между прямоугольниками, например, 5 точек.
Пытаемся сдвинуться на 10
Видим - о, пересекаются - и отпрыгиваем назад на 10.
Но на 5 ведь можно было пододвинуться!
Но т.к. на 5 точек можно было подвинуться (т.е. есть зазор в 5 точек) - то и получается. что "не можем приблизиться вплотную". Т.к. чтобы упереться - надо на 5 точек переместиться.


han_malign   (2011-06-07 10:03:00) [11]


> Переместились на 10 единиц, если врезались, то обратно перемещаемся
> на 10 единиц.

- а если пересеклись, смотрим на сколько "единиц" надо уменьшить последний шаг, перерассчитываем граничное состояние...

Для гладкости рендеринга граничные состояния надо предсказывать, то есть считать на шаг вперед...


>  Case FDir of
>    dirUp   : Dec(FY, OffSet);
>    dirDown : Inc(FY, OffSet);
>    dirLeft : Dec(FX, OffSet);
>    dirRight: Inc(FX, OffSet);
>  end;

- вообще-то, достаточно вектора скорости (vx, vy), при "столкновении" менять знак нужной компоненты...
inc(FY, vy); inc(FX, vx);
или вульгарный OffsetRect...


MonoLife   (2011-06-07 04:48:00) [12]


> Переместились на 10 единиц, если врезались, то обратно перемещаемся
> на 10 единиц. Вроде бы всё логично.

лучше, имхо, не так
если врезались, то положение красного нужно указывать относительно синего с учетом его ширины и высоты, т.е. впритык.


Dimka Maslov   (2011-06-06 21:41:00) [13]


> это же не стенка, а горизонт событий чёрной дыры.


Причём сам прямоугольник находится внутри. Чтож, современная физика такое допускает.


Inovet   (2011-06-06 20:39:00) [14]

> [19] Dimka Maslov ©   (06.06.11 20:03)
> У нас же релятивистская задача, как я понимаю.

А, так вот почему он никак не может долететь до стенки - это же не стенка, а горизонт событий чёрной дыры.


IPranker   (2011-06-06 20:24:00) [15]


> или сделай шаг = 1,


:)


> или меняй логику на подгонку (что тоже
> не составит труда писавшему код, потому как - делов то...)


Понять не могу, хоть убей. :)

Порядок действий такой:
Переместились на 10 единиц, если врезались, то обратно перемещаемся на 10 единиц. Вроде бы всё логично.
Но работает не так как надо.


sniknik   (2011-06-06 20:15:00) [16]

> Может быть порядок действий в Таймере не правильный?
правильный... для той логики которая реализована, а не прилегает потому что дискретность... шаг выбран такой, что есть промежуток между двумя граничными состояниями.

или сделай шаг = 1, или меняй логику на подгонку (что тоже не составит труда писавшему код, потому как - делов то...)


IPranker   (2011-06-06 20:13:00) [17]

4 направления движения - вверх, вниз, влево, вправо. :)


IPranker   (2011-06-06 20:05:00) [18]


> Очень интересно в каких это 4-х направлениях. В плоскости
> у него 3 степени свободы.


Имелось ввиду 4 направления движения.


> ObjectMove(-10);


Блин, я туплю!


> + переменные функции из глобальных сделал объектными


У меня привычка глобальные переменные объявлять как приватные поля класса. Их по префиксу Ф видно. :)


> переменные "россыпью" свел бы в рект


Ректы для статичных прямоугольников-преград.
А для своего сделал россыпью, чтобы легче манипулировать с координатами.
Хотя да, есть же функция OffsetRect...


> Врёт он, что писал.


Я писАл. Честно.
Просто туплю чё-та. Извините.


> и еще небось удивляется "а чего не помогают, код же привел,
>  сделал вид что сам что-то делал"... ага, счаз.


Столкновение вроде как сделал.
Но столкновения то не плотно прилегают какбы.
Т.е. прямоугольник врезается в нескольких пикселях от преграды.

Может быть порядок действий в Таймере не правильный?


Dimka Maslov   (2011-06-06 20:03:00) [19]


> Очень интересно в каких это 4-х направлениях. В плоскости
> у него 3 степени свободы.


В пространстве-времени на плоскости как раз 4 степени свободы. У нас же релятивистская задача, как я понимаю.


sniknik   (2011-06-06 19:51:00) [20]

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

и еще небось удивляется "а чего не помогают, код же привел, сделал вид что сам что-то делал"... ага, счаз.


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо