Collisions - Macros und Procs

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.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Collisions - Macros und Procs

Beitrag 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.
Zuletzt geändert von Kaeru Gaman am 20.10.2006 23:24, insgesamt 1-mal geändert.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag 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
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Fluid Byte
Beiträge: 3110
Registriert: 27.09.2006 22:06
Wohnort: Berlin, Mitte

Beitrag 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.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag 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.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Eric
Beiträge: 303
Registriert: 05.09.2004 09:50
Wohnort: Göttingen

Beitrag 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.
El_Choni_work: cant't you just spit the binary data to sqlite, as you would spit a hamster into a microwave oven?
* Fangles falls off the chair laughing
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8807
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag 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
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag 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..
Zuletzt geändert von Kaeru Gaman am 10.10.2006 23:42, insgesamt 1-mal geändert.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8807
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

Coo..ool. <)
Benutzeravatar
Fluid Byte
Beiträge: 3110
Registriert: 27.09.2006 22:06
Wohnort: Berlin, Mitte

Beitrag 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:
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8807
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag 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)
Antworten