Thema: FreePascal Vektoriales Rechnen

Einzelnen Beitrag anzeigen

Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
721 Beiträge
 
Delphi 11 Alexandria
 
#21

AW: Vektoriales Rechnen

  Alt 16. Mai 2017, 07:52
Hallo Mauroon

die Funktion ZweiObjekte( ) überprüft, ob sich zwei Objekte a und b, welche sich mit gleichmässiger Geschwindigkeit je entlang einer Gerade bewegen jetzt gerade oder in der Zukunft treffen werden.

IN:
a1 : Momentaner Standort von a
a2 : a ist unterwegs von a1 in Richtung a2
ppsa: Geschwindigkeit von a in Pixel Pro Sekunden
radiusa : Zur Vereinfachung wird für die Form der Objekte ein Kreis mit Radius radiusa angenommen. [Man könnte hier natürlich auch ein Rechteck oder die wirkliche Form annehmen.]

OUT:
treffpunkt_a : Ortsvektor von a beim Zusammentreffen mit b.
kontakt: TRUE, wenn sich die beiden Objekte momentan überlappen.
zeit_bis_a2: Zeit, welche a von a1 nach a2 benötigt
Result: TRUE, wenn sich die beiden Objekte jetzt treffen oder sich in Zukunft treffen werden.
Falls Result=TRUE: sekundenbiszumteffen: Nach dieser Zeit treffen a und b aufeinander.



Delphi-Quellcode:
function ZweiObjekte( a1, a2 : TRPoint; radiusa : extended; ppsa : extended;
                      b1, b2 : TRPoint; radiusb : extended; ppsb : extended;
                      var treffpunkt_a, treffpunkt_b : TRPoint;
                      var zeit_bis_a2 : extended; var sekundenbiszumteffen : extended; var kontakt : boolean ):boolean;

var speeddelta, startdelta, speeda, speedb : TVektor;
    Radius : extended;
    quadres : TQuadResultat;
    res : boolean;

    function getVektor( a, b : TRPoint ): TVektor;
    begin
        Result.x := b.x - a.x;
        Result.y := b.y - a.y;
    end;

    function speedvektor( v : TVektor; pps : extended ) : TVektor;
    var len : extended;
    // IN v in Bewegungsrichtung pps=Speed des Objekts Pixel pro Sekunden
    // OUT Vektor - nach einer Sekunde hat sich das Objekt um diesen Vektor verschoben
    begin
        len := sqrt( sqr(v.x) + sqr(v.y) );
        if ( len > 0 ) then
        begin
          Result.x := v.x/len*pps;
          Result.y := v.y/len*pps;
        end
        else
        begin
          v.x := 0;
          v.y := 0;
        end;
    end;

    function OrtNachTSekunden( start, speedvektor : TVektor; sec : extended ) : TVektor;
    begin
        Result.x := start.x + sec*speedvektor.x;
        Result.y := start.y + sec*speedvektor.y;
    end;

    function hkontakt( pos1, pos2 : TRPoint; radius1, radius2 : extended ):boolean;
    begin
        Result := sqrt(sqr(pos2.x-pos1.x) + sqr(pos2.y-pos1.y)) < (radius1 + radius2);
    end;


begin
  Radius := radiusa + radiusb;
  res := false;
  kontakt := false;

  // Objekt a bewegt pro Sekunde "speeda" vorwärts - Objekt b bewegt pro Sekunde "speeda" vorwärts
  speeda := speedvektor( getVektor( a1, a2 ), ppsa );
  speedb := speedvektor( getVektor( b1, b2 ), ppsb );

  // Solange benötigt a von a1 nach a2:
  if abs(speeda.x) > abs(speeda.y) then
    zeit_bis_a2 := ( a2.x - a1.x )/speeda.x else
    zeit_bis_a2 := ( a2.y - a1.y )/speeda.y;

  // nach t Sekunden befindet sich Objekt a an der Stelle a1 + t*speeda,
  // nach t Sekunden befindet sich Objekt b an der Stelle b1 + t*speedb
  // Treffen die Objekte aufeinander? D.g. gibt es ein t: a1 + t*speeda = b1 + t*speedb?
  speeddelta := getVektor( speedb, speeda );
  startdelta := getVektor( b1, a1 );

  if ( speeddelta.x = 0 ) and ( speeddelta.y = 0 ) then
  begin // die relative geschwindigkeit zwischen a und b ist 0 =>
        // prüfen, ob sich a und b am momentanen ort berühren:
      res := hkontakt( a1, b1, radiusa, radiusb );
      treffpunkt_a := a1;
      treffpunkt_b := b1;
      kontakt := res;
      sekundenbiszumteffen := 0;
  end
  else
  begin
       // sonst muss für ein Zusammentreffen nach t gelten:
       // ¦a1+t*speeda - (b1+t*speedb)¦ < Radius =>
       // Dies führt auf eine quadratische Gleichung in t, welche wir lösen müssen:
      if Quadratische_Gleichung_Reell( (sqr(speeddelta.x) + sqr(speeddelta.y)),
        2*( speeddelta.x * startdelta.x + speeddelta.y * startdelta.y),
        sqr(startdelta.x) + sqr(startdelta.y)- sqr(Radius),
        quadres ) then
      begin
          // quadres.x2 ist immer <= quadres.x1 (da in dieser quadratischen Gleichung (at^2+bt+c=0) a immer positiv ist)
          if ( quadres.x1 <> quadres.x2 ) then // wir werden nur Überschneidungen der Kreise als Treffer
          begin
              kontakt := ( quadres.x2 < 0 ) and ( quadres.x1 > 0 );
              if kontakt then
              begin
                 // a und b haben momentan einen gemeinsamen Schnitt
                 treffpunkt_a := a1;
                 treffpunkt_b := b1;
                 res := true;
                 sekundenbiszumteffen := 0;
              end
              else
              begin
                 // a und b werden in quadres.x2 Sekunden einen gemeinsamen Schnitt haben
                 if ( quadres.x2 > 0 ) then
                 begin
                    treffpunkt_a := OrtNachTSekunden( a1, speeda, quadres.x2 );
                    treffpunkt_b := OrtNachTSekunden( b1, speedb, quadres.x2 );
                    res := true;
                    sekundenbiszumteffen := quadres.x2;
                 end;
              end;
          end;
      end;
  end;
  Result := res;
end;


Anwendungsbeispiel:
Dein Spielobjekt a befindet sich an Position a1, das Zielobjekt an Position a2. a (dein Projektil) bewegt sich mit ppsa in Richtung a2.
b ist ein Hindernis, welches sich auch bewegt oder auch still steht ( dann ppsb = 0 wählen ).
Die Funktion überprüft, ob und wann (var sekundenbiszumteffen) a auf b treffen wird.
Die Funktion berechnet ebenfalls, wie lange a von a1 nach a2 benötigt.


Code unten:
a bewegt sich entlang der x Achse von x=1000 nach -1000. speed 500
b bewegt sich entlang der y Achse von y=500 nach 0. speed 250

Die Funktion gibt TRUE zurück, weil sich die Objekte natürlich nach ca. 2 Sekunden in der Region (0,0) treffen werden.
a und b treffen nach treffer_in_sec=1.989266873708 aufeinander.
a benötigt von a1 nach a2 bisa=4 Sekunden.
=> a trifft aufs Hindernis b, bevor a beim Punkt a2 angekommen ist.

Delphi-Quellcode:
procedure TForm92.Button1Click(Sender: TObject);
var a1, a2, b1, b2, tpa, tpb : TRPoint;
    ppsa, ppsb, radiusa, radiusb, bisa2, treffer_in_sec : extended;
    kontakt : boolean;

begin
  a1 := RPoint( 0,1000);
  a2 := RPoint( 0,-1000 );
  b1 := RPoint( 500,0 );
  b2 := RPoint( 0,0 );

  radiusa := 3;
  radiusb := 3;
  ppsa := 500;
  ppsb := 250;

  if ZweiObjekte( a1, a2, radiusa, ppsa,
                  b1, b2, radiusb, ppsb, tpa, tpb, bisa2, treffer_in_sec, kontakt ) then
  begin
      showmessage( 'treffer' );
  end
  else showmessage( 'kein treffer' );
end;
Michael Gasser
  Mit Zitat antworten Zitat