OpenGL v Delphi4



Detekce kolizí


Příklad a zdroják ke stažení (724k)



Pro detekci kolizí jsem použil předchozí příklad, jen jsem přidal pár procedur. Možná existují daleko lepší algoritmy pro detekci kolizí, ale já jsem si vymyslel jednoduché výpočty, bez goniometrických funkcí a jinejch složitostí a zdá se, že to funguje. Pokud někdo znáte lepší způsob výpočtů, napište mi prosím, rád se přiučím.

No a teď k věci.

Do 'public' jsem přidal tyto proměnné.


  public
    ...
    Xtrue, Ytrue, Ztrue: array [1..6] of boolean;     //kolizní hodnoty
    PxLast, PyLast, PzLast: array [1..6] of double;   //poslední pozice kamery před kolizí
  end;

Vytvořil jsem nové procedury.

  procedure InitCollision;
  procedure Collision;

No a teď se pokusím každou proceduru trochu popsat.


procedure TForm1.InitCollision;

Proměnné jsou:


  var
    Ax, Ay, Az,
    Bx, By, Bz,
    Cx, Cy, Cz,
    Dx, Dy, Dz: double;                                //Body objektu
    Px, Py, Pz: double;                                //Pozice kamery
    Xdiff, Ydiff, Zdiff, Cdiff, Fdiff : boolean;       //Kolizní hodnoty
    i: integer;                                        //Počet objektů
             
Teď převedu hodnoty pozice kamery do proměných 'Px, Py, Pz'.

  //pozice kamery
  Px := X;
  Py := -Y;
  Pz := -Z;                        
             
Znaménka minus jsou tam proto, aby odpovídala 'Y,Z' souřadnice kamery 'Y,Z' souřadnici objektu.

Nyní převedu hodnoty všech šesti objektů do proměných 'Ax' až 'Dz'.


  for i := 1 to 6 do begin
    //stena 1
    if i = 1 then begin
      //pozice bodu A
      Ax := VertexPointer[0].X;
      Ay := VertexPointer[0].Y;
      Az := VertexPointer[0].Z;
      //pozice bodu B
      Bx := VertexPointer[1].X;
      By := VertexPointer[1].Y;
      Bz := VertexPointer[1].Z;
      //pozice bodu C
      Cx := VertexPointer[2].X;
      Cy := VertexPointer[2].Y;
      Cz := VertexPointer[2].Z;
      //pozice bodu D
      Dx := VertexPointer[5].X;
      Dy := VertexPointer[5].Y;
      Dz := VertexPointer[5].Z;
    end;
    //stena 2
    if i = 2 then begin
      //pozice bodu A
      Ax := VertexPointer2[0].X;
      Ay := VertexPointer2[0].Y;
      Az := VertexPointer2[0].Z;
      //pozice bodu B
      Bx := VertexPointer2[1].X;
      By := VertexPointer2[1].Y;
      Bz := VertexPointer2[1].Z;
      //pozice bodu C
      Cx := VertexPointer2[2].X;
      Cy := VertexPointer2[2].Y;
      Cz := VertexPointer2[2].Z;
      //pozice bodu D
      Dx := VertexPointer2[5].X;
      Dy := VertexPointer2[5].Y;
      Dz := VertexPointer2[5].Z;
    end;
    //stena 3
    if i = 3 then begin
      //pozice bodu A
      Ax := VertexPointer3[0].X;
      Ay := VertexPointer3[0].Y;
      Az := VertexPointer3[0].Z;
      //pozice bodu B
      Bx := VertexPointer3[1].X;
      By := VertexPointer3[1].Y;
      Bz := VertexPointer3[1].Z;
      //pozice bodu C
      Cx := VertexPointer3[2].X;
      Cy := VertexPointer3[2].Y;
      Cz := VertexPointer3[2].Z;
      //pozice bodu D
      Dx := VertexPointer3[5].X;
      Dy := VertexPointer3[5].Y;
      Dz := VertexPointer3[5].Z;
    end;
    //stena 4
    if i = 4 then begin
      //pozice bodu A
      Ax := VertexPointer4[0].X;
      Ay := VertexPointer4[0].Y;
      Az := VertexPointer4[0].Z;
      //pozice bodu B
      Bx := VertexPointer4[1].X;
      By := VertexPointer4[1].Y;
      Bz := VertexPointer4[1].Z;
      //pozice bodu C
      Cx := VertexPointer4[2].X;
      Cy := VertexPointer4[2].Y;
      Cz := VertexPointer4[2].Z;
      //pozice bodu D
      Dx := VertexPointer4[5].X;
      Dy := VertexPointer4[5].Y;
      Dz := VertexPointer4[5].Z;
    end;
    //strop
    if i = 5 then begin
      //pozice bodu A
      Ax := VertexPointer5[0].X;
      Ay := VertexPointer5[0].Y;
      Az := VertexPointer5[0].Z;
      //pozice bodu B
      Bx := VertexPointer5[1].X;
      By := VertexPointer5[1].Y;
      Bz := VertexPointer5[1].Z;
      //pozice bodu C
      Cx := VertexPointer5[2].X;
      Cy := VertexPointer5[2].Y;
      Cz := VertexPointer5[2].Z;
      //pozice bodu D
      Dx := VertexPointer5[5].X;
      Dy := VertexPointer5[5].Y;
      Dz := VertexPointer5[5].Z;
    end;

    //podlaha
    if i = 6 then begin
      //pozice bodu A
      Ax := VertexPointer6[0].X;
      Ay := VertexPointer6[0].Y;
      Az := VertexPointer6[0].Z;
      //pozice bodu B
      Bx := VertexPointer6[1].X;
      By := VertexPointer6[1].Y;
      Bz := VertexPointer6[1].Z;
      //pozice bodu C
      Cx := VertexPointer6[2].X;
      Cy := VertexPointer6[2].Y;
      Cz := VertexPointer6[2].Z;
      //pozice bodu D
      Dx := VertexPointer6[5].X;
      Dy := VertexPointer6[5].Y;
      Dz := VertexPointer6[5].Z;
    end;             
No a teď, když mám všechny hodnoty, které potřebuju, roztřídim objekty podle vzdáleností jednotlivých bodů od sebe.

  //rozdíly vzdálenosti bodů
  if Ax <= Bx then
    Xdiff := True
  else
    Xdiff := False;

  if Cy <= Ay then
    Ydiff := True
  else
    Ydiff := False;

  if Dz <= Cz then
    Zdiff := True
  else
    Zdiff := False;                        
             
Objekty takto třídím, abych věděl, jak jsou orientovány v prostoru a podle toho mohl později vypočítat kolizi mezi objektem a kamerou. Pro každou orientaci se totiž počítá kolize trochu jinak.

No a teď následuje hlavní výpočet.


    //výpočet
    //X hodnota
    if Xdiff then begin
      if ((Bx - Ax) <= 0) then begin             //zruší nulovou hodnotu
        Ax := Ax - 0.5;
        Bx := Bx + 0.5;
      end;
      if ((Px >= Ax)and(Px <= Bx)) or ((Px >= Cx)and(Px <= Dx)) then
        Xtrue[i] := True
      else
        Xtrue[i] := False;
    end;
    if not Xdiff then begin
      if ((Px >= Bx)and(Px <= Ax)) or ((Px >= Dx)and(Px <= Cx)) then
        Xtrue[i] := True
      else
        Xtrue[i] := False;
    end;
    //Y hodnota
    if Ydiff then begin
      if ((Ay - Cy) <= 0) then begin             //zruší nulovou hodnotu
          Ay := Ay + 0.5;
          Cy := Cy - 0.5;
      end;
      if ((Py <= Ay)and(Py >= Cy)) or ((Py <= By)and(Py >= Dy)) then
        Ytrue[i] := True
      else
        Ytrue[i] := False;
    end;
    if not Ydiff then begin
      if ((Py <= Cy)and(Py >= Ay)) or ((Py <= Dy)and(Py >= By)) then
        Ytrue[i] := True
      else
        Ytrue[i] := False;
    end;
    //Z hodnota
    if Zdiff then begin
      if ((Az - Bz) <= 0) then begin       //zruší nulovou hodnotu
        Bz := Bz - 0.5;
        Az := Az + 0.5
      end;
      if (((Pz <= Az)and(Pz >= Bz)) or ((Pz <= Cz)and(Pz >= Dz)))               //stěny
      or (((Pz >= Az)and(Pz <= Cz)) or ((Pz >= Bz)and(Pz <= Dz)))  then         //podlaha a strop
        Ztrue[i] := True
      else
        Ztrue[i] := False;
    end;
    if not Zdiff  then begin
      if ((Pz <= Bz)and(Pz >= Az)) or ((Pz <= Dz)and(Pz >= Cz))  then
        Ztrue[i] := True
      else
        Ztrue[i] := False;
    end;                      
             
Vyznáte se v tom? Já teda moc ne.
Nejdřív počítám 'X' souřadnice bodů objektu vůči kameře. Když se kamera nachází mezi těmito body vyplivne to hodnotu 'Xtrue[i] := True;', což znamená, že se zapíná kolize mezi objektem a kamerou na 'X' souřadnici. To samé se provede na souřadnici 'Y' a 'Z'. Procedura tedy vyplivne hodnoty 'Xtrue[i]', 'Ytrue[i]', 'Ztrue[i]', se kterými pak dále pracuji.

Nakonec načtu poslední pozici kamery před následnou kolizí.


  //poslední pozice kamery před kolizí
  if (Xtrue[i] = False) or (Ytrue[i] = False) or (Ztrue[i] = False) then begin
    PxLast[i] := X;
    PyLast[i] := Y;
    PzLast[i] := Z;
  end;                 
             
Celou proceduru bez mejch poznámek si můžete prohlédnout zde.



procedure TForm1.Collision;

U této procedury je jen jedna proměnná:


  var
    i: integer;              //počet objektů                
             
Výpočet už je jednoduchý.

  for i := 1 to 6 do begin
    if Xtrue[i] and Ytrue[i] and Ztrue[i] then begin
      X := PxLast[i];
      Y := PyLast[i];
      Z := PzLast[i];
    end;
  end;
 
Pokud všechny tři kolizní hodnoty jsou pravdivé, načte se poslední pozice kamery před kolizí. Pro každý objekt se toto počítá zvlášť.
Na celou proceduru můžete mrknout zde.


Jo, ještě vám řeknu, jak se v prostoru pohybovat.
Takže dopředu, dozadu a otáčení pomocí šipek.
'PageUp'- nahoru
'PageDown' - dolu
'Home' - pohled nahoru
'End' - pohled dolu
'Esc' - konec
'Enter' - FullScreen Mode 640x480 16b



glClipPlane, glDrawElements, FullScreen - vše v jednom Multitexturing
Home