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... ![]() |