Bälle die von Wänden und Gegenständen abprallen

  • Hi,
    ich habe mal als Grundlage für ein Spiel mal eine kleine Physik Demo gebaut. Nur leider spinnt die Kollisionserkennung manchmal rum (Ball fliegt durch Körper durch).

    viel Spaß damit

    autoit.de/wcf/attachment/12873/

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <WinAPI.au3>
    #include <WindowsConstants.au3>
    #include <Array.au3>
    HotKeySet("{Esc}", "_Exit")

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

    _GDIPlus_Startup()
    $hGUI = GUICreate("Ball Physik Demo by Sprenger120", 500, 500, 50, 50)
    $hGUIDC = _WinAPI_GetDC($hGUI)
    $hBitmapBackpuffer = _WinAPI_CreateCompatibleBitmap($hGUIDC, 500, 500)
    $hDCBackpuffer = _WinAPI_CreateCompatibleDC($hGUIDC)
    _WinAPI_SelectObject($hDCBackpuffer, $hBitmapBackpuffer)
    $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDCBackpuffer)
    GUISetState(@SW_SHOW)

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

    Global Const $iBall = 5;Größe des Balles
    Global Const $iPunkteAnz = 7 ;Anzahl der Punkte
    Global Const $iRectAnz = 5;Anzahl der Quadrate

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

    Global Const $ARRAY_X = 0
    Global Const $ARRAY_Y = 1
    Global Const $ARRAY_StepX = 2
    Global Const $ARRAY_StepY = 3
    Global Const $ARRAY_NextStepColl = 4
    Global Const $ARRAY_Width = 2
    Global Const $ARRAY_Height = 3

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

    Dim $aPunkte[$iPunkteAnz][5] ;X|Y|StepX|StepY|NextStepCollision
    Dim $aRect[$iRectAnz][4] ;X|Y|W|H

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

    ;Punkte Array befüllen

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

    For $x = 0 To $iPunkteAnz - 1
    $aPunkte[$x][$ARRAY_X] = Random(0,500,1)
    $aPunkte[$x][$ARRAY_Y] = Random(0,500,1)
    $aPunkte[$x][$ARRAY_StepX] = 8;Random(5, 10, 1)
    $aPunkte[$x][$ARRAY_StepY] = 8;Random(5, 10, 1)
    If Mod($aPunkte[$x][$ARRAY_StepX], 2) Then $aPunkte[$x][$ARRAY_StepX] += 1
    ;~ If Mod($aPunkte[$x][$ARRAY_StepY], 2) Then $aPunkte[$x][$ARRAY_StepY] += 1
    $aPunkte[$x][$ARRAY_NextStepColl] = 0
    Next

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

    ;Rect Array befüllen
    For $x = 0 To $iRectAnz - 1
    $aRect[$x][0] = Random(100, 400, 1)
    $aRect[$x][1] = Random(100, 400, 1)
    $aRect[$x][2] = Random(20, 90, 1)
    $aRect[$x][3] = Random(20, 90, 1)

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

    While 1

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

    ;Prüfen ob sich Rechtecke nicht berühren
    For $i = 0 To $iRectAnz - 1
    If $i = $x Then ContinueLoop

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

    If _RectCollision($aRect[$x][$ARRAY_X], $aRect[$x][$ARRAY_Y], $aRect[$x][$ARRAY_Width], $aRect[$x][$ARRAY_Width], $aRect[$i][0], $aRect[$i][1], $aRect[$i][2], $aRect[$i][3]) Then
    $aRect[$x][0] = Random(100, 400, 1)
    $aRect[$x][1] = Random(100, 400, 1)
    $aRect[$x][2] = Random(20, 90, 1)
    $aRect[$x][3] = Random(20, 90, 1)
    ContinueLoop 2
    EndIf
    Next
    ExitLoop
    WEnd
    Next

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

    #cs
    $aPunkte[0][$ARRAY_X] = 0
    $aPunkte[0][$ARRAY_Y] = 125
    $aPunkte[0][$ARRAY_StepX] = 10;Random(5, 15, 1)
    $aPunkte[0][$ARRAY_StepY] = 0;Random(5, 15, 1)
    $aPunkte[0][$ARRAY_NextStepColl] = 0

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

    $aPunkte[1][$ARRAY_X] = 125
    $aPunkte[1][$ARRAY_Y] = 0
    $aPunkte[1][$ARRAY_StepX] = 0;Random(5, 15, 1)
    $aPunkte[1][$ARRAY_StepY] = 10;Random(5, 15, 1)
    $aPunkte[1][$ARRAY_NextStepColl] = 0

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

    $aPunkte[2][$ARRAY_X] = 300
    $aPunkte[2][$ARRAY_Y] = 101
    $aPunkte[2][$ARRAY_StepX] = 10;Random(5, 15, 1)
    $aPunkte[2][$ARRAY_StepY] = 0;Random(5, 15, 1)
    $aPunkte[2][$ARRAY_NextStepColl] = 0

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

    $aPunkte[3][$ARRAY_X] = 101
    $aPunkte[3][$ARRAY_Y] = 300
    $aPunkte[3][$ARRAY_StepX] = 0;Random(5, 15, 1)
    $aPunkte[3][$ARRAY_StepY] = 10;Random(5, 15, 1)
    $aPunkte[3][$ARRAY_NextStepColl] = 0

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

    $aRect[0][$ARRAY_X] = 100
    $aRect[0][$ARRAY_Y] = 100
    $aRect[0][$ARRAY_Width] = 50
    $aRect[0][$ARRAY_Height] = 50

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

    $aRect[1][$ARRAY_X] = 150
    $aRect[1][$ARRAY_Y] = 100
    $aRect[1][$ARRAY_Width] = 50
    $aRect[1][$ARRAY_Height] = 50
    $aRect[2][$ARRAY_X] = 200
    $aRect[2][$ARRAY_Y] = 100
    $aRect[2][$ARRAY_Width] = 50
    $aRect[2][$ARRAY_Height] = 50
    $aRect[3][$ARRAY_X] = 125
    $aRect[3][$ARRAY_Y] = 150
    $aRect[3][$ARRAY_Width] = 50
    $aRect[3][$ARRAY_Height] = 50
    $aRect[4][$ARRAY_X] = 175
    $aRect[4][$ARRAY_Y] = 150
    $aRect[4][$ARRAY_Width] = 50
    $aRect[4][$ARRAY_Height] = 50
    #ce

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

    $hBrushPunkte = _GDIPlus_BrushCreateSolid(0xFF22b14c)
    While Sleep(20)
    _GDIPlus_GraphicsClear($hGraphics, 0xFFFFFFFF)

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

    ;Punkte Zeichnen
    For $x = 0 To $iPunkteAnz - 1
    _GDIPlus_GraphicsFillRect($hGraphics, $aPunkte[$x][$ARRAY_X] + $iBall / 2, $aPunkte[$x][$ARRAY_Y] + $iBall / 2, $iBall, $iBall, $hBrushPunkte)
    Next

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

    ;Rect's zeichnen
    For $x = 0 To $iRectAnz - 1
    _GDIPlus_GraphicsDrawRect($hGraphics, $aRect[$x][0], $aRect[$x][1], $aRect[$x][2], $aRect[$x][3])
    Next

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

    For $x = 0 To $iPunkteAnz - 1
    ;Seitenwände
    If $aPunkte[$x][$ARRAY_X] > 500 Then $aPunkte[$x][$ARRAY_StepX] *= -1 ;Rechts
    If $aPunkte[$x][$ARRAY_Y] > 500 Then $aPunkte[$x][$ARRAY_StepY] *= -1 ;Unten
    If $aPunkte[$x][$ARRAY_X] < 0 Then $aPunkte[$x][$ARRAY_StepX] *= -1 ;Links
    If $aPunkte[$x][$ARRAY_Y] < 0 Then $aPunkte[$x][$ARRAY_StepY] *= -1 ;Oben

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

    Switch $aPunkte[$x][$ARRAY_NextStepColl]
    Case True
    Switch $aPunkte[$x][$ARRAY_NextStepColl]
    Case 1 ;oben, unten
    $aPunkte[$x][$ARRAY_StepY] *= -1
    Case 2 ;rechts, links
    $aPunkte[$x][$ARRAY_StepX] *= -1
    Case 3 ;beide Achsen umkehren
    $aPunkte[$x][$ARRAY_StepX] *= -1
    $aPunkte[$x][$ARRAY_StepY] *= -1
    EndSwitch
    $aPunkte[$x][$ARRAY_NextStepColl] = 0
    $aPunkte[$x][$ARRAY_StepX] = Int($aPunkte[$x][$ARRAY_StepX])
    $aPunkte[$x][$ARRAY_StepY] = Int($aPunkte[$x][$ARRAY_StepY])
    Case False
    If $aPunkte[$x][$ARRAY_StepX] = 0 Then $aPunkte[$x][$ARRAY_StepX] = 0.0000001
    If $aPunkte[$x][$ARRAY_StepY] = 0 Then $aPunkte[$x][$ARRAY_StepY] = 0.0000001

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

    ;ein schritt weitersetzen
    $aPunkte[$x][$ARRAY_X] += $aPunkte[$x][$ARRAY_StepX]
    $aPunkte[$x][$ARRAY_Y] += $aPunkte[$x][$ARRAY_StepY]

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

    $iTempX = $aPunkte[$x][$ARRAY_X] + $aPunkte[$x][$ARRAY_StepX]
    $iTempY = $aPunkte[$x][$ARRAY_Y] + $aPunkte[$x][$ARRAY_StepY]

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

    $m = 0
    ;Steigung errechnen
    If $iTempX - $aPunkte[$x][$ARRAY_X] <> 0 Then
    $m = ($iTempY - $aPunkte[$x][$ARRAY_Y]) / ($iTempX - $aPunkte[$x][$ARRAY_X])
    $b = $aPunkte[$x][$ARRAY_Y] - $m * $aPunkte[$x][$ARRAY_X]
    Else
    $m = 0
    EndIf

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

    ;~ ConsoleWrite("m=" & $m & @CRLF)

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

    ;Testen ob der Punkt ein Quadrat berühren wird im nächsten step
    For $i = 0 To $iRectAnz - 1
    If Not _RectCollision($iTempX, $iTempY, 1, 1, $aRect[$i][$ARRAY_X], $aRect[$i][$ARRAY_Y], $aRect[$i][$ARRAY_Width], $aRect[$i][$ARRAY_Height]) Then ContinueLoop
    ;Ermitteln woher der Ball kommt
    #cs
    .__2__
    | |
    1 3
    |__4__|
    #ce
    $ForStep = 1
    If $aPunkte[$x][$ARRAY_StepX] < 0 Then $ForStep = -1

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

    ;Bestimmen woher die Kugel kommt
    For $f = $aPunkte[$x][$ARRAY_X] To $aPunkte[$x][$ARRAY_X] + $aPunkte[$x][$ARRAY_StepX] Step $ForStep
    $y = $m * $f + $b

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

    ;Ecke
    If $f = $aRect[$i][$ARRAY_X] And $y = $aRect[$i][$ARRAY_Y] Then ;oben links
    ConsoleWrite("Ecke: oben links" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 3
    ExitLoop 2
    EndIf

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

    If $f = $aRect[$i][$ARRAY_X] + $aRect[$i][$ARRAY_Width] And $y = $aRect[$i][$ARRAY_Y] Then ;oben rechts
    ConsoleWrite("Ecke: oben rechts" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 3
    ExitLoop 2
    EndIf

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

    If $f = $aRect[$i][$ARRAY_X] + $aRect[$i][$ARRAY_Width] And $y = $aRect[$i][$ARRAY_Y] + $aRect[$i][$ARRAY_Height] Then ;unten rechts
    ConsoleWrite("Ecke: unten rechts" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 3
    ExitLoop 2
    EndIf

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

    If $f = $aRect[$i][$ARRAY_X] And $y = $aRect[$i][$ARRAY_Y] + $aRect[$i][$ARRAY_Height] Then ;unten links
    ConsoleWrite("Ecke: untenlinks" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 3
    ExitLoop 2
    EndIf

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

    ;Seiten
    If $y = $aRect[$i][$ARRAY_Y] And $f >= $aRect[$i][$ARRAY_X] And $f <= $aRect[$i][$ARRAY_X] + $aRect[$i][$ARRAY_Width] Then ; oben
    ConsoleWrite("oben" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 1
    ExitLoop 2
    EndIf
    If $y = $aRect[$i][$ARRAY_Y] + $aRect[$i][$ARRAY_Height] And $f >= $aRect[$i][$ARRAY_X] And $f <= $aRect[$i][$ARRAY_X] + $aRect[$i][$ARRAY_Width] Then ;unten
    ConsoleWrite("unten" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 1
    ExitLoop 2
    EndIf
    If $f = $aRect[$i][$ARRAY_X] And $y >= $aRect[$i][$ARRAY_Y] And $y <= $aRect[$i][$ARRAY_Y] + $aRect[$i][$ARRAY_Height] Then ; links
    ConsoleWrite("links" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 2
    ExitLoop 2
    EndIf
    If $f = $aRect[$i][$ARRAY_X] + $aRect[$i][$ARRAY_Width] And $y >= $aRect[$i][$ARRAY_Y] And $y <= $aRect[$i][$ARRAY_Y] + $aRect[$i][$ARRAY_Height] Then ; rechts
    ConsoleWrite("rechts" & @CRLF)
    $aPunkte[$x][$ARRAY_NextStepColl] = 2
    ExitLoop 2
    EndIf
    Next
    Next
    EndSwitch
    Next
    _WinAPI_BitBlt($hGUIDC, 0, 0, 500, 500, $hDCBackpuffer, 0, 0, $SRCCOPY)
    WEnd

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

    While GUIGetMsg() <> -3
    WEnd

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

    Func _RectCollision($iX1, $iY1, $iWidth1, $iHeight1, $iX2, $iY2, $iWidth2, $iHeight2)
    ;Author: Faweyr
    Return $iX1 + $iWidth1 > $iX2 And $iX1 < $iX2 + $iWidth2 And $iY1 + $iHeight1 > $iY2 And $iY1 < $iY2 + $iHeight2
    EndFunc ;==>_RectCollision

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

    Func _Exit()
    _GDIPlus_GraphicsDispose($hGraphics)
    _WinAPI_DeleteObject($hBitmapBackpuffer)
    _WinAPI_DeleteDC($hDCBackpuffer)
    _WinAPI_ReleaseDC($hGUI, $hGUIDC)
    _GDIPlus_BrushDispose($hBrushPunkte)
    _GDIPlus_Shutdown()
    Exit
    EndFunc ;==>_Exit

    [/autoit]
  • Das rotieren kommt dadurch, dass es auf der Außenlinie bleibt, also die ganze Zeit If-Then Wechsel, das im Körper, kommt von der Geschwindigkeit über 1 Pixel/Schleifenrunde, wenn man nur für einen Pixel in der Nähe prüft. Jedenfalls wäre das warscheinlich

  • ~~Update~~
    Ich habe mir einen völlig neuen Kollisionserkennungs-Algorythmus erdacht ,weil die Physik Engine von Moritz einfach nicht benutzbar ist. Dieser ist viel besser, funktioniert aber noch nicht perfekt. Die Bälle fliegen manchmal einfach durch die Körper durch. Die Erkennungsrate sinkt rapide wenn StepX und StepY unterschiedlich, nicht gerade oder zu hoch sind. Ich kann mir das verhalten nicht erklären ?(