Jump'n'Run Kollision

Übersicht BlitzBasic Codearchiv

Neue Antwort erstellen

Noobody

Betreff: Jump'n'Run Kollision

BeitragDi, Jun 29, 2010 11:55
Antworten mit Zitat
Benutzer-Profile anzeigen
Auf Wunsch habe ich hier noch einen alten Code von mir zum Thema Jump'n'Run Kollision hochgeladen. Er basiert auf dem Separating Axis Theorem, beherrscht also beliebige (konvexe) Formen für Hindernisse und kann somit so ziemlich jede Tileform nachahmen. Da er, anders wie ein anderer Code von mir, nicht die B3D-Kollision benutzt, ist er auch allgemein einsetzbar.

Der Algorithmus erkennt alle Kollisionen, in die der Spieler verwickelt wurde und speichert die Kollisionsnormale der jeweiligen Kollision. Im Beispiel werden die Normalen dazu verwendet, dass der Spieler nur springen kann, wenn er auf etwas draufsteht, dass er von Decken abprallt, wenn er dagegenspringt, und dass er zu steile Ebenen nicht hochlaufen kann.

Dank dem SAT sind natürlich auch andere Formen für den Spieler vorstellbar als ein Rechteck, aber für das Beispiel bot sich die Form so an.

Kurzum, hier der Code BlitzBasic: [AUSKLAPPEN]
;------------------ Kollisionscode ---------- Beispielcode weiter unten

Const MAX_VERTICES = 256 ;Maximale Anzahl Eckpunkte pro Kollisionsobjekt
Const MAX_EDGES = 256 ;Maximale Anzahl Verbindungslinien pro Objekt
Const MAX_COLLISIONS = 16 ;Maximale Anzahl an Kollisionen pro Objekt, die registriert werden

Const STEPSIZE = 12 ;Der Abstand (in Grad) zwischen den Eckpunkten des mit CreateCircle erstellten Kreises

Const GRAVITY# = 0.3 ;Gravitationsstärke

Const NO_SLIDE = True
Const SLIDE_SLOPE# = 45.0

Global XSpeed#, YSpeed# ;Geschwindigkeit des Spielers

Global ProjectedMin#, ProjectedMax#
Global ResultX#, ResultY#

Type TVertex
Field X#
Field Y#

Field Parent.TEntity
End Type

Type TEdge
Field P1.TVertex
Field P2.TVertex

Field Parent.TEntity
End Type

Type TEntity
Field VertexCount
Field Vertex.TVertex[ MAX_VERTICES - 1 ]

Field EdgeCount
Field Edge.TEdge[ MAX_EDGES - 1 ]

Field CenterX#
Field CenterY#

Field CollNX#[ MAX_COLLISIONS ]
Field CollNY#[ MAX_COLLISIONS ]

Field CollX#[ MAX_COLLISIONS ]
Field CollY#[ MAX_COLLISIONS ]

Field CollEntity.TEntity[ MAX_COLLISIONS ]

Field CollisionCount
End Type

Function UpdatePlayer( Player.TEntity )
EntityMove Player, XSpeed#, YSpeed#

UpdateEntities()

For Entity.TEntity = Each TEntity
If Entity <> Player Then Player\CollisionCount = Player\CollisionCount + ProcessCollision( Player, Entity )

If Player\CollisionCount = MAX_COLLISIONS Then Exit
Next

XSpeed# = ( KeyDown( 205 ) - KeyDown( 203 ) )*3

YSpeed# = YSpeed# + GRAVITY#

Jump = KeyHit( 57 )

For I = 0 To Player\CollisionCount - 1
If Sgn( XSpeed# ) = -Sgn( Player\CollNX[ I ] ) And Abs( Player\CollNX[ I ] ) > 0.9 Then XSpeed# = 0

If Player\CollNY[ I ] > 0 Then
If YSpeed# < 0 Then YSpeed# = -YSpeed#*0.1
ElseIf Player\CollNY[ I ] < 0 Then
If YSpeed# > 0 Then YSpeed# = GRAVITY#*3

If Jump Then YSpeed# = -10
EndIf
Next
End Function

Function EntityMove( Entity.TEntity, VX#, VY# )
For i = 0 To Entity\VertexCount - 1
Entity\Vertex[ i ]\X# = Entity\Vertex[ i ]\X# + VX#
Entity\Vertex[ i ]\Y# = Entity\Vertex[ i ]\Y# + VY#
Next
End Function

Function EntityTurn( Entity.TEntity, Rotation#, CenterX#, CenterY# )
For i = 0 To Entity\VertexCount - 1
VX# = Entity\Vertex[ i ]\X# - CenterX#
VY# = Entity\Vertex[ i ]\Y# - CenterY#

Radius# = Sqr( VX#*VX# + VY#*VY# )
Angle# = ATan2( VY#, VX# ) - Rotation#

Entity\Vertex[ i ]\X# = CenterX# + Radius#*Cos( Angle# )
Entity\Vertex[ i ]\Y# = CenterY# + Radius#*Sin( Angle# )
Next
End Function

Function UpdateEntities()
For Entity.TEntity = Each TEntity
Entity\CenterX# = 0
Entity\CenterY# = 0

For i = 0 To Entity\VertexCount - 1
Entity\CenterX# = Entity\CenterX# + Entity\Vertex[ i ]\X#
Entity\CenterY# = Entity\CenterY# + Entity\Vertex[ i ]\Y#
Next

Entity\CenterX# = Entity\CenterX#/Entity\VertexCount
Entity\CenterY# = Entity\CenterY#/Entity\VertexCount

Entity\CollisionCount = 0
Next
End Function

Function Min#( A#, B# )
If A# < B# Then Return A# Else Return B#
End Function

Function Max#( A#, B# )
If A# > B# Then Return A# Else Return B#
End Function

Function CreateBox.TEntity( X#, Y#, Width#, Height# )
Entity.TEntity = New TEntity

P1.TVertex = CreateVertex( Entity, X# - Width#/2, Y# - Height#/2 )
P2.TVertex = CreateVertex( Entity, X# + Width#/2, Y# - Height#/2 )
P3.TVertex = CreateVertex( Entity, X# + Width#/2, Y# + Height#/2 )
P4.TVertex = CreateVertex( Entity, X# - Width#/2, Y# + Height#/2 )

CreateEdge( Entity, P1, P2 )
CreateEdge( Entity, P2, P3 )
CreateEdge( Entity, P3, P4 )
CreateEdge( Entity, P4, P1 )

Entity\CenterX# = X#
Entity\CenterY# = Y#

Return Entity
End Function

Function CreateCircle.TEntity( X#, Y#, RadiusX#, RadiusY# )
Entity.TEntity = New TEntity

For i = STEPSIZE To 360 Step STEPSIZE
P.TVertex = CreateVertex( Entity, X# + Cos( i )*RadiusX#, Y# + Sin( i )*RadiusY# )
If i <> STEPSIZE Then CreateEdge( Entity, P, Before P )
Next

CreateEdge( Entity, P, Entity\Vertex[ 0 ] )

Return Entity
End Function

Function CreateVertex.TVertex( Parent.TEntity, X#, Y#, Mass# = 1 )
If Parent <> Null Then
If Parent\VertexCount < MAX_VERTICES - 1 Then
Vertex.TVertex = New TVertex
Vertex\X# = X#
Vertex\Y# = Y#
Vertex\Parent = Parent

Parent\Vertex[ Parent\VertexCount ] = Vertex
Parent\VertexCount = Parent\VertexCount + 1

Return Vertex
EndIf
EndIf
End Function

Function CreateEdge.TEdge( Parent.TEntity, P1.TVertex, P2.TVertex )
If Parent <> Null Then
If Parent\EdgeCount < MAX_EDGES - 1 Then
If P1 <> P2 Then
Edge.TEdge = New TEdge
Edge\P1 = P1
Edge\P2 = P2
Edge\Parent = Parent

Parent\Edge[ Parent\EdgeCount ] = Edge
Parent\EdgeCount = Parent\EdgeCount + 1

Return Edge
EndIf
EndIf
EndIf
End Function

Function Dotproduct#( X1#, Y1#, X2#, Y2# )
Return X1#*X2# + Y1#*Y2#
End Function

Function Normalize( X#, Y# )
Length# = Sqr( X#*X# + Y#*Y# )
ResultX# = X#/Length#
ResultY# = Y#/Length#
End Function

Function ProjectToAxis( AxisX#, AxisY#, Entity.TEntity )
Local DotP# = Dotproduct( AxisX#, AxisY#, Entity\Vertex[ 0 ]\X#, Entity\Vertex[ 0 ]\Y# )
ProjectedMin# = DotP#
ProjectedMax# = DotP#

For i = 1 To Entity\VertexCount - 1
DotP# = Dotproduct( AxisX#, AxisY#, Entity\Vertex[ i ]\X#, Entity\Vertex[ i ]\Y# )

If DotP# < ProjectedMin# Then ProjectedMin# = DotP# ElseIf DotP# > ProjectedMax# Then ProjectedMax# = DotP#
Next
End Function

Function IntervalDistance#( A1#, B1#, A2#, B2# )
If A1# < A2# Then
Return A2# - B1#
Else
Return A1# - B2#
EndIf
End Function

Function ProcessCollision( E1.TEntity, E2.TEntity )
Local MinDistance# = 100000, CollAxisX#, CollAxisY#, EdgeEntity.TEntity
For i = 0 To E1\EdgeCount + E2\EdgeCount - 1
If i < E1\EdgeCount Then
Edge.TEdge = E1\Edge[ i ]
Else
Edge.TEdge = E2\Edge[ i - E1\EdgeCount ]
EndIf

AxisX# = -( Edge\P2\Y# - Edge\P1\Y# )
AxisY# = Edge\P2\X# - Edge\P1\X#
Normalize( AxisX#, AxisY# )
AxisX# = ResultX#
AxisY# = ResultY#

ProjectToAxis( AxisX#, AxisY#, E1 )
Min# = ProjectedMin#
Max# = ProjectedMax#
ProjectToAxis( AxisX#, AxisY#, E2 )

Distance# = IntervalDistance( Min#, Max#, ProjectedMin#, ProjectedMax# )
If Distance# > 0 Then
Return False
ElseIf Abs( Distance# ) < Abs( MinDistance# ) Then
MinDistance# = Abs( Distance# )
CollAxisX# = AxisX#
CollAxisY# = AxisY#

DiffX# = E1\CenterX# - E2\CenterX#
DiffY# = E1\CenterY# - E2\CenterY#

If Dotproduct( CollAxisX#, CollAxisY#, DiffX#, DiffY# ) < 0 Then
CollAxisX# = -CollAxisX#
CollAxisY# = -CollAxisY#
EndIf

EdgeEntity = Edge\Parent
EndIf
Next

Local Result#[ 2 ]
GetNearestPoint( E1, E2, EdgeEntity, CollAxisX#, CollAxisY#, Result )

If NO_SLIDE And CollAxisY# < 0.0 And ACos( -CollAxisY# ) < SLIDE_SLOPE Then
For i = 0 To E1\VertexCount - 1
E1\Vertex[ i ]\Y# = E1\Vertex[ i ]\Y# + MinDistance#*Sgn( CollAxisY# )
Next
Else
For i = 0 To E1\VertexCount - 1
E1\Vertex[ i ]\X# = E1\Vertex[ i ]\X# + MinDistance#*CollAxisX#
E1\Vertex[ i ]\Y# = E1\Vertex[ i ]\Y# + MinDistance#*CollAxisY#
Next
EndIf

TFormNormal CollAxisX#, CollAxisY#, 0, 0, 0

E1\CollNX [ E1\CollisionCount ] = TFormedX()
E1\CollNY [ E1\CollisionCount ] = TFormedY()
E1\CollX [ E1\CollisionCount ] = Result[ 0 ]
E1\CollY [ E1\CollisionCount ] = Result[ 1 ]
E1\CollEntity[ E1\CollisionCount ] = E2

Return True
End Function

Function GetNearestPoint( E1.TEntity, E2.TEntity, EdgeEntity.TEntity, CollAxisX#, CollAxisY#, Result#[ 2 ] )
If EdgeEntity <> E2 Then
Temp.TEntity = E2
E2 = E1
E1 = Temp
EndIf

Local C# = -E2\CenterX#*CollAxisX# - E2\CenterY#*CollAxisY#

Local Sign = Sgn( E1\CenterX#*CollAxisX# + E1\CenterY#*CollAxisY# + C# )

Local SmallestY# = 10000
For i = 0 To E1\VertexCount - 1
Vertex.TVertex = E1\Vertex[ i ]

D# = Sign*( Vertex\X#*CollAxisX# + Vertex\Y#*CollAxisY# ) + C#

If D# < SmallestY# Then
SmallestY# = D#

Result[ 0 ] = Vertex\X#
Result[ 1 ] = Vertex\Y#
EndIf
Next
End Function



;------------------------------------ Beispielcode --------------------------

Const GWIDTH = 800
Const GHEIGHT = 600

Graphics GWIDTH, GHEIGHT, 0, 2
SetBuffer BackBuffer()

Box.TEntity = CreateBox( 400, 450, 200, 100 ) ;Level erstellen
EntityTurn Box, 10, 500, 500

Box.TEntity = CreateBox( 530, 452, 50, 200 )
EntityTurn Box, -10, 555, 550

CreateCircle( 700, 390, 130, 50 )
CreateCircle( 200, 500, 100, 90 )
CreateBox ( 400, 600, 800, 10 )

Circle.TEntity = CreateCircle( 300, 300, 50, 50 )

Player.TEntity = CreateBox( 50, 50, 20, 40 ) ;In diesem Beispiel ist der Spieler eine Box. Selbstverständlich gehen auch andere Formen

Timer = CreateTimer( 60 )

Local DiffX#, DiffY#, HadContact

While Not KeyHit( 1 )
Cls

UpdatePlayer( Player )
Render ( Player )

EntityTurn Circle, 0.5, 400, 200 ;Den Kreis um den Bildschirmmittelpunkt kreisen lassen

For I = 0 To Player\CollisionCount - 1 ;Hier noch ein kleiner Beispielcode, wie man den Spieler auf einem bewegenden Objekt halten kann
If Player\CollEntity[ I ] = Circle Then
If HadContact And Player\CollNY[ I ] < 0.0 Then
DiffX# = DiffX# + XSpeed#

EntityMove Player, Circle\CenterX# + DiffX# - Player\CenterX#, Circle\CenterY# + DiffY# - Player\CenterY#
Else
HadContact = True

DiffX# = Player\CenterX# - Circle\CenterX#
DiffY# = Player\CenterY# - Circle\CenterY#
EndIf

Exit
ElseIf I = Player\CollisionCount - 1 Then
HadContact = False
EndIf
Next

If Not Player\CollisionCount Then HadContact = False

Flip 0
WaitTimer Timer
Wend
End



Function Render( Player.TEntity ) ;Simple Funktion zum rendern
LockBuffer BackBuffer()

For Edge.TEdge = Each TEdge
If Edge\Parent\CollisionCount Then Color 255, 0, 0 Else Color 0, 255, 0
Line Edge\P1\X#, Edge\P1\Y#, Edge\P2\X#, Edge\P2\Y#
Next

For Vertex.TVertex = Each TVertex
If Vertex\X# >= 0 And Vertex\X# < GWIDTH Then
If Vertex\Y# >= 0 And Vertex\Y# < GHEIGHT Then
WritePixelFast Floor( Vertex\X# ), Floor( Vertex\Y# ), $00FFFFFF
EndIf
EndIf
Next

UnlockBuffer BackBuffer()

Color 255, 255, 255
Text 0, 0, "Anzahl Kollisionen: " + Player\CollisionCount

For I = 0 To Player\CollisionCount - 1
Text 0, 15 + I*15, "Kollisionsnormale " + ( I + 1 ) + ": " + Player\CollNX[ I ] + " " + Player\CollNY[ I ]
Next
End Function
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun
  • Zuletzt bearbeitet von Noobody am Di, Jun 29, 2010 20:17, insgesamt 2-mal bearbeitet

ozzi789

BeitragDi, Jun 29, 2010 19:59
Antworten mit Zitat
Benutzer-Profile anzeigen
Sehr schön,
Danke!

Kommt gleich in meine Code-Sammlung.

mfg ozzi
0x2B || ! 0x2B
C# | C++13 | Java 7 | PHP 5

Noobody

BeitragDi, Jun 29, 2010 20:17
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe heute den Code nochmals überarbeitet, damit der Charakter nicht mehr schräge Flächen hinunterrutscht. Mithilfe der Konstante NO_SLIDE kann man einstellen, ob der Spieler schräge Flächen runterrutscht (False) oder eben nicht (True). Hat man NO_SLIDE auf True, so kann man mithilfe der Konstante SLIDE_SLOPE ausserdem einstellen, bis wieviel Grad Steigung der Charakter noch auf der Fläche halten soll. Im Beispielcode sind jetzt 45° eingestellt, weswegen er auf der Oberseite von Kugeln und den schwach schrägen Boxen festsitzt, dafür beim engen Spalt hinunterrutscht. Für unbegrenztes Halten einfach SLIDE_SLOPE auf Werte grösser als 90 setzen.

Als weiteres Beispiel habe ich ein kleines Codebeispiel hinzugefügt, um zu zeigen, wie man den Charakter dazu bringen kann, mit sich bewegenden Objekten mitzufahren. Falls man irgendwo in seinem Spiel herumfahrende Plattformen hat, ist das ganz nützlich. Zum ausprobieren springt einfach mal auf die rotierende Kugel, dann seht ihr, was ich meine.

Den Code habe ich oben nacheditiert.
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun

Neue Antwort erstellen


Übersicht BlitzBasic Codearchiv

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group