Seite 1 von 2

Collisions - Macros und Procs

Verfasst: 02.07.2006 16:44
von Kaeru Gaman
es wird immer wieder so viel über Kollisionen geredet, da hab ich mich mal hingesetzt und ein paar Macros und Procs geschrieben.

die Proceduren haben das Präfix P_ vor dem Macronamen gleicher funktion.

folgendes ist hier enthalten:

AbsEx(Wert)
berechnung des absoluten Wertes einer Zahl.
wird benötigt für die Bounding-Box-Collision.
vielen Dank an Remi für die eliminierung eines unerklärlichen fehlers.

StdColl(x1,y1,a1,b1,x2,y2,a2,b2)
die Standard-Collision, funktioniert genauso wie SpriteCollision(), nur dass hier keine SpriteNummer angegeben wird, sondern Höhe und Breite des Objekts.
dadurch spart sie funktionsaufrufe, und ist außerdem auch für mittels DirectDraw gezeichnete Objekte verwendbar (z.b. Box() )

BoundBoxColl(x1,y1,a1,b1,x2,y2,a2,b2)
Bounding-Box-Collision
hier sind die Koordinaten der Mittelpunkt der Box, die Dimensionen sind der abstand zum Rand, also die Hälfte der Breite bzw. Höhe.

BoundCircleColl(x1,y1,r1,x2,y2,r2)
Bounding-Circle-Collision
auch hier wird der mittelpunkt angegeben, und der jeweilige radius.
für viele spielelemente (raumschiffe etc.) entspricht ein Umkreis eher den ausmaßen des objekts als ein Kasten.
ich musste hier leider SQR() verwenden, auf die schnelle hab ich kein workaround erdenken können, was diese zweifellos langwierige funktion überflüssig macht.
Achtung:
aber NTQ hat es gelöst. :allright: thnx, Nic
ich habe seine verbesserungen ins Listing übernommen.

natürlich hätte ich bei den beiden letzten Procs die formeln direkt ins If setzen können, ich habe aber mal die zwischenvariablen beibehalten, wie in der ersten auch, wo sie notwendig sind.

!!HINWEIS!!
bei verwendung der Macros bitte nur einzelwerte/einzelvariablen übergeben, keine ausdrücke. besonders bei den ersten beiden kann es sonst zu einem fehler kommen: "Expression too complex, out of CPU-registers"
die Prozeduren sind wahrscheinlich etwas langsamer (hab jetzt keinen performance-check durchgeführt) aber dafür können die Argumente auch komplexere ausdrücke sein.
außerdem sollten die Procs auch in PB 3.94 funktionieren, möglicherweise auch in 3.30 (nicht getestet)

und hier ist der code:

Code: Alles auswählen

; ********************************************************
; ***
; ***  Collision Macros / Procedures
; ***
; ***  by Kaeru Gaman 2006-07-02
; ***
; ***  PB Ver 4.0
; ***
; ********************************************************

; ***
; ***   Absolute Value
; ***

Macro AbsEx(Expression) 
  ( ( ((Expression) >= 0 Or 0) *2 - 1  ) * (Expression) ) 
EndMacro

Procedure P_AbsEx(Wert)
  
  If Wert < 0
    wert *-1
  EndIf

  ProcedureReturn Wert

EndProcedure

; ***
; ***   Standard Collision, equal to SpriteCollision()
; ***

Macro StdColl(x1,y1,a1,b1,x2,y2,a2,b2)
 (0 Or (0>=((0 Or (x1 < x2))*(x2 - (x1 + a1)))+((0 Or (x1 >= x2))*(x1 - (x2 + a2)))))And(0 Or (0>=((0 Or (y1 < y2))*(y2 - (y1 + b1)))+((0 Or (y1 >= y2))*(y1 - (y2 + b2)))))
EndMacro

Procedure P_StdColl(x1,y1,a1,b1,x2,y2,a2,b2)
  Coll = #False
  
  If x1 < x2
    dx = x2 - (x1 + a1)
  Else
    dx = x1 - (x2 + a2)
  EndIf
  If y1 < y2
    dy = y2 - (y1 + b1)
  Else
    dy = y1 - (y2 + b2)
  EndIf
  
  If dx < 0 And dy < 0
    Coll = #True
  EndIf
  
  ProcedureReturn Coll

EndProcedure

; ***
; ***   Bounding Box
; ***

Macro BoundBoxColl(x1,y1,a1,b1,x2,y2,a2,b2)
 ( ( 0 Or ((AbsEx(x1-x2) - a1 - a2)<0)) And ( 0 Or ((AbsEx(y1-y2) - b1 - b2)<0)) )
EndMacro

Procedure P_BoundBoxColl(x1,y1,a1,b1,x2,y2,a2,b2)
  Coll = #False
  
  dx = P_AbsEx(x1-x2) - a1 - a2
  dy = P_AbsEx(y1-y2) - b1 - b2
  
  If dx < 0 And dy < 0
    Coll = #True
  EndIf
  
  ProcedureReturn Coll

EndProcedure

; ***
; ***   Bounding Circle
; ***

Macro BoundCircleColl(x1,y1,r1,x2,y2,r2) 
 ( 0 Or (( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) - (r1 + r2) * (r1 + r2) ) < 0) ) 
EndMacro 

Procedure P_BoundCircleColl(x1,y1,r1,x2,y2,r2) 
  Coll = #False 

  dist = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) - (r1 + r2) * (r1 + r2) 
  
  If dist < 0 
    Coll = #True 
  EndIf 

  ProcedureReturn Coll 

EndProcedure
ergänzung: das Macro AbsEx() ist sau lahm, ich empfehle, auch im Bounding-Box-Macro die Procedure für AbsEx zu benutzen.

Verfasst: 03.07.2006 08:58
von Kaeru Gaman
NEWS:

das AbsEx()-Macro hab ich überarbeitet:
es enthält nun den ausdruck nicht mehr 4x, sondern nur noch 2x, und sollte somit deutlich schneller sein.
ob es aber mit der proc schon mithalten kann, ist fraglich, besonders bei komplexen ausdrücken.

Code: Alles auswählen

Macro AbsEx(Expression) 
  ( ( ((Expression) >= 0 Or 0) *2 - 1  ) * (Expression) ) 
EndMacro

Verfasst: 10.10.2006 23:10
von Fluid Byte
StdColl(x1,y1,a1,b1,x2,y2,a2,b2)
die Standard-Collision, funktioniert genauso wie SpriteCollision(), nur dass hier keine SpriteNummer angegeben wird, sondern Höhe und Breite des Objekts.
dadurch spart sie funktionsaufrufe, und ist außerdem auch für mittels DirectDraw gezeichnete Objekte verwendbar (z.b. Box() )
Läuft das nicht auf's gleiche hinaus?

SpriteCollision() holt sich intern (wie auch immer) die Abmessungen des Sprites via der angegeben ID. Mit StdColl() oder BoundBoxColl() gibt man zwar direkt Werte für die Abmessungen an aber diese müssen ja ebenso mit Funktionsaufrufen wie z.B. SpriteWidth() / SpriteHeight() oder ähnlichem ermittelt werden.

Verfasst: 10.10.2006 23:20
von Kaeru Gaman
> aber diese müssen ja ebenso mit Funktionsaufrufen wie z.B. SpriteWidth() / SpriteHeight() oder ähnlichem ermittelt werden.

nun, vielleicht weißt du sie ja.
und wenn nicht, ermittelst du sie einmal VOR der schleife, und verwendest dann die werte.
außerdem, wie gesagt, es ist für jedes beliebige areal verwendbar,
egal, ob es einer Box, einer gruppe von objekten, oder einfach nur einem abstrakten bereich entspricht. ;)
z.b. kann man an SpriteCollision bemängeln, dass immer der komplette bereich getestet wird.
aber evtl hat man drumrum nen rahmen oder nen freien bereich,
der gar keine kollision auslösen soll?
zum beispiel nen schatten an nem sprite-button?

außerdem dürfte eigentlich die reine koordinaten-lösung schneller sein,
teste halt mal bei dir die performance.

Verfasst: 10.10.2006 23:25
von Eric
>aber diese müssen ja ebenso mit Funktionsaufrufen wie z.B. SpriteWidth() / SpriteHeight() oder ähnlichem ermittelt werden.
>>und ist außerdem auch für mittels DirectDraw gezeichnete Objekte verwendbar (z.b. Box() )
z.B. für Box() gibt es kein SpriteWidth()/SpriteHeight() und in Situationen, wo
man eine Kollision braucht, aber nicht zeichnen will, ist es so viel flexibler.

Verfasst: 10.10.2006 23:26
von NicTheQuick
So sollte es auch ohne SQR funktionen. Habs aber nicht getestet:

Code: Alles auswählen

Macro BoundCircleColl(x1,y1,r1,x2,y2,r2)
 ( 0 Or (( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) - (r1 + r2) * (r1 + r2) ) < 0) )
EndMacro

Procedure P_BoundCircleColl(x1,y1,r1,x2,y2,r2)
  Coll = #False

  dist = (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) - (r1 + r2) * (r1 + r2)
 
  If dist < 0
    Coll = #True
  EndIf

  ProcedureReturn Coll

EndProcedure
Aber seit wann kann PureBasic mit logische Ausdrücken umgehen? Das
geht doch gar nicht:

Code: Alles auswählen

a.l = 1
b.l = 2
c.l = a < b

Debug c

c = a > b

Debug c

Verfasst: 10.10.2006 23:31
von Kaeru Gaman
> Aber seit wann kann PureBasic mit logische Ausdrücken umgehen?

( 0 Or (( ist der zauberspruch, entspricht einem #False Or,
dann kann PB plötzlich damit umgehen, als ob ein Bool-Cast auf den Ausdruck stattfindet.

[edit]
> So sollte es auch ohne SQR funktionen. Habs aber nicht getestet:
ehrlich bin ich jetzt grad zu faul, das mal durchzurechnen.
ist möglich, dass es funktioniert, muss aber nicht sein.
quadrat ist nicht linear. bei etwas linearem wärs logisch, dass es geht...
...also bei quadrat.... ich tipp erstmal auf nein..

Verfasst: 10.10.2006 23:40
von NicTheQuick
Coo..ool. <)

Verfasst: 11.10.2006 18:01
von Fluid Byte
außerdem dürfte eigentlich die reine koordinaten-lösung schneller sein, teste halt mal bei dir die performance.
Bin grad' dabei nen' Test Programm zu schreiben welches die Makro, Funktions- und auch PB interne Variante miteinander vergleicht.

Ma' guckn' ob die Ergebnisse repräsentativ sind oder doch nur Humbug. :roll:

Verfasst: 12.10.2006 10:10
von NicTheQuick
@Kaeru:
Da es sich nur um ein größer-oder-kleiner Null handelt, ist das so richtig.
Vielleicht überzeugt dich ja die Rechnung:

Code: Alles auswählen

0          > Sqr( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) ) - r1 - r2  | + r1 + r2
r1 + r2    > Sqr( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) )            | (...)²
(r1 + r2)² > (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)                   | - (r1 + r2)²
0          > (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) - (r1 + r2)²
0          > (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) - (r1+r2)*(r1+r2)