хостинг и регистрация доменов
 
Навигация
Delphi
Sail Engine
Полезные ресурсы

iSADA team  Pascal Games
Проверка столкновений
В процессе разработки графических приложений, очень часто возникает необходимость, определять столкновения (коллизии) с различными геметрическими фигурами. Ниже рассмотрены некоторые функции, которые реализуют поставленную задачу. Итак, поехали.

Для начала определим структуру TVector:
Код на Delphi:
001.
002.
003.
004.
type
  TVector = record
    X, Y, Z: Single;
  end;

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

Код на Delphi:
001.
002.
003.
004.
005.
006.
function CubeVsPoint(Box, Point: TVector; Size: Single): Boolean;
Begin
  if (abs(Point.x - Box.x)< Size) and
     (abs(Point.y - Box.y)< Size) and
     (abs(Point.z - Box.z)< Size) then Result := True else Result := False;
end;

Немного изменив код, получаем коллизию точки с ящиком (Вариант 1):
Код на Delphi:
001.
002.
003.
004.
005.
006.
function BoxVsPoint(Box ,BoxSize, Point: TVector): Boolean;
begin
  if (abs(Point.x - Box.x)< BoxSize.x) and
     (abs(Point.y - Box.y)< BoxSize.y) and
     (abs(Point.z - Box.z)< BoxSize.z) then Result := True else Result := False;
end;

Проверка столкновения ящика с точкой (Вариант 2):

Код на Delphi:
001.
002.
003.
004.
005.
006.
function BoxVsPoint2(Min_X, Max_X, Pos: TVector): Boolean;
begin
if (Pos.X >= Min_X.X) and (Pos.X <= Max_X.X) and
   (Pos.y >= Min_X.y) and (Pos.y <= Max_X.y) and
   (Pos.z >= Min_X.z) and (Pos.z <= Max_X.z) then Result := True else Result := False;
end;

Определение столкновений двух ящиков с разными размерами:

Код на Delphi:
001.
002.
003.
004.
005.
006.
007.
008.
009.
010.
011.
function Box1VsBox2(Box1, Box2, Box1Size, Box2Size: TVector): Boolean;
begin
 if (box1.X + box1size.X < Box2.X) or
    (box1.y + box1size.y < Box2.y) or
    (box1.z + box1size.z < Box2.z) or
 
    (Box2.x + box2size.X < box1.X) or
    (Box2.y + box2size.y < box1.y) or
    (Box2.z + box2size.z < box1.z) then
     Result:=false else result:=true;
end;

Столкновение ящика со сферой:

Код на Delphi:
001.
002.
003.
004.
005.
006.
007.
008.
009.
010.
011.
012.
013.
014.
015.
016.
017.
018.
019.
020.
021.
022.
023.
024.
025.
026.
function BoxVsSphere(Min_X, Max_X, Pos: TVector; R: Single): Boolean;
var
  d: Single;
begin
  d := 0;
 
  // если центр сферы лежит перед AABB,
  if (Pos.X < Min_X.X) then
    // то вычисляем расстояние по этой оси
    d := d+ abs(Pos.X - Min_X.X);
 
  // если центр сферы лежит после AABB,
  if (Pos.X > Max_X.X) then
    // то вычисляем расстояние по этой оси
    d := d+ abs(Pos.X - Max_X.X);
 
(******************************************************************************)
  if (Pos.Y < Min_X.Y) then d := d + abs(Pos.Y - Min_X.Y);
  if (pos.Y > Max_X.Y) then d := d + abs(Pos.Y - Max_X.Y);
(******************************************************************************)
  if (Pos.Z < Min_X.Z) then d := d + abs(Pos.Z - Min_X.Z);
  if (pos.Z > Max_X.Z) then d := d + abs(Pos.Z - Max_X.Z);
(******************************************************************************)
 
  Result := d  <= ( R);
end;

Проверка столкновений отрезка с кубом:

Код на Delphi:
001.
002.
003.
004.
005.
006.
007.
008.
009.
010.
011.
012.
013.
014.
015.
016.
017.
018.
019.
020.
021.
022.
023.
024.
025.
026.
027.
028.
029.
030.
031.
032.
033.
034.
035.
036.
037.
038.
039.
040.
041.
042.
043.
044.
045.
046.
047.
048.
049.
050.
051.
052.
053.
054.
055.
function CubeVsLine(BP, LBEGIN, LEND: TVector; BS :single): Boolean;
Var
  MID,
  DIR,
  T   : TVector;
  HL,
  R   : Single;
 
begin
  // Получаем центр
  Mid.x := lbegin.x+(lend.x-lbegin.x) * 0.5;
  Mid.y := lbegin.y+(lend.y-lbegin.y) * 0.5;
  Mid.z := lbegin.z+(lend.z-lbegin.z) * 0.5;
 
  // Получаем направление
  dir.x := (lend.x-lbegin.x);
  dir.y := (lend.y-lbegin.y);
  dir.z := (lend.z-lbegin.z);
 
  // Получаем длину
  hl := sqrt(sqr(dir.x)+sqr(dir.y)+sqr(dir.z));
  // Нормализуем её
  if hl <> 0 then
  begin
    dir.x := dir.x / hl;
    dir.y := dir.y / hl;
    dir.z := dir.z / hl;
 
    hl    := hl * 0.5;
  end;
 
  // Получаем позицию куба относительно середины линии
  t.x := BP.x -mid.x;
  t.y := BP.y -mid.y;
  t.z := BP.z -mid.z;
 
  // проверяем, является ли одна из осей X,Y,Z разделяющей
  if ( (abs(T.x) > BS + hl * abs(dir.x)) or
       (abs(T.y) > BS + hl * abs(dir.y)) or
       (abs(T.z) > BS + hl * abs(dir.z)) ) then begin Result := False ; Exit; end;
 
  // проверяем X ^ dir
  r := BS*abs(dir.z) + BS * abs(dir.y);
  if ( abs(T.y*dir.z - T.z * dir.y) > r ) then begin Result := False ; Exit; end;
 
  // проверяем Y ^ dir
  r := BS * abs(dir.z) + BS * abs(dir.x);
  if ( abs(T.z * dir.x - T.x * dir.z) > r ) then begin Result := False ; Exit; end;
 
  // проверяем Z ^ dir
  r := BS*abs(dir.y) + BS * abs(dir.x);
  if ( abs(T.x * dir.y - T.y * dir.x) > r ) then begin Result := False ; Exit; end;
 
  Result := True;
end;

Простая проверка столкновений сферы и точки. Если дистанция до сферы меньше её радиуса, то столкновение произошло:

Код на Delphi:
001.
002.
003.
004.
005.
006.
007.
008.
009.
function SphereVsPoint(Sphere, Point: TVector; R: Single): Boolean;
Var
  dist: Single;
begin
  // Получение дистанции мужду двумя точками
  dist := sqrt(sqr(Sphere.X-Point.X) + sqr(Sphere.Y-Point.Y) + sqr(Sphere.Z-Point.Z));
  // Если она меньше радиуса сферы, то столкновение есть
  if dist <= R then Result := True else Result := False;
end;

Проверка на столкновение двух сфер:

Код на Delphi:
001.
002.
003.
004.
005.
006.
007.
008.
009.
010.
011.
012.
013.
014.
015.
function SphereVsSphere(Sphere1, Sphere2: TVector; R1, R2: Single): Boolean;
Var
  R,RR  : Single;
  X,Y,Z : Single;
begin
  R:= (R1 + R2);
  //Получем центр и радиус
  X := Sphere2.x - Sphere1.X;
  Y := Sphere2.Y - Sphere1.Y;
  Z := Sphere2.Z - Sphere1.Z;
 
  rr := sqrt(x*x + y*y + z*z);
  if rr < r then Result := True else Result := False;
  // если r больше rr то нет столкновения
end;

Проверка столкновения сферы с отрезком:

Код на Delphi:
001.
002.
003.
004.
005.
006.
007.
008.
009.
010.
011.
012.
013.
014.
015.
016.
017.
018.
019.
020.
021.
022.
023.
024.
025.
026.
027.
028.
029.
030.
031.
032.
033.
034.
035.
036.
037.
038.
039.
040.
041.
042.
043.
044.
045.
046.
047.
048.
049.
050.
051.
052.
053.
function SphereVsLine(Sphere, LB, LE: TVector; R: Single): Boolean;
var
  Point,
  Vector1,
  Vector2, 
  Dir    : TVector;
  D, T   : Single;
begin
  Result := False;
 
  // Узнаём положение сферы относительно начала линии
  Vector1.x := Sphere.x - Lb.x;
  Vector1.y := Sphere.y - Lb.y;
  Vector1.z := Sphere.z - Lb.z;
 
  // Узнаём направление
  Dir.x  := Le.x - Lb.x;
  Dir.y  := Le.y - Lb.y;
  Dir.z  := Le.z - Lb.z;
  
  // Узнаём длину
  D := sqrt(sqr(Dir.X) + sqr(Dir.Y) + sqr(Dir.Z));
  
  // Нормализируем её
  if d >0 then
  begin
    Vector2.x  := Dir.x * 1/d;
    Vector2.y  := Dir.y * 1/d;
    Vector2.z  := Dir.z * 1/d;
  end;
 
  // Узнаём дистанцию между началом и концом отрезка
  d := sqrt(sqr(Le.X - LB.X) +
            sqr(Le.Y - LB.Y) +
            sqr(Le.Z - LB.Z) );
 
  // Перемножаем их и получаем пределы линии, где может быть сфера
  t := (Vector1.X * Vector2.X) + (Vector1.Y * Vector2.Y) + (Vector1.Z * Vector2.Z);
 
  // Если вышли за пределы отрезка
  if t+r <= 0 then  Exit;
  if t-r >= d then  Exit;
 
  // Получаем местоположение сферы относительно отрезка
  Point.x := lb.x + Vector2.x* t;
  Point.y := lb.y + Vector2.y* t;
  Point.z := lb.z + Vector2.z* t;
 
  // Получаем дистанцию между сферой и точкой ..
  // Если она меньше радиуса сферы, то столкновение произошло
  if sqrt(sqr(Sphere.x-Point.x)+sqr(Sphere.y-Point.y)+sqr(Sphere.z-Point.z)) <= R then
  Result := True;
end;

Готовый пример можно взятьздесь :)

Автор статьи: Svsd-Val Добавил: Kordal, во Вторник 9 Июня 2009 ( 0 ) Комментариев


Design by Kornet Network