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] [EINKLAPPEN]
Const MAX_VERTICES = 256 Const MAX_EDGES = 256 Const MAX_COLLISIONS = 16
Const STEPSIZE = 12
Const GRAVITY# = 0.3
Const NO_SLIDE = True Const SLIDE_SLOPE# = 45.0
Global XSpeed#, YSpeed#
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
Const GWIDTH = 800 Const GHEIGHT = 600
Graphics GWIDTH, GHEIGHT, 0, 2 SetBuffer BackBuffer()
Box.TEntity = CreateBox( 400, 450, 200, 100 ) 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 )
Timer = CreateTimer( 60 )
Local DiffX#, DiffY#, HadContact
While Not KeyHit( 1 ) Cls UpdatePlayer( Player ) Render ( Player ) EntityTurn Circle, 0.5, 400, 200 For I = 0 To Player\CollisionCount - 1 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 ) 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
|