Here is another approach.
It works like this. We load our level into an array where each position of the array keeps track of the sprite that is drawn there (a tile).
We then do collision checking by rounding off the players position to the nearest tile in the level array. We assume the player's sprite is smaller than the tile size and also that the movement rate is also smaller than a tile size. We then check for collision with 1 of four possible tile positions. We do this by looking at the level array to see what sprite is there. If it is one that has collisions we check for overlap of the sprite images.
Checking for things in this way minimizes the the number of checks we are performing to just four. It is possible to make further improvements but the water starts to get a little muddy (i.e. less clear) the further the method is refined. At this stage it is relatively simple.
If a collision occurs we move the player back to the former position. I've also added an additional level for testing as well as mad the sprite #vwall act as an exit and coded some things specifically to handle that (I partly muddied the water in the process, oops). The additions naturally would be removed to tailor things to your specific game play.
Since you haven't included any of your sprites I created some simple ones to enable a demonstration.
Code: Select all
UseJPEGImageDecoder()
; Structure mapdata_block
; type.s
; x.i
; y.i
; sprite.i
; EndStructure
Enumeration ;windows
#mainwin
;#mazewin
EndEnumeration
Enumeration ;sprites
#floor0 = 1
#fwall
#hwall
#vwall
#thomas
EndEnumeration
#True = 1 ;define basic constants for Macintosh (if they aren't defined already)
#False = 0
#blockSize = 32 ;assumes all tiles are the same size and that they are square
Global thomasx.i,thomasy.i
Global movestep.i = 5
Procedure handleError(value, text.s)
If value = #False
MessageRequester("Error", "Sprite system can't be initialized")
End
EndIf
EndProcedure
;This may seem a little complicated but it simply takes the pointer to the level data in the data section,
;reads in each line and converting the blocks into sprite#'s and stores them in an array that corresponds to
;tile positions on the screen. This array of the levelData sprites is used to render the screen and check for
;collisions with the player.
Procedure setupLevel(*levelData, Array levelData(2))
Protected rowCount, columnCount, x, y, *levelDataPtr = *levelData, mapline$, blockcode$
While PeekS(*levelData) <> "end"
rowCount + 1
*levelData + (MemoryStringLength(*levelData) + 1) * SizeOf(Character)
Wend
If rowCount > 1
columnCount = MemoryStringLength(*levelDataPtr) / 2 ;each block uses 2 characters
rowCount - 1: columnCount - 1
Dim levelData(columnCount, rowCount)
*levelData = *levelDataPtr
For y = 0 To rowCount
mapline$ = PeekS(*levelData)
For x = 0 To columnCount
blockcode$ = Mid(mapline$, x * 2 + 1, 2)
Select blockcode$
Case "01"
levelData(x, y) = #fwall
Case "02"
levelData(x, y) = #hwall
Case "03"
levelData(x, y) = #vwall
Case "00"
levelData(x, y) = #floor0
EndSelect
Next
*levelData + ((columnCount + 1) * 2 + 1) * SizeOf(Character) ;add memory length of line onto pointer
Next
EndIf
EndProcedure
Procedure drawscreen(Array levelData(2))
Protected i, j, x, y, columnCount = ArraySize(levelData(), 1), rowCount = ArraySize(levelData(), 2)
ClearScreen(RGB(255,255,255))
y = 0
For j = 0 To rowCount
x = 0
For i = 0 To columnCount
DisplaySprite(levelData(i, j), x, y)
x + #blockSize
Next
y + #blockSize
Next
DisplaySprite(#thomas, thomasx, thomasy)
FlipBuffers()
EndProcedure
;If the return value <> 0 then there was a collision with the given spriteID.
Procedure collisionCheck(spriteID, mapX, mapY, Array levelData(2))
;Collision checking assumes previous position was a legal position and that movestep < #blockSize.
;It compares the tile position that overlaps the position (thomasx, thomasy) with the levelData()
;to see what sprite occupies that space.
Protected pow, columnCount = ArraySize(levelData(), 1), rowCount = ArraySize(levelData(), 2)
If mapX >= 0 And mapX <= columnCount And mapY >= 0 And mapY <= rowCount
;check four positions for collisions
If levelData(mapX, mapY) = spriteID
;only map is needed for a collision in this position
pow = %100
EndIf
If pow = 0 And mapX + 1 <= columnCount And levelData(mapX + 1, mapY) = spriteID
;check overlap with tile to the right
pow = Bool(SpriteCollision(#thomas, thomasx, thomasy, spriteID, (mapX + 1) * #blockSize, mapY * #blockSize) <> 0) * %110
EndIf
If pow = 0 And mapX + 1 <= columnCount And mapY + 1 <= rowCount And levelData(mapX + 1, mapY + 1) = spriteID
;check overlap with tile to the lower-right
pow = Bool(SpriteCollision(#thomas, thomasx, thomasy, spriteID, (mapX + 1) * #blockSize, (mapY + 1) * #blockSize) <> 0) * %111
EndIf
If pow = 0 And mapY + 1 <= rowCount And levelData(mapX, mapY + 1) = spriteID
;check overlap with tile below
pow = Bool(SpriteCollision(#thomas, thomasx, thomasy, spriteID, mapX * #blockSize, (mapY + 1) * #blockSize) <> 0) * %101
EndIf
Else
;player off map, further check required to determine direction
pow = %1000
EndIf
ProcedureReturn pow ;return value encodes type of collision and where
EndProcedure
Procedure movementthread(Array levelData(2), blank.i = 0)
Protected oldposx = thomasx, oldposy = thomasy, collision, redraw
Protected columnCount = ArraySize(levelData(), 1), rowCount = ArraySize(levelData(), 2)
ExamineKeyboard()
If KeyboardPushed(#PB_Key_Down)
thomasy + movestep
kp = 1
EndIf
If KeyboardPushed(#PB_Key_Up)
thomasy - movestep
kp = 1
EndIf
If KeyboardPushed(#PB_Key_Right)
thomasx + movestep
kp = 1
EndIf
If KeyboardPushed(#PB_Key_Left)
thomasx - movestep
kp = 1
EndIf
If kp > 0
;Collision checking assumes (oldposx, oldposy) is a legal position and that movestep < #blockSize.
;It compares the tile position that overlaps the position (thomasx, thomasy) with the levelData()
;to see what sprite occupies that space.
redraw = #True
x = thomasx / #blockSize: y = thomasy / #blockSize
If collisionCheck(#fwall, x, y, levelData())
thomasx = oldposx
thomasy = oldposy
redraw = #False
Else
;treat collision with #vwall as an exit
collision = collisionCheck(#vwall, x, y, levelData())
If collision
x + Bool(collision & %10 <> 0): y + Bool(collision & %01 <> 0) ;adjust position for the four possible tiles
thomasx = x * #blockSize + #blockSize / 4;move player inside the tile where the exit 'was'
thomasy = y * #blockSize + #blockSize / 4
setupLevel(?level2, levelData()) ;level2 uses #vwall that is placed in a random position as an exit
Select Random(3, 1)
Case 1: levelData(columnCount - x, rowCount - y) = #vwall
Case 2: levelData(columnCount - x, y) = #vwall
Case 3: levelData(x, rowCount - y) = #vwall
EndSelect
EndIf
EndIf
If redraw
drawscreen(levelData())
EndIf
EndIf
EndProcedure
handleError(InitSprite(), "Sprite system can't be initialized")
handleError(InitKeyboard(), "Sprite system can't be initialized")
;
; Now, open a 800*600 - 32 bits screen
;
handleError(OpenWindow(#mainwin,1,1,800,600, "Casper Compile",
#PB_Window_TitleBar | #PB_Window_Invisible | #PB_Window_ScreenCentered |
#PB_Window_MinimizeGadget | #PB_Window_SystemMenu),
"Unable to open main window")
handleError(OpenWindowedScreen(WindowID(#mainwin),0,0,800, 600), "Can't open a 800*600 - 32 bit screen !")
; Load our 16 bit sprite (which is a 24 bit picture in fact, as BMP doesn't support 16 bit format)
;handleError(LoadSprite(#fwall,"sprites\fwall.jpg"), "Couldn't load sprite " + "sprites\fwall.jpg")
;handleError(LoadSprite(#hwall,"sprites\hwall.jpg"), "Couldn't load sprite " + "sprites\hwall.jpg")
;handleError(LoadSprite(#vwall,"sprites\vwall.jpg"), "Couldn't load sprite " + "sprites\vwall.jpg")
;handleError(LoadSprite(#floor0,"sprites\floortrip.jpg"), "Couldn't load sprite " + "sprites\floortrip.jpg")
;handleError(LoadSprite(#thomas,"sprites\thomas.jpg"), "Couldn't load sprite " + "sprites\thomas.jpg")
;create a few sprites for testing
CreateSprite(#fwall, 32, 32)
StartDrawing(SpriteOutput(#fwall))
Box(0, 0, #blockSize, #blockSize, RGB(255, 255, 255))
StopDrawing()
CreateSprite(#vwall, 32, 32)
StartDrawing(SpriteOutput(#vwall))
Box(0, 0, #blockSize, #blockSize, RGB(255, 0, 255))
StopDrawing()
CreateSprite(#floor0, 32, 32) ;use default black sprite
CreateSprite(#thomas, 11, 11)
StartDrawing(SpriteOutput(#thomas))
Circle(OutputWidth() / 2, OutputHeight() / 2, OutputWidth() / 2, RGB(0, 255, 0))
StopDrawing()
Dim levelData(0, 0) ;this will be redimensioned for each level
setupLevel(?level1, levelData())
thomasx = 133
thomasy = 133
drawscreen(levelData())
HideWindow(#mainwin,0)
Define quit = #False
Repeat ; Start of the event loop
WindowID = EventWindow() ; The Window where the event is generated, can be used in the gadget procedures
Event = WaitWindowEvent(2) ; This line waits until an event is received from Windows
GadgetID = EventGadget() ; Is it a gadget event?
EventType = EventType() ; The event type
Repeat
event = WindowEvent()
If event = #PB_Event_CloseWindow
quit = #True
EndIf
; If Event = #PB_Event_Gadget
;
; Select GadgetID
;
;
;
;
; EndSelect
;
; EndIf
Until event = 0 Or quit = #True
movementthread(levelData())
Until Event = #PB_Event_CloseWindow
DataSection
level1:
Data.s "01010101010101010101010101010101010101010101010101"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000030000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01000000000000000000000000000000000000000000000001"
Data.s "01010101010101010101010101010101010101010101010101"
Data.s "end"
level2:
Data.s "01010101010101010101010101010101010101010101010101"
Data.s "01000000000100000001010000000001000000000000000001"
Data.s "01010001000101010000000001010001000101010101000101"
Data.s "01000001000100010101010000010101000100000001000001"
Data.s "01000101010100000000010100010000000100010101010001"
Data.s "01000000000000010100000100010100010100000000000001"
Data.s "01010001010101010101000100000000000101010101010001"
Data.s "01000001000000000100000100010001000000010000000001"
Data.s "01000101000101000000010101010001010101010101010101"
Data.s "01000101000000010101000000010001000001000000010001"
Data.s "01000001010100000000000100000001010001000101010001"
Data.s "01010101000101010101010101010000000001000000000001"
Data.s "01000001000000010000000000010001010001010001010101"
Data.s "01000101010100010001010100010101000000010000000001"
Data.s "01000000000100010001000100010001000101010101010001"
Data.s "01000101000100010001000100010001000000010000000001"
Data.s "01000001010100010001000100010001000100010101010001"
Data.s "01010000000000000000000100000000000100000000000001"
Data.s "01010101010101010101010101010101010101010101010101"
Data.s "end"
EndDataSection
Let me know if this was helpful or if there is anything about the code I can clear up.