• Hi there,
    ich hab mich die letzten Tage, rein aus interesse, mal mit Vektoren beschäftigt und bin dann auch auf AABB gestoßen. AABB ist der Algorythmus der in abgewandelter Form in jedem Shooter benutzt wird um Die Kollision Kugel -> Hitbox zu überprüfen. Der Vorteil von AABB ist, das man keine Schleife braucht die die Schussbahn entlag geht und alle 2 Millimeter einzeln überprüft ob eine Kollision stattfindet.
    Falls sich jemand noch weiter mit der Materie beschäftigen möchte, dem kann ich nur die Videos von Jorge Rodriguez empfehlen

    Eventuell noch etwas zu dem Skript:
    - Mit $vBoxMin,$vBoxMax wird das Viereck definiert
    - $v1 ist ein Bezugspunkt der gebraucht wird um einen Vektor zu bilden, der letztenendes mit dem Viereck kollidieren soll

    Spoiler anzeigen
    [autoit]

    #include <gdiplus.au3>
    #include <GuiConstantsEx.au3>
    #include <Misc.au3>
    _GDIPlus_Startup();

    [/autoit] [autoit][/autoit] [autoit]

    HotKeySet("{Esc}", "_Exit")

    [/autoit] [autoit][/autoit] [autoit]

    Global $iWindowH = 500
    Global $iWindowW = 500
    Global $dMouseAngle = 0.0
    Global $rad2pi = 180 / 3.1415926535

    [/autoit] [autoit][/autoit] [autoit]

    $hWindow = GUICreate("Test", $iWindowH, $iWindowW)
    GUISetState()
    $hGr = _GDIPlus_GraphicsCreateFromHWND($hWindow)
    _GDIPlus_GraphicsClear($hGr, 0xFFFFFFFF)

    [/autoit] [autoit][/autoit] [autoit]

    Global $vBoxMin[2] = [50, 50]
    Global $vBoxMax[2] = [120, 120]
    Global $v1[2] = [300, 300]
    Global $vInter[2];

    [/autoit] [autoit][/autoit] [autoit]

    $hRed = _GDIPlus_PenCreate(0xFFFF0000)
    $hGreen = _GDIPlus_PenCreate(0xFF00FF00)
    $hBlue = _GDIPlus_PenCreate(0xFF0000FF)
    While 1
    Sleep(10)
    Local $coord = GUIGetCursorInfo($hWindow)
    If $coord[0] < 0 Or $coord[1] < 0 Then ContinueLoop
    Local $vMouse[2] = [$coord[0], $coord[1]]
    _GDIPlus_GraphicsClear($hGr, 0xFFFFFFFF)

    [/autoit] [autoit][/autoit] [autoit]

    Local $fCollision = False;

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    if (LineIntersection($vBoxMin,$vBoxMax,$vMouse,$v1,$vInter)) Then
    $fCollision = True
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    If $fCollision Then
    _GDIPlus_GraphicsDrawRect($hGr, $vBoxMin[0], $vBoxMin[1], $vBoxMax[0] - $vBoxMin[0], $vBoxMax[1] - $vBoxMin[1], $hGreen)
    Else
    _GDIPlus_GraphicsDrawRect($hGr, $vBoxMin[0], $vBoxMin[1], $vBoxMax[0] - $vBoxMin[0], $vBoxMax[1] - $vBoxMin[1], $hRed)
    EndIf
    _GDIPlus_GraphicsFillRect($hGr, $v1[0], $v1[1], 5, 5)
    _GDIPlus_GraphicsDrawLine($hGr, $vMouse[0], $vMouse[1], $v1[0], $v1[1])
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    Func LineIntersection($vBoxMin, $vBoxMax, $v0, $v1,ByRef $vInter)
    Local $f_low = 0;
    $f_high = 1;

    [/autoit] [autoit][/autoit] [autoit]

    if (not ClipLine(0,$vBoxMin,$vBoxMax,$v0,$v1,$f_low,$f_high)) Then
    return False
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    if (not ClipLine(1,$vBoxMin,$vBoxMax,$v0,$v1,$f_low,$f_high)) Then
    return False
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Local $b = vecSubtract($v1,$v0)
    $vInter = vecAdd($v0, vecMultiply($b,$f_low))

    [/autoit] [autoit][/autoit] [autoit]

    return true;
    EndFunc ;==>LineIntersect

    [/autoit] [autoit][/autoit] [autoit]

    Func ClipLine($d, $Pmin, $Pmax, $v0, $v1, ByRef $f_low, ByRef $f_high)
    Local $f_lowDim = 0, $f_highDim = 1
    Local $Len = ($v1[$d] - $v0[$d])

    [/autoit] [autoit][/autoit] [autoit]

    if $Len = 0 then return false

    [/autoit] [autoit][/autoit] [autoit]

    $f_lowDim = ($Pmin[$d] - $v0[$d]) / $Len
    $f_highDim = ($Pmax[$d] - $v0[$d]) / $Len

    [/autoit] [autoit][/autoit] [autoit]

    If ($f_lowDim > $f_highDim) Then
    swap($f_lowDim, $f_highDim)
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    If ($f_highDim < $f_low) Then
    Return False;
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    If ($f_lowDim > $f_high) Then
    Return False;
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $f_low = max($f_lowDim, $f_low);
    $f_high = min($f_highDim, $f_high);

    [/autoit] [autoit][/autoit] [autoit]

    If ($f_low > $f_high) Then
    Return False
    EndIf
    Return True;
    EndFunc ;==>ClipLine

    [/autoit] [autoit][/autoit] [autoit]

    Func swap(ByRef $a, ByRef $b)
    Local $tmp = $a;
    $a = $b
    $b = $tmp
    EndFunc ;==>swap

    [/autoit] [autoit][/autoit] [autoit]

    Func max($a, $b)
    If $a > $b Then
    Return $a
    Else
    Return $b;
    EndIf
    EndFunc ;==>max

    [/autoit] [autoit][/autoit] [autoit]

    Func min($a, $b)
    If $a < $b Then
    Return $a
    Else
    Return $b;
    EndIf
    EndFunc ;==>min

    [/autoit] [autoit][/autoit] [autoit]

    Func _Exit()
    _GDIPlus_PenDispose($hGreen)
    _GDIPlus_PenDispose($hGreen)
    _GDIPlus_PenDispose($hRed)
    _GDIPlus_GraphicsDispose($hGr)
    _GDIPlus_Shutdown()
    Exit
    EndFunc ;==>_Exit

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func vecAdd($v1, $v2)
    Local $rVec[2]
    $rVec[0] = $v1[0] + $v2[0]
    $rVec[1] = $v1[1] + $v2[1]
    Return $rVec
    EndFunc ;==>vecAdd

    [/autoit] [autoit][/autoit] [autoit]

    Func vecMultiply($v1, $fVal)
    Local $rVec[2]
    $rVec[0] = $v1[0] * $fVal
    $rVec[1] = $v1[1] * $fVal
    Return $rVec
    EndFunc ;==>vecMultiply

    [/autoit] [autoit][/autoit] [autoit]

    Func vecSubtract($v2, $v1)
    Local $rVec[2]
    $rVec[0] = $v2[0] - $v1[0]
    $rVec[1] = $v2[1] - $v1[1]
    Return $rVec
    EndFunc ;==>vecSubtract

    [/autoit]

    (Jaja ich weiß der Code ist nicht wirklich das Gelbe vom Ei... Es ist ja nur zum Testen)

    //Edit: Was ich evt. noch vergessen hab zu erwähnen: Man kann dieses 2D Beispiel auch auf 3D erweitern.
    Dazu müsste man nur alle Vektoren noch eine Z Koordinate verpassen und noch eine weitere Dimensionsüberprüfung für Z hinzufügen

    [autoit]


    ;Func LineIntersection()
    if (not ClipLine(2,$vBoxMin,$vBoxMax,$v0,$v1,$f_low,$f_high)) Then
    return False
    EndIf

    [/autoit]
  • Top :thumbup:, als ich mal ein kleines Shooterspiel schreiben wollte, stand ich genau vor dem Problem. Damit wollte ich mich schon immer mal beschäftigen. Ich lese mich dann jetzt mal da rein... Danke :thumbup:

    "Je mehr Käse, desto mehr Löcher; je mehr Löcher, desto weniger Käse. Ergo: Je mehr Käse, desto weniger Käse. 8| "
    "Programmers never die: they just GOSUB without RETURN"
    "I tried to change the world but I couldn't find the source code."

  • Ich habe mir den Algorithmus nicht im Detail angeschaut, aber könnte er auch Kollisionen erkennen, wenn z.B. die Kugel einen größeren Bewegungsvektor hat, als das Objekt, das er durchdringt? D.h. realistisch würde die Kugel das Objekt durchqueren, aber praktisch "überspringt" sie das Objekt aufgrund des größeren Bewegungsvektors.

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    Einmal editiert, zuletzt von UEZ (29. September 2013 um 10:12)

  • Hi,
    kann sein, dass ich etwas falsch verstanden habe, oder dass deine grafische Umsetzung einen Fehler hat...

    Jedenfalls ist es so, dass das Quadrat nicht grün ist, wenn ich in seine Richtung schiesse (der Mauszeiger also nicht im oder "hinter" dem Quadrat ist.
    Das reduziert deinen "Algorithmus" zum einfachsten "Haben zwei Linien einen Schnittpunkt"-Problem.

    Das ist aber imho falsch, denn nur allein wenn ich in die Richtung des Quadrates schiesse, werde ich es jedenfalls treffen! Das ist ja der Sinn dabei, eine Richtung anzugeben und dann zu ermitteln, ob dort ein Ziel getroffen wurde oder nicht. Wenn dann noch die Entfernung dazukommt, wird es interessant (Ballistisches Problem). Dann wird das Ziel nämlich auch nicht getroffen, wenn der Schuss vor oder hinter dem Ziel aufgeschlagen ist. (Hinweis: Wenn eine gerade Anzahl von Linien gekreutzt wurden und der Schütze ausserhalb des Ziels steht, dann hat er vorbeigeschossen...)

  • Das Beispielskript ist korrekt. Da wird "nur" der Geschwindigkeitsvektor dargestellt und die Kugel geht praktisch nur einen Schritt.
    Und genau das Durchquerungs-ohne-Berührungs Problem soll damit gelöst werden, sonst hätte die ganze Vektorrechnung ja überhaupt keinen Sinn. (Dann würde ein einfaches IsInRect(Kugelmitte) ausreichen)

    lg
    M

  • Zitat von UEZ

    aber könnte er auch Kollisionen erkennen, wenn z.B. die Kugel einen größeren Bewegungsvektor hat, als das Objekt, das er durchdringt?


    Solange sich die Kugel immernoch "hinter" der Box befindet, sollte das funktionieren. Soweit ich AABB verstanden habe kümmert es sich nur um die Richtung in die die Kugel fliegt.

    Andy: Sagen wir mal so: Die Maus ($v0) bist du als Spieler und der Fixe Punkt ($v1) ist dort wo du hinguckst. v1 ist nur Fix, weil ich ja irgentwie einen Vektor aus den Zwei Koordinaten machen muss.
    Der Algorithmus ermittelt nur ob du die Box anguckst (mit dem Vektor schneidest).

    //Edit: Mars: Danke für die Antwort, habs zu spät gesehn

  • Hi,

    Zitat

    Da wird "nur" der Geschwindigkeitsvektor dargestellt und die Kugel geht praktisch nur einen Schritt.

    welchen Schritt?
    Wenn ich das Script richtig nachvollziehe, wird lediglich auf Berührung der schwarzen Linie mit dem Inhalt (Seiten) des Rechtecks geprüft.
    Dazu braucht man allerdings keine Vektorrechnung^^

    Sinn würde es für mich machen, wenn abgefragt würde, ob der "Geschwindigkeitsvektor" in Richtung des Zieles zeigt und somit das Ziel trifft.
    Um das mal zu verdeutlichen:
    Sprengers Script zielt mit einem Gewehr auf ein Ziel. Der Laserpointer der Zieleinrichtung zeigt definitiv auf das Ziel. Der Schütze drückt ab und trifft nicht! Er drückt nochmals ab und trifft immer noch nicht....Nachgefragt, warum er nicht trifft bekommt er die Antwort, "Naja, das Geschoss fliegt nicht so weit!".
    Auf die Frage, wie man denn das Ziel nun treffen soll, kommt die Antwort: "Du musst weiter schiessen!"
    Na gut, dann schiesst er also "weiter" und zielt weit hinter das Ziel und drückt ab. Treffer, da der Schuss durchs Ziel geht!?
    Was soll das denn?

    Grotesk wird es dann, wenn der Schütze im Ziel steht und garnicht schiesst, aber trotzdem schon getroffen hat ;)

    Stell dir in einem TD vor, du schiesst in einen bestimmten Bereich (Sprengers Map, Tower ist der Ursprungspunkt des Vektors).
    Der Mauscursor stellt das Zielgebiet dar. "Vor" dem Ziel triffst du das Ziel nicht, zielst du aber weit dahinter, dann schon....

    WENN lediglich eine Richtung angegeben ist, dann muss NUR feststehen, ob das Ziel in dieser Richtung liegt, unerheblich von der Entfernung.
    Wenn die Entfernung auch noch angegeben ist, dann stellt sich die Frage garnicht! Denn dann brauche ich lediglich den kleinsten Abstand zum Ziel zu prüfen und die Rechnerei ist hinfällig!

  • Wenn du jetzt aber davon ausgehst, dass z.B. eine AK-47 300 Meter weit schießen kann und du die Länge dementsprechend einstellst, dann passt das alles wieder. Im Video wurde stattdessen einfach ein sehr großer Wert genommen, sodass die Distanz keine Rolle mehr spielt. Das Skript ist in dieser Hinsicht vielleicht etwas ungünstig, aber sonst sehe ich da kein Problem, es sollte ja auch ein bisschen realistisch sein.

  • Interessanter Denkansatz. Nur leider wüsste ich dazu jetzt keine Andere Lösung als die hier.
    Was man machen könnte zu dem Problem des nicht Treffens ist, den Richtungsvektor aus v1-v0 zu normalisieren und dann mit einer großen Zahl multiplizieren um genug Reichweite zu haben.

    Eventuell sieht man es aus dem Script nicht wirklich raus, aber man bekommt auch geliefert wo genau die Kugel den Körper trifft was mit der Kleinsten Abstand Methode nur bedingt möglich währe wenn ich nicht falsch liege.