![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Столкновения прямоугольников. [D7, WinXP]
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 прибавить... да ни в жисть.
и еще небось удивляется "а чего не помогают, код же привел, сделал вид что сам что-то делал"... ага, счаз.