Seitengenaue Kollisionsabfrage

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Josef Sniatecki
Beiträge: 657
Registriert: 02.06.2008 21:29
Kontaktdaten:

Seitengenaue Kollisionsabfrage

Beitrag von Josef Sniatecki »

Hi,

habe mal in den tiefen der Hyperlinks gestöbert und endlich eine
Lösung für Kollisionen zwischen zwei Rechtecken gefunden. Oft habe ich hier
schon mal ein Thread eröffnet, um zu erfahren wie man dieses Problem
löst. Doch leider konnten mir auch nicht einmal die guten Makros von
Kaeru weiter helfen. :(

Nun zum eigentlichen Code:

Code: Alles auswählen

Enumeration 1
  #Collision_Left
  #Collision_Right
  #Collision_Top
  #Collision_Bottom
EndEnumeration

Procedure.i RectangleCollision(X1.f, Y1.f, Width1.f, Height1.f, X2.f, Y2.f, Width2.f, Height2.f)
  Protected A.f, B.f
  
  A = (X2 + Width2/2)  - (X1 + Width1/2)
  B = (Y2 + Height2/2) - (Y1 + Height1/2)
  
  If Abs(A) - (Width1/2) - (Width2/2) <= 0 And Abs(B) - (Height1/2) - (Height2/2) <= 0
    If A => 0
      If B => 0
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Left
        Else
          ProcedureReturn #Collision_Top
        EndIf
      Else
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Left
        Else
          ProcedureReturn #Collision_Bottom
        EndIf
      EndIf
    Else
      If B => 0
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Right
        Else
          ProcedureReturn #Collision_Top
        EndIf
      Else
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Right
        Else
          ProcedureReturn #Collision_Bottom
        EndIf
      EndIf
    EndIf
  Else
    ProcedureReturn #Null
  EndIf
EndProcedure
Diese Funktion prüft, ob es überhaupt zu einer Kollision zwischen zwei
Rechtecken gekommen ist. Danach wird überprüft, welche Seite des
zweiten Rechteckes getroffen wurde.

Als ich diese Funktion endlich gefunden habe, bin ich sofort an einen kleinen
Jump&Run-Prototyp ran gegangen:

Code: Alles auswählen

Enumeration 1
  #Collision_Left
  #Collision_Right
  #Collision_Top
  #Collision_Bottom
EndEnumeration

; Die göttliche Funktion:
Procedure.i RectangleCollision(X1.f, Y1.f, Width1.f, Height1.f, X2.f, Y2.f, Width2.f, Height2.f)
  Protected A.f, B.f
  
  A = (X2 + Width2/2)  - (X1 + Width1/2)
  B = (Y2 + Height2/2) - (Y1 + Height1/2)
  
  If Abs(A) - (Width1/2) - (Width2/2) <= 0 And Abs(B) - (Height1/2) - (Height2/2) <= 0
    If A => 0
      If B => 0
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Left
        Else
          ProcedureReturn #Collision_Top
        EndIf
      Else
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Left
        Else
          ProcedureReturn #Collision_Bottom
        EndIf
      EndIf
    Else
      If B => 0
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Right
        Else
          ProcedureReturn #Collision_Top
        EndIf
      Else
        If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
          ProcedureReturn #Collision_Right
        Else
          ProcedureReturn #Collision_Bottom
        EndIf
      EndIf
    EndIf
  Else
    ProcedureReturn #Null
  EndIf
EndProcedure



#ScreenWidth  = 1024
#ScreenHeight = 768
#ScreenDepth  = 32



Structure Player
  X.f
  Y.f
  Size.i
  MoveX.f
  MoveY.f
EndStructure
Structure Wall
  X.f
  Y.f
  Width.i
  Height.i
EndStructure



Global Player.Player
Global NewList Wall.Wall()
Define Side.i



Procedure.i AddWall(X.i, Y.i, Width.i, Height.i)
  AddElement(Wall())
  
  Wall()\X      = X
  Wall()\Y      = Y
  Wall()\Width  = Width
  Wall()\Height = Height
  
  ProcedureReturn @Wall()
EndProcedure



If Not (InitSprite() And InitKeyboard() And InitMouse())
  MessageRequester("Error", "InitSprite(), InitKeyboard(), InitMouse()")
ElseIf Not OpenScreen(#ScreenWidth, #ScreenHeight, #ScreenDepth, "CollisionTest")
  MessageRequester("Error", "OpenScreen()")
EndIf



Player\Size = 30
Player\X    = #ScreenWidth/2  - Player\Size/2
Player\Y    = #ScreenHeight/2 - Player\Size/2

; Ein paar Wände zum hüpfen:
AddWall(100, 600, 900, 10)
AddWall(200, 500, 80, 10)
AddWall(300, 400, 80, 10)
AddWall(400, 300, 80, 10)
AddWall(500, 200, 80, 10)
AddWall(350, 400, 300, 10)
AddWall(700, 200, 20, 300)
AddWall(800, 200, 20, 700)
AddWall(820, 400, 20, 10)
AddWall(820, 500, 40, 10)
AddWall(900, 300, 40, 10)



Repeat
  ExamineKeyboard()
  ExamineMouse()
  
  ClearScreen(#Black)
    StartDrawing(ScreenOutput())
      ; Draw Player:
      Box(Player\X, Player\Y, Player\Size, Player\Size, #White)
      
      ; Draw all walls:
      ForEach Wall()
        Box(Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height, #Red)
      Next
    StopDrawing()
  FlipBuffers()
  
  If KeyboardPushed(#PB_Key_Left)
    Player\MoveX = -4
  ElseIf KeyboardPushed(#PB_Key_Right)
    Player\MoveX = 4
  Else
    Player\MoveX = 0
  EndIf
  If Jumpable And Player\MoveY = 0 And KeyboardPushed(#PB_Key_Up)
    ; Springen:
    Player\MoveY = 15
    Jumpable = #False
  Else
    ; Die Gravitation zieht den Spieler hinunter:
    Player\MoveY - 0.9
    If Player\MoveY < -12
      Player\MoveY = -12
    EndIf
  EndIf
  
  Player\X + Player\MoveX
  Player\Y - Player\MoveY
  
  ForEach Wall()
    Side = RectangleCollision(Player\X, Player\Y, Player\Size, Player\Size, Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height)
    ;Kollision ist aufgetreten; Welche Seite der Wand ist betroffen?:
    If Side
      Select Side
        Case #Collision_Left
          Player\X = Wall()\X - Player\Size
          Player\MoveX = 0
        Case #Collision_Right
          Player\X = Wall()\X + Wall()\Width
          Player\MoveX = 0
        Case #Collision_Top
          ; Der Spieler ist auf einen Boden gelandet:
          Player\Y = Wall()\Y - Player\Size
          Player\MoveY = 0
          ; Den Spieler wieder springfähig machen:
          Jumpable = #True
        Case #Collision_Bottom
          Player\Y = Wall()\Y + Wall()\Height
          Player\MoveY = 0
      EndSelect
    EndIf
  Next
  
  Delay(13) ;CPU ein bisschen schonen
Until KeyboardPushed(#PB_Key_Escape)
Tata... Die Kollisionsabfrage funktioniert perfekt. Ich denke jeder würde
gerne ein richtiges BreakOut- oder Jump&Run-Game machen. Deswegen
habe ich dies hier gepostet.

Gruß Josef
Zuletzt geändert von Josef Sniatecki am 18.08.2009 12:47, insgesamt 1-mal geändert.
PB 4.61 | Windows Vista - 32Bit
Homepage

"Wahrlich es ist nicht das Wissen, sondern das Lernen, nicht das Besitzen sondern das Erwerben, nicht das Dasein, sondern das Hinkommen, was den grössten Genuss gewährt." - Carl Friedrich Gauß
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Beitrag von DarkDragon »

Hmm naja, also irgendwie fehlt da was:

Was wenn das Rechteck genau auf dem anderen liegt? Was wenn sich das eine Rechteck mit dem anderen in einer Ecke überschneidet? Dann müssten es ja 2 Seiten sein, die überlagert wurden.

Ansonsten ist es gut ;-) .
Ich denke jeder würde gerne ein richtiges BreakOut- oder Jump&Run-Game machen.
Als ob man das nicht auch ohne das hinbekommen würde :freak: . Dann wären ja alle Jump'n'Runs die hier schonmal ansatzweise gebaut wurden nicht richtige Jump'n'Runs. Und das Snookerspiel von STARGATE würde auch nicht richtig sein, da er ja nicht deine Routine verwendet.
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
Benutzeravatar
Josef Sniatecki
Beiträge: 657
Registriert: 02.06.2008 21:29
Kontaktdaten:

Beitrag von Josef Sniatecki »

DarkDragon hat geschrieben:Hmm naja, also irgendwie fehlt da was:

Was wenn das Rechteck genau auf dem anderen liegt? Was wenn sich das eine Rechteck mit dem anderen in einer Ecke überschneidet? Dann müssten es ja 2 Seiten sein, die überlagert wurden.
Ich denke jeder würde
gerne ein richtiges BreakOut- oder Jump&Run-Game machen.
Als ob man das nicht auch ohne das hinbekommen würde. :freak:
Natürlich, diese Varianten gibt es auch. Aber für solche Games wie BreakOut
genügt das. Ich habe auch rechenintensivere Versionen gefunden, in denen
jede Seite eines Rechtecks nach Schnittpunkten geprüft wurde. Aber meiner
Meinung nach ist das für Spiele einfach zu viel.
Deswegen habe ich die einfache und trotzdem schöne Variante genommen.
:wink:

>>Als ob man das nicht auch ohne das hinbekommen würde:
Ich hab mal ein BreakOut-Game versucht. Dort habe ich Rechtecke durch 4 orthogonale
und symmetrische Achsen geteilt. Nachdem ich die Kollision mit den
Teilen versucht habe, ging es manchmal schief, da der Ball öfters Zwei Sektoren trifft. Und wenn es bei
BreakOut schief geht, dann bestimmt auch in vielen Jum&Run-Varianten.

>> Dann wären ja alle Jump'n'Runs die hier schonmal ansatzweise gebaut wurden nicht richtige Jump'n'Runs. Und das Snookerspiel von STARGATE würde auch nicht richtig sein, da er ja nicht deine Routine verwendet.

Das Habe ich ja auch nicht gemeint. Natürlich habe einige ihre eigenen
Routinen. Aber nun mal nicht jeder. Außerdem wird in Snooker
hauptsächlich eine Routine für Kreise genutzt. :wink:

EDIT:
>>Ansonsten ist es gut
Danke :)
PB 4.61 | Windows Vista - 32Bit
Homepage

"Wahrlich es ist nicht das Wissen, sondern das Lernen, nicht das Besitzen sondern das Erwerben, nicht das Dasein, sondern das Hinkommen, was den grössten Genuss gewährt." - Carl Friedrich Gauß
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Beitrag von DarkDragon »

Naja, so wäre es wohl üblicher:

Code: Alles auswählen

Procedure.i RectangleCollision(X1.f, Y1.f, Width1.f, Height1.f, X2.f, Y2.f, Width2.f, Height2.f)
  Protected A.f, B.f
 
  A = (X2 + Width2/2)  - (X1 + Width1/2)
  B = (Y2 + Height2/2) - (Y1 + Height1/2)
 
  If Abs(A) - (Width1/2) - (Width2/2) <= 0 And Abs(B) - (Height1/2) - (Height2/2) <= 0
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure



#ScreenWidth  = 1024
#ScreenHeight = 768
#ScreenDepth  = 32



Structure Player
  X.f
  Y.f
  Size.i
  MoveX.f
  MoveY.f
EndStructure
Structure Wall
  X.f
  Y.f
  Width.i
  Height.i
EndStructure



Global Player.Player
Global NewList Wall.Wall()
Define Side.i



Procedure.i AddWall(X.i, Y.i, Width.i, Height.i)
  AddElement(Wall())
 
  Wall()\X      = X
  Wall()\Y      = Y
  Wall()\Width  = Width
  Wall()\Height = Height
 
  ProcedureReturn @Wall()
EndProcedure



If Not (InitSprite() And InitKeyboard() And InitMouse())
  MessageRequester("Error", "InitSprite(), InitKeyboard(), InitMouse()")
ElseIf Not OpenScreen(#ScreenWidth, #ScreenHeight, #ScreenDepth, "CollisionTest")
  MessageRequester("Error", "OpenScreen()")
EndIf



Player\Size = 30
Player\X    = #ScreenWidth/2  - Player\Size/2
Player\Y    = #ScreenHeight/2 - Player\Size/2

; Ein paar Wände zum hüpfen:
AddWall(100, 600, 900, 10)
AddWall(200, 500, 80, 10)
AddWall(300, 400, 80, 10)
AddWall(400, 300, 80, 10)
AddWall(500, 200, 80, 10)
AddWall(350, 400, 300, 10)
AddWall(700, 200, 20, 300)
AddWall(800, 200, 20, 700)
AddWall(820, 400, 20, 10)
AddWall(820, 500, 40, 10)
AddWall(900, 300, 40, 10)



Repeat
  ExamineKeyboard()
  ExamineMouse()
 
  ClearScreen(#Black)
    StartDrawing(ScreenOutput())
      ; Draw Player:
      Box(Player\X, Player\Y, Player\Size, Player\Size, #White)
     
      ; Draw all walls:
      ForEach Wall()
        Box(Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height, #Red)
      Next
    StopDrawing()
  FlipBuffers()
 
  If KeyboardPushed(#PB_Key_Left)
    Player\MoveX = -4
  ElseIf KeyboardPushed(#PB_Key_Right)
    Player\MoveX = 4
  Else
    Player\MoveX = 0
  EndIf
  If Jumpable And Player\MoveY = 0 And KeyboardPushed(#PB_Key_Up)
    ; Springen:
    Player\MoveY = 15
    Jumpable = #False
  Else
    ; Die Gravitation zieht den Spieler hinunter:
    Player\MoveY - 0.9
    If Player\MoveY < -12
      Player\MoveY = -12
    EndIf
  EndIf
 
  Player\X + Player\MoveX
  
  ; Der Spieler bewegt sich ja nun mit der Geschwindigkeit Player\MoveX in eine Richtung.
  ; Wenn er nun kollidiert bewegen wir ihn in X richtung zurück und setzen Player\MoveX auf 0
 
  ForEach Wall()
    Collided = RectangleCollision(Player\X, Player\Y, Player\Size, Player\Size, Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height)
    ;Kollision ist aufgetreten
    If Collided
      Player\X - Player\MoveX
      Player\MoveX = 0
      Break
    EndIf
  Next
  
  Player\Y - Player\MoveY
  
  ; Der Spieler bewegt sich ja nun mit der Geschwindigkeit Player\MoveY in eine Richtung.
  ; Wenn er nun kollidiert bewegen wir ihn in Y richtung zurück und setzen Player\MoveY auf 0
 
  ForEach Wall()
    Collided = RectangleCollision(Player\X, Player\Y, Player\Size, Player\Size, Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height)
    ;Kollision ist aufgetreten
    If Collided
      If Player\MoveY < 0
        Jumpable = #True
      EndIf
      Player\Y + Player\MoveY
      Player\MoveY = 0
      Break
    EndIf
  Next
 
  Delay(13) ;CPU ein bisschen schonen
Until KeyboardPushed(#PB_Key_Escape)
Desweiteren kannst du so auch die Seite feststellen mit der kollidiert wurde. Wenn die X/Y Geschwindigkeit negativ ist: die linke/obere des anderen Rechtecks, wenn sie positiv ist: die rechte/untere des anderen Rechtecks. Wenn du jetzt statt die Geschwindigkeit auf 0 zu setzen deine Formeln wieder einsetzt hast du dasselbe Ergebnis. Bei einem Breakoutspiel ist es weniger wichtig, den Ball korrekt an den Rand zu setzen, da du dann einfach die Geschwindigkeit X oder Y * -1 nimmst und der Ball sich sowieso wieder entfernt.

Achja, ein Optimierungsforschlag noch:
Ersetze alle / 2 mit * 0.5 oder wenn du Ganzzahlen hast mit >> 1
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
Benutzeravatar
Josef Sniatecki
Beiträge: 657
Registriert: 02.06.2008 21:29
Kontaktdaten:

Beitrag von Josef Sniatecki »

Die Variante funktioniert zwar auch, trotzdem kommt mir meine irgendwie
sanfter vor :wink:.

Zum Optimierungsvorschlag:
Das ist mir bewusst. Multiplikation und Shiften ist schneller als Division.
Macht das aber nicht der PB-Compiler von selbst? Oder irre ich mich da?
PB 4.61 | Windows Vista - 32Bit
Homepage

"Wahrlich es ist nicht das Wissen, sondern das Lernen, nicht das Besitzen sondern das Erwerben, nicht das Dasein, sondern das Hinkommen, was den grössten Genuss gewährt." - Carl Friedrich Gauß
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

hatte ich dir ja glaubich schon mal gegeben, der kompletten thread hast du ja selber oben verlinkt...

die Procedure collCircBox sollte ja genau tun was du willst
http://www.purebasic.fr/german/viewtopi ... 234#129234
wobei es vielleicht sinnvoll wäre, als rückgabewert den jeweiligen sektor anstatt nur #True einzusetzen.

und vielleicht gehst du das problem auch nur falsch an.


bei BreakOut zum Beispiel würde man üblicher weise prüfen, ob der Ball runter zum Schläger kommt.
wenn das der Fall ist, vergleicht mal Schläger-X und Ball-X.
da sieht man, ob der Ball mit dem schläger kollidiert.
oft wird jetzt nur noch die koordinatendifferenz benutzt, um den neuen abschlagwinkel zu ermitteln.

mit den Steinen die man wegschlagen muss ist es noch einfacher:
sobaldder Ball mit irgendeinem stein kollidiert, wird dieser stein gelöscht,
und die jeweilige geschwindigkeit des balls umgedreht.
ob X oder Y oder beide entscheidet man anhand des sektors, wie ihn z.b. meine funktion zurückgibt.


PS:
ups, sorry, hab grad erst gemerkt dass wir hier in C,T&T sind, du also diesmal garnicht gefragt hattest.....
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
DarkDragon
Beiträge: 6291
Registriert: 29.08.2004 08:37
Computerausstattung: Hoffentlich bald keine mehr
Kontaktdaten:

Beitrag von DarkDragon »

Wenn du wie von mir Vorgeschlagen deine Neupositionierung übernimmst geht es sanfter ;-) .
Josef Sniatecki hat geschrieben:Zum Optimierungsvorschlag:
Das ist mir bewusst. Multiplikation und Shiften ist schneller als Division.
Macht das aber nicht der PB-Compiler von selbst? Oder irre ich mich da?
Das sollte er auf Befehl hin tun, er tut es aber nicht.
Angenommen es gäbe einen Algorithmus mit imaginärer Laufzeit O(i * n), dann gilt O((i * n)^2) = O(-1 * n^2) d.h. wenn man diesen Algorithmus verschachtelt ist er fertig, bevor er angefangen hat.
Benutzeravatar
Josef Sniatecki
Beiträge: 657
Registriert: 02.06.2008 21:29
Kontaktdaten:

Beitrag von Josef Sniatecki »

DarkDragon hat geschrieben:Wenn du wie von mir Vorgeschlagen deine Neupositionierung übernimmst geht es sanfter ;-) .
Josef Sniatecki hat geschrieben:Zum Optimierungsvorschlag:
Das ist mir bewusst. Multiplikation und Shiften ist schneller als Division.
Macht das aber nicht der PB-Compiler von selbst? Oder irre ich mich da?
Das sollte er auf Befehl hin tun, er tut es aber nicht.
Ich glaubs nicht, es funzt :shock:

Code: Alles auswählen

Procedure.i RectangleCollision(X1.f, Y1.f, Width1.f, Height1.f, X2.f, Y2.f, Width2.f, Height2.f)
  Protected A.f, B.f
 
  A = (X2 + Width2/2)  - (X1 + Width1/2)
  B = (Y2 + Height2/2) - (Y1 + Height1/2)
 
  If Abs(A) - (Width1/2) - (Width2/2) < 0 And Abs(B) - (Height1/2) - (Height2/2) < 0
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure



#ScreenWidth  = 1024
#ScreenHeight = 768
#ScreenDepth  = 32



Structure Player
  X.f
  Y.f
  Size.i
  MoveX.f
  MoveY.f
EndStructure
Structure Wall
  X.f
  Y.f
  Width.i
  Height.i
EndStructure



Global Player.Player
Global NewList Wall.Wall()
Define Side.i



Procedure.i AddWall(X.i, Y.i, Width.i, Height.i)
  AddElement(Wall())
 
  Wall()\X      = X
  Wall()\Y      = Y
  Wall()\Width  = Width
  Wall()\Height = Height
 
  ProcedureReturn @Wall()
EndProcedure



If Not (InitSprite() And InitKeyboard() And InitMouse())
  MessageRequester("Error", "InitSprite(), InitKeyboard(), InitMouse()")
ElseIf Not OpenScreen(#ScreenWidth, #ScreenHeight, #ScreenDepth, "CollisionTest")
  MessageRequester("Error", "OpenScreen()")
EndIf



Player\Size = 30
Player\X    = #ScreenWidth/2  - Player\Size/2
Player\Y    = #ScreenHeight/2 - Player\Size/2

; Ein paar Wände zum hüpfen:
AddWall(100, 600, 900, 10)
AddWall(200, 500, 80, 10)
AddWall(300, 400, 80, 10)
AddWall(400, 300, 80, 10)
AddWall(500, 200, 80, 10)
AddWall(350, 400, 300, 10)
AddWall(700, 200, 20, 300)
AddWall(800, 200, 20, 700)
AddWall(820, 400, 20, 10)
AddWall(820, 500, 40, 10)
AddWall(900, 300, 40, 10)



Repeat
  ExamineKeyboard()
  ExamineMouse()
 
  ClearScreen(#Black)
    StartDrawing(ScreenOutput())
      ; Draw Player:
      Box(Player\X, Player\Y, Player\Size, Player\Size, #White)
     
      ; Draw all walls:
      ForEach Wall()
        Box(Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height, #Red)
      Next
    StopDrawing()
  FlipBuffers()
 
  If KeyboardPushed(#PB_Key_Left)
    Player\MoveX = -4
  ElseIf KeyboardPushed(#PB_Key_Right)
    Player\MoveX = 4
  Else
    Player\MoveX = 0
  EndIf
  If Jumpable And Player\MoveY = 0 And KeyboardPushed(#PB_Key_Up)
    ; Springen:
    Player\MoveY = 15
    Jumpable = #False
  Else
    ; Die Gravitation zieht den Spieler hinunter:
    Player\MoveY - 0.9
    If Player\MoveY < -12
      Player\MoveY = -12
    EndIf
  EndIf
 
  Player\X + Player\MoveX
 
  ; Der Spieler bewegt sich ja nun mit der Geschwindigkeit Player\MoveX in eine Richtung.
  ; Wenn er nun kollidiert bewegen wir ihn in X richtung zurück und setzen Player\MoveX auf 0
 
  ForEach Wall()
    Collided = RectangleCollision(Player\X, Player\Y, Player\Size, Player\Size, Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height)
    ;Kollision ist aufgetreten
    If Collided
      If Player\MoveX > 0
        ;Links:
        Player\X = Wall()\X - Player\Size
      Else
        ;Rechts:
        Player\X = Wall()\X + Wall()\Width
      EndIf
      Player\MoveX = 0
    EndIf
  Next
 
  Player\Y - Player\MoveY
 
  ; Der Spieler bewegt sich ja nun mit der Geschwindigkeit Player\MoveY in eine Richtung.
  ; Wenn er nun kollidiert bewegen wir ihn in Y richtung zurück und setzen Player\MoveY auf 0
 
  ForEach Wall()
    Collided = RectangleCollision(Player\X, Player\Y, Player\Size, Player\Size, Wall()\X, Wall()\Y, Wall()\Width, Wall()\Height)
    ;Kollision ist aufgetreten
    If Collided
      If Player\MoveY < 0
        Player\Y = Wall()\Y - Player\Size
        Jumpable = #True
      Else
        Player\Y = Wall()\Y + Wall()\Height
      EndIf
      Player\MoveY = 0
      Break
    EndIf
  Next
 
  Delay(13) ;CPU ein bisschen schonen
Until KeyboardPushed(#PB_Key_Escape)
Vielent dank DarkDragon

@Kaeru Gaman:
Eigentlich soll ich mich entschuldigen, da ich hier eine Funktion in C, T&T
gepostet habe, obwohl es noch andere Lösungsvarianten gibt.

Trotzdem habe ich mal deine SectorCollision getestet und bemerkt, dass
sie nicht immer korrekt funktioniert.

Ich habe mal einen Test mit zwei Versionen gemacht. "Version 1" ist meine
ursprüngliche und "Version 2" deine. Nach längerem Herumspielen
mit der Tastatur bemerkt man die ersten Macken bei Version 2.

Code: Alles auswählen

Global Ball_X.f       = 10
Global Ball_Y.f       = 10
Global Ball_MoveX.f   = 5
Global Ball_MoveY.f   = 5
Global Ball_Radius.i  = 8

Global Brick_X.i      = 300
Global Brick_Y.i      = 300
Global Brick_Width.i  = 100
Global Brick_Height.i = 50


;---------------Version 1
; Enumeration 1
;   #Collision_Left
;   #Collision_Right
;   #Collision_Top
;   #Collision_Bottom
; EndEnumeration
; Procedure.i SectorCollision(X1.f, Y1.f, Width1.f, Height1.f, X2.f, Y2.f, Width2.f, Height2.f)
;   Protected A.f, B.f
;  
;   A = (X2 + Width2/2)  - (X1 + Width1/2)
;   B = (Y2 + Height2/2) - (Y1 + Height1/2)
;  
;   If Abs(A) - (Width1/2) - (Width2/2) <= 0 And Abs(B) - (Height1/2) - (Height2/2) <= 0
;     If A => 0
;       If B => 0
;         If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
;           ProcedureReturn #Collision_Left
;         Else
;           ProcedureReturn #Collision_Top
;         EndIf
;       Else
;         If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
;           ProcedureReturn #Collision_Left
;         Else
;           ProcedureReturn #Collision_Bottom
;         EndIf
;       EndIf
;     Else
;       If B => 0
;         If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
;           ProcedureReturn #Collision_Right
;         Else
;           ProcedureReturn #Collision_Top
;         EndIf
;       Else
;         If Abs(A) - (Width1/2) - (Width2/2) > Abs(B) - (Height1/2) - (Height2/2)
;           ProcedureReturn #Collision_Right
;         Else
;           ProcedureReturn #Collision_Bottom
;         EndIf
;       EndIf
;     EndIf
;   Else
;     ProcedureReturn #Null
;   EndIf
; EndProcedure

;-------------Version 2
Enumeration 1
  #Collision_Top
  #Collision_Left
  #Collision_Bottom
  #Collision_Right
EndEnumeration
Procedure.l SectorCollision( x1.l, y1.l, a1.l, b1.l, x2.l, y2.l, a2.l, b2.l )
  Protected dx.l, dy.l
  Protected sx.l, sy.l
  Protected Coll.l = 0
 
  dx = (x1 + a1/2) - (x2 + a2/2)
  If dx < 0
    dx = -dx
    sx = -1
  ElseIf dx > 0
    sx = 1
  EndIf

  dy = (y1 + b1/2) - (y2 + b2/2)
  If dy < 0
    dy = - dy
    sy = -1
  ElseIf dy > 0
    sy = 1
  EndIf
 
  If dx < (a1+a2)/2 And dy < (b1+b2)/2
    If dx < dy
      Coll = 2 + sy
    Else
      Coll = 3 + sx
    EndIf
  EndIf
 
  ProcedureReturn Coll

EndProcedure 



InitSprite()
InitKeyboard()
OpenScreen(1024, 768, 32, "Test")



Repeat
  ExamineKeyboard()
  
  ClearScreen(#Black)
    StartDrawing(ScreenOutput())
      Box(Brick_X, Brick_Y, Brick_Width, Brick_Height, #Red)
      Circle(Ball_X + Ball_Radius, Ball_Y + Ball_Radius, Ball_Radius, #White)
    StopDrawing()
  FlipBuffers()
  
  Ball_X + Ball_MoveX
  Ball_Y + Ball_MoveY
  
  If KeyboardPushed(#PB_Key_Up)
    Ball_MoveY - 0.2
  ElseIf KeyboardPushed(#PB_Key_Down)
    Ball_MoveY + 0.2
  EndIf
  If KeyboardPushed(#PB_Key_Left)
    Ball_MoveX - 0.2
  ElseIf KeyboardPushed(#PB_Key_Right)
    Ball_MoveX + 0.2
  EndIf
  
  If Ball_X < 0
    Ball_X = 0
    Ball_MoveX = -Ball_MoveX
  ElseIf Ball_X > 1024 - Ball_Radius*2
    Ball_X = 1024 - Ball_Radius*2
    Ball_MoveX = -Ball_MoveX
  EndIf
  If Ball_Y < 0
    Ball_Y = 0
    Ball_MoveY = -Ball_MoveY
  ElseIf Ball_Y > 768 - Ball_Radius*2
    Ball_Y = 768 - Ball_Radius*2
    Ball_MoveY = -Ball_MoveY
  EndIf
  
  If Ball_X < Brick_X+Brick_Width And Ball_X+Ball_Radius*2 > Brick_X And Ball_Y < Brick_Y+Brick_Height And Ball_Y+Ball_Radius*2 > Brick_Y
    ;Kollision ist aufgetreten:
    Select SectorCollision(Ball_X, Ball_Y, Ball_Radius*2, Ball_Radius*2, Brick_X, Brick_Y, Brick_Width, Brick_Height)
      Case #Collision_Left
        Ball_X = Brick_X - Ball_Radius*2
        Ball_MoveX = -Ball_MoveX
      Case #Collision_Top
        Ball_Y = Brick_Y - Ball_Radius*2
        Ball_MoveY = -Ball_MoveY
      Case #Collision_Right
        Ball_X = Brick_X + Brick_Width
        Ball_MoveX = -Ball_MoveX
      Case #Collision_Bottom
        Ball_Y = Brick_Y + Brick_Height
        Ball_MoveY = -Ball_MoveY 
    EndSelect
  EndIf
Until KeyboardPushed(#PB_Key_Escape)
Trotzdem Danke für deine Tipps
PB 4.61 | Windows Vista - 32Bit
Homepage

"Wahrlich es ist nicht das Wissen, sondern das Lernen, nicht das Besitzen sondern das Erwerben, nicht das Dasein, sondern das Hinkommen, was den grössten Genuss gewährt." - Carl Friedrich Gauß
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

also, "macken" einer Funktion, die auf die Übergabe von ein paar koordinaten eine sektorinformation zurückgibt,
wo also nix static ist und aufbewahrt wird, können nicht "nach einigem herumspielen mit der tastatur" auftreten.

das was man bei deinem beispiel manchmal sehen kann, muss ich schlicht als "mangelhafte einbindung" bezeichnen,
zumal die ganze umsetzung dieser Demo ja "zweifelhaft" ist, wenn die durch die tastatur gesteuerten bewegungen nicht mal der erfolgten richtungsänderung durch die kollision rechnung tragen.


dafür, dass du ein Beispiel von dir in der C,T&T postest, musst du dich keinesfalls entschuldigen.


aber dass du es auf meinen simplen und funktionellen Algorithmus schiebst,
wenn du dich schwer damit tust ihn richtig zu benutzen, macht mich jetzt doch ein wenig betroffen.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Josef Sniatecki
Beiträge: 657
Registriert: 02.06.2008 21:29
Kontaktdaten:

Beitrag von Josef Sniatecki »

Kaeru Gaman hat geschrieben:also, "macken" einer Funktion, die auf die Übergabe von ein paar koordinaten eine sektorinformation zurückgibt,
wo also nix static ist und aufbewahrt wird, können nicht "nach einigem herumspielen mit der tastatur" auftreten.

das was man bei deinem beispiel manchmal sehen kann, muss ich schlicht als "mangelhafte einbindung" bezeichnen,
zumal die ganze umsetzung dieser Demo ja "zweifelhaft" ist, wenn die durch die tastatur gesteuerten bewegungen nicht mal der erfolgten richtungsänderung durch die kollision rechnung tragen.


dafür, dass du ein Beispiel von dir in der C,T&T postest, musst du dich keinesfalls entschuldigen.


aber dass du es auf meinen simplen und funktionellen Algorithmus schiebst,
wenn du dich schwer damit tust ihn richtig zu benutzen, macht mich jetzt doch ein wenig betroffen.
OK, jetzt muss ich mich wirklich entschuldigen.

Mich würde es aber sehr freuen, wenn du mir zeigen würdest, wie man
deine Funktion richtig einbindet, ohne es sich schwer zu machen :)
PB 4.61 | Windows Vista - 32Bit
Homepage

"Wahrlich es ist nicht das Wissen, sondern das Lernen, nicht das Besitzen sondern das Erwerben, nicht das Dasein, sondern das Hinkommen, was den grössten Genuss gewährt." - Carl Friedrich Gauß
Antworten