sigh, more tile based collision detection, sorry!
BlitzPlus Forums/BlitzPlus Programming/sigh, more tile based collision detection, sorry!
| ||
| Hi Sorry, yet another tb collision post! But at least my code works! I was reading this article and based my code on roughly on theirs: http://www.tonypa.pri.ee/tbw/tut05.html I know my code is messy and could be reduced considerably, but I just wanted to get something working and i'll refactor it when ive got somewheres. Im not a coding noob and I dont like copying and pasting unless I know what its doing, so most of the code is mine, the movement code i wrote from reading a sonic guide. Its 4am so forgive me, but if you have a look at my CheckCollision2(), it basically calls GetMyCorners(curX, newY), then does Y detection, then calls GetMyCorders(newX, newY) and does X detection. I cant understand why I need to call GetMyCorners a third time and add 1 to newX. I know why it works, because otherwise its 1px short of detecting the right hand tile, but the guide above doesnt have to do this. Anyway, any help would be useful, and heres some code: oh you'll need a "graphics\tiles1.png" 32x32, just one tile tho Cheers! Const SCR_W# = 400
Const SCR_H# = 400
Graphics SCR_W, SCR_H
SetBuffer BackBuffer()
Type Player
Field w, h
Field MapX#, MapY#, ScrX#, ScrY#
Field XSpeed#, YSpeed#, MoveX#, MoveY#
Field Airborne, Jumping, AbleToJump
Field upY, downY, leftX, rightX, standingY
Field topLeftType, topRightType, bottomLeftType, bottomRightType
Field standingLeftType, standingRightType
Field xtile, ytile
End Type
Global p.Player = New Player
PrepPlayerStart()
GameTimer = CreateTimer(60)
Global FloorY = 580
Color 255,255,255
; math functions
Function Min(n1%, n2%)
If n1% < n2% Then Return n1% Else Return n2%
End Function
Function MinFloat#(n1#, n2#)
If n1# < n2# Then Return n1# Else Return n2#
End Function
Function Max(n1%, n2%)
If n1% > n2% Then Return n1% Else Return n2%
End Function
Function MaxFloat#(n1#, n2#)
If n1# > n2# Then Return n1# Else Return n2#
End Function
;;;; MOVE VARS
;Global XSpeed# = 0
Global Acc# = 0.046875
Global Dec# = 0.5
Global Frc# = 0.046875
Global MaxXSpeed# = 6
Global Gravity# = 0.21875
Global JumpSpeed# = -6
Global SmallJumpSpeed# = -4
Global MaxYSpeed# = 12
;;;; IN-AIR VARS
Global AirMaxXSpeed# = 6
Global Air# = 0.09375 ; acc and speed really, no such thing as AirSpeed or frc or dec, just 'air'
;;;; MAP VARS
Global CAM_W# = SCR_W
Global CAM_H# = SCR_H
Global CamX# = 0
Global CamY# = 0
Global TILE_SIZE = 32
Global MAP_W = 20 ; in tiles
Global MAP_H = 20
Dim map(MAP_H,MAP_W)
Dim tileType(MAP_H,MAP_W)
Const TILE_SOLID = 1
Const TILE_EMPTY = 0
ReadMapData()
ReadTileTypeData()
Global NumTiles = 1
Global img_tiles = LoadAnimImage("graphics\tiles1.png",32,32,0,NumTiles)
Global MapWidthPx = MAP_W * TILE_SIZE
Global MapHeightPx = MAP_H * TILE_SIZE
ClsColor 164,211,238
;;;; MAIN LOOP
While Not KeyHit(1)
UpdatePlayerVel()
; gravity
ApplyGravity()
; set move distances to speed increment
p\MoveX = p\XSpeed
p\MoveY = p\YSpeed
; reduce move distances if going out of map bounds
EnforceMapBounds2()
; reduce move distances if collision occurs
CheckCollision2()
; add move distances to map position
UpdatePlayerMapPos()
; position camera around player
UpdateCamPos()
; move player screen position according to how much camera has moved
UpdatePlayerScrPos()
DrawMap()
Rect p\ScrX, p\ScrY, 10, 10, 1
;Line 0,300,399,300
Text 0,10,"XSpeed:" + p\XSpeed + " | YSpeed: " + p\YSpeed
Text 0,23,"ScrX:"+p\ScrX+" | ScrY:"+p\ScrY
Text 0,36,"MoveX:"+p\MoveX+" | MoveY:"+p\MoveY
Text 0,49,"MapX:"+p\MapX+"|MapY:"+p\MapY
WaitTimer GameTimer
Flip
Cls
Wend
End
Function UpdatePlayerMapPos()
p\MapX = p\MapX + p\MoveX
p\MapY = p\MapY + p\MoveY
p\xtile = Floor(p\MapX / TILE_SIZE)
p\ytile = Floor(p\MapY / TILE_SIZE)
;Print p\MapY
;WaitKey
End Function
Function PlayerLanded()
p\AbleToJump = True
p\Jumping = False
p\Airborne = False
p\YSpeed = 0
JumpKeyPressed = False
End Function
Function GoneAirborne()
p\AbleToJump = False
p\Jumping = False
p\Airborne = True
JumpKeyPressed = False
End Function
Function ApplyGravity()
If p\Airborne Then
p\YSpeed = p\YSpeed + Gravity
If p\YSpeed > MaxYSpeed Then p\YSpeed = MaxYSpeed
EndIf
End Function
Function PrepPlayerStart()
p\MapX = 0
p\MapY = 0
p\ScrX = 0
p\ScrY = 0
p\XSpeed = 0
p\YSpeed = 0
p\MoveX = 0
p\MoveY = 0
p\Airborne = True
p\Jumping = False
p\AbleToJump = False
p\w = 10
p\h = 10
End Function
Function EnforceMapBounds2()
If p\MapX + p\MoveX + 10 > MapWidthPx Then
p\MoveX = MapWidthPx - p\MapX - 10
p\XSpeed = 0
;DebugLog("1")
ElseIf p\MapX + p\MoveX < 0 Then
p\MoveX = p\MapX * -1
p\XSpeed = 0
;DebugLog("2")
EndIf
If p\MapY + p\MoveY + 10 > MapHeightPx Then
p\MoveY = MapHeightPx - p\MapY - 10
p\YSpeed = 0
;DebugLog("3")
ElseIf p\MapY + p\MoveY < 0 Then
p\MoveY = p\MapY * -1
p\YSpeed = 0
;DebugLog("4")
EndIf
End Function
Function DrawMap()
offsetX# = CamX
offsetY# = CamY
startX = Floor(offsetX / TILE_SIZE)
endX = Floor((offsetX + CAM_W-1) / TILE_SIZE)
startY = Floor(offsetY / TILE_SIZE)
endY = Floor((offsetY + CAM_H-1) / TILE_SIZE)
;DebugLog "startX:" + startX + " | endX:" + endX + " | startY:" + startY + " | endY:" + endY
For y = startY To endY
For x = startX To endX
t = Int(map(y,x))-1
;Print "map("+y+","+x+") = " + t
If t > -1 DrawTile(t, x * TILE_SIZE - offsetX, y * TILE_SIZE - offsetY)
Next
Next
End Function
Function DrawTile(t, x, y)
DrawImage img_tiles,x,y,t
End Function
Function ReadMapData()
Restore mapData
For y = 0 To 19
For x = 0 To 19
Read map(y,x)
Next
Next
End Function
Function ReadTileTypeData()
Restore tileTypeData
For y = 0 To 19
For x = 0 To 19
Read tileType(y,x)
Next
Next
End Function
Function GetMyCorners(x#, y#, ob.Player)
; tile co-ords
ob\upY = Floor( y / TILE_SIZE )
ob\downY = Floor( (y + ob\h - 1) / TILE_SIZE )
ob\leftX = Floor( x / TILE_SIZE )
ob\rightX = Floor( (x + ob\w - 1) / TILE_SIZE )
ob\standingY = Floor( (y + ob\h) / TILE_SIZE )
;DebugLog "upY:"+ob\upY+"|downY:"+ob\downY+"|leftX:"+ob\leftX+"|rightX:"+ob\rightX+"|ob\w:"+ob\w+"|h:"+ob\h
ob\topLeftType = tileType(ob\upY, ob\leftX)
ob\topRightType = tileType(ob\upY, ob\rightX)
ob\bottomLeftType = tileType(ob\downY, ob\leftX)
ob\bottomRightType = tileType(ob\downY, ob\rightX)
ob\standingLeftType = tileType(ob\standingY, ob\leftX)
ob\standingRightType = tileType(ob\standingY, ob\rightX)
End Function
Function CheckCollision2()
Local newY# = p\MapY + p\MoveY
;DebugLog p\w
GetMyCorners(p\MapX, newY, p)
Local dirY = Sgn(p\MoveY)
;If p\MoveY < 0 dirY = -1
;If p\MoveY > 0 dirY = 1
; UP
If dirY < 0 Then
If p\topLeftType = TILE_SOLID Or p\topRightType = TILE_SOLID Then
p\MoveY = ((p\upY+1) * TILE_SIZE) - p\MapY
p\YSpeed = 0
DebugLog "boink head"
EndIf
EndIf
; DOWN
If dirY > 0 Then
If p\bottomLeftType = TILE_SOLID Or p\bottomRightType = TILE_SOLID Then
;DebugLog "old MoveY:"+p\MoveY+"|new MoveY:"
p\MoveY = (p\downY * TILE_SIZE) - (p\MapY+p\h)
;DebugLog p\MoveY
DebugLog "landed"
p\YSpeed = 0
PlayerLanded()
EndIf
EndIf
newY = p\MapY + p\MoveY
Local newX# = p\MapX + p\MoveX
Local dirX = Sgn(p\MoveX)
;If p\MoveX < 0 dirX = -1
;If p\MoveX > 0 dirX = 1
GetMyCorners(newX, newY, p)
; LEFT
If dirX < 0 Then
If p\topLeftType = TILE_SOLID Or p\bottomLeftType = TILE_SOLID Then
p\MoveX = ((p\leftX+1) * TILE_SIZE) - p\MapX
p\XSpeed = 0
EndIf
EndIf
GetMyCorners(newX+1, newY, p)
; RIGHT
If dirX > 0 Then
If p\topRightType = TILE_SOLID Or p\bottomRightType = TILE_SOLID Then
p\MoveX = ((p\rightX) * TILE_SIZE) - (p\MapX+p\w)
p\XSpeed = 0
EndIf
EndIf
GetMyCorners(p\MapX+p\MoveX, newY, p)
If p\Airborne = False And ( p\standingLeftType = TILE_EMPTY And p\standingRightType = TILE_EMPTY ) GoneAirborne()
;p\Airborne = p\standingLeftType = TILE_EMPTY And p\standingRightType = TILE_EMPTY
;p\Airborne = True
End Function
Function UpdateCamPos()
CamX = MaxFloat(0, p\MapX - CAM_W/2 + 5)
CamY = MaxFloat(0, p\MapY - CAM_H/2 - 50)
If CamX + CAM_W > MapWidthPx CamX = MapWidthPx - CAM_W
If CamY + CAM_H > MapHeightPx CamY = MapHeightPx - CAM_H
;DebugLog "CamX: "+CamX+"|CamY:"+CamY
;DebugLog "pMoveX:"+p\MoveX+"|pMapX:"+p\MapX
End Function
Function UpdatePlayerScrPos()
p\ScrX = p\MapX - CamX
p\ScrY = p\MapY - CamY
End Function
Function UpdatePlayerVel()
Local LeftDown = KeyDown(203)
Local RightDown = KeyDown(205)
Local JumpHit = KeyHit(200)
Local JumpDown = KeyDown(200)
If ( JumpHit And p\AbleToJump = True ) Then
p\AbleToJump = False
p\Jumping = True
p\Airborne = True
p\YSpeed = JumpSpeed
JumpKeyPressed = False
EndIf
JumpKeyPressed = JumpKeyPressed Or (p\Jumping And JumpDown = False)
; catch small jumps
If p\Jumping And JumpKeyPressed And p\YSpeed < SmallJumpSpeed Then
p\YSpeed = SmallJumpSpeed
EndIf
;;;; AIR-DRAG AND IN-AIR PHYSICS
If p\Airborne
;;;; MOVE CODE (in air)
If LeftDown Then
p\XSpeed = p\XSpeed - Air
ElseIf RightDown Then
p\XSpeed = p\XSpeed + Air
EndIf
; limit to max air XSpeed
If Abs(p\XSpeed) > AirMaxXSpeed Then
p\XSpeed = AirMaxXSpeed * Sgn(p\XSpeed)
EndIf
;Else ; is it right to apply air drag if no x keys are pressed or what?
If p\YSpeed < 0 And p\YSpeed > SmallJumpSpeed Then
; flooring basically enforces X to being > 0.125
p\XSpeed = p\XSpeed - (Floor(p\XSpeed / 0.125) / 256)
; dont apply air drag for small jumps
; NOTE: this should be done before applying gravity really
; have just moved gravity to after jump + move code
EndIf
;EndIf
Else
;;;; MOVE CODE (on floor)
; pressing left
If LeftDown > 0 Then
If p\XSpeed > 0 Then ; if going right then dec
p\XSpeed = p\XSpeed - Dec
ElseIf p\XSpeed > -MaxXSpeed Then ; if we are going left but less than max, inc by acc
p\XSpeed = p\XSpeed - Acc ; TODO: could this actually allow a speed greater than max speed?
; might move above check outside of if, so it xspeed is never faster than max
Else
; move this into if above if we want to be able to move quicker than max
; by an external force, eg, jump pad or something, because using this code,
; if we are pushed to great than max, and user presses that same direction,
; then line below will set the speed to max, which will be lower than the actual speed
; Or maybe MaxXSpeed can be set if we use a jump pad or something, then reset it sometime
p\XSpeed = -MaxXSpeed ; otherwise cap out at max
EndIf
; pressin right
ElseIf RightDown Then
If p\XSpeed < 0 Then ; if going left then dec
p\XSpeed = p\XSpeed + Dec
ElseIf p\XSpeed < MaxXSpeed Then ; if we are going right but less than max, inc by acc
p\XSpeed = p\XSpeed + Acc
Else
p\XSpeed = MaxXSpeed ; otherwise cap out at max
EndIf
; no X pressin, apply friction
Else
If p\XSpeed <> 0 Then
p\XSpeed = p\XSpeed - (MinFloat(Abs(p\XSpeed), Frc) * Sgn(p\XSpeed))
EndIf
EndIf
EndIf
End Function
.mapData
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,0,0,0
Data 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.tileTypeData
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,0,0,0
Data 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Data 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
| ||
| Hi, wanted to say if you need to change your collision paradigm, I describe an intuitive one here: http://blitzbasic.com/Community/posts.php?topic=99818#1174401 |
| ||
| Thanks for the reply Kryzon. I'm a bit busy atm so I've just had a quick read and it looks good thanks, I appreciate that. I have read quite a few different methods over the last week but just to get something working I started simple. Its just wierd I must be missing something in that I think I've adapted the guides' method correctly but I'm forced to make an extra step not in the guide. I'll take a better look at it later hopefully. All I want to do really is get something working, nothing special just something I can build on bit by bit. If anyone has suggestions on my code that would be useful too. |
| ||
| I think in that article he handles the player by the center, and you seem to handling it by the top-left corner. That changes the logic a bit, so you are probably compensating for this. EDIT: Another good handle position for platform games is at the feet, bottom-center. It just makes comparing with platforms easier. |
| ||
| Yeah that threw me to start with, until I realised, wondered why he was adding width and height on all the time. I can see why a bottom handle would be useful but I'm not sure my brain would take to making the switch, too long in the tooth. I'll print your article off and read it properly. Can't sit in front of a machine too long. |
| ||
| Hi. Make sure you print the last post as well, it has more clear code on the collision function: http://blitzbasic.com/Community/posts.php?topic=99818#1174643 I'm certain you can improve the method more for your game, since you're looking for an extendable solution. I imagine for a tile game you need something like a new type of collider such as "line" colliders, between two defined points. You'd use these lines to 'outline' clusters of solid tiles; So it's much faster than checking collisions against each tile - you only need to compare against the outline of the whole cluster, as tiles within the cluster will never get to touch the player, only the outer ones (that's why it's faster): ![]() This is actually getting me interested. If you need any help with that, we'll work on it. |
| ||
| Kryzon, that would be good. I've dropped you an email, maybe we could exchange ideas and come up with something good. |
| ||
| Sure thing. It'd be more contributing to post code here (for any interested others to see as well, especially ones that get here by googling keywords), but I'll check it out. EDIT: I gave some more thought on that article you were referencing about tile collisions. It's way faster to use with tile maps since it only checks the four corners of the moving object, instead of comparing with every single collideable tile in the map. It works, but then you also need to stick to the conditions the method implies. These conditions are: - No movable object should move more than the tile size in a single step (else the corner checking might miss any solid tiles the object goes through, if it lands too far). - No object should be bigger than two tiles in any direction, else a solid tile might slip through the middle (since the algorithm only checks the four corners). ![]() More on the speed limitation. If you can leave the "back sides" (sides that face opposite the direction of movement) in place, and only move the "front sides" (sides that face the direction of movement), you end up getting the whole region the object will travel. Then you need to check for tiles inside this region and proceed in a way I haven't thought of yet, give it a day... |

