2D Overhead scrolling maze

BlitzMax Forums/BlitzMax Beginners Area/2D Overhead scrolling maze

zamfir(Posted 2006) [#1]
I'm sure that someone has done this before, nevertheless: I am looking for a sample of a 2D overhead scroller, with a map that includes "walls" for collisions. The controls don't matter, I intend to use a car sort of control scheme. I'm thinking something similar to the old 2D GTA games (GTA1 and GTA2). Has anyone done/seen anything like that?


Scott Shaver(Posted 2006) [#2]
this may be a bit more complicated than you wanted but:

map editor
http://www.scottshaver2000.com/forum/viewtopic.php?t=140

tilemap module
http://www.scottshaver2000.com/forum/viewtopic.php?t=135

there are some samples in the mapeditor zip file that cover what you want


JazzieB(Posted 2006) [#3]
There's always my example that can be found on the Code section of my Blitz site (link in my sig). Not quite as complicated as the one above, but the walls are generated randomly in my example.

The map is simply stored in an array, as it pretty much would be any tilemap, so can be populated by any means - DefData, load from file, etc.


AD(Posted 2006) [#4]
I'm sure someone attempted to do a GTA clone in Blitz Basic, but I am not sure what ever happened to it.


zamfir(Posted 2006) [#5]
Jazzie, I looked at your site, I couldn't find the program to which you were referring. Mind telling me which title, or providing a link?


zamfir(Posted 2006) [#6]
Scott Shaver,

Loved what you are doing but unfortunately I was unable to compile and run any of your examples... I extracted each example into it's own directory, are they dependent upon files in other directories? Should I just unzip everything into one directory?


zamfir(Posted 2006) [#7]
I'm still taking responses to this question... This should be a sticky thing, like scrolling 101... I doubt anyone buying BlitzMax DOESN'T want scrolling and collisions.


tonyg(Posted 2006) [#8]
That's great Zamfir. I look forward to reading it when you've finished. What have you got so far?


Scott Shaver(Posted 2006) [#9]
You need to make sure you have my tilemap module to compile the examples


zamfir(Posted 2006) [#10]
Ah!


zamfir(Posted 2006) [#11]
On your forum, you have this: "The tilemaps module had to be updated due to a change in BlitzMax handling of bigendian machines."

... I'm not seeing the tilemap module.


Scott Shaver(Posted 2006) [#12]
the second link in my first post


zamfir(Posted 2006) [#13]
Scott,

fter doing the MinGW thing and rebuilding all modules, your stuff worked like a charm. Great stuff, and I'll probably use it.

Another question though... If anyone's seen on here the demo where it takes a very large PNG or some other bitmap and breaks it up into smaller pieces so any video card can handle it... Would it be possible to make a map using one large image for the obstacles and transparent alpha for the background and test collisions against that? That might be a way to break out of the grid format... Just a thought.

Thanks!


Scott Shaver(Posted 2006) [#14]
the system works by having all of the tiles in a single image, you could modify the code but it would be a pain.


Scott Shaver(Posted 2006) [#15]
note to tohers, if there is a reference to igl.mod in the demos remove it.


zamfir(Posted 2006) [#16]
Scott, is your collision all rectangular? Also, what about rotating the background around the player?


Scott Shaver(Posted 2006) [#17]
There is no rotation, the tilemap module was intended for platformer type games. As for the collision stuff that's really up to you, there isn't any built in collision handling as you can see in the examples, I implmented the wall collisions in the easiest manner possible, full tile collision. If you use some non-rectangular tiles (via transparency) you could implement the collisions in your own way.


JazzieB(Posted 2006) [#18]
Jazzie, I looked at your site, I couldn't find the program to which you were referring. Mind telling me which title, or providing a link?


You may have clicked on the wrong link in my sig, as there are two sites there. My SOS Software site where all my games are, and the Blitz site, which is my programming site (code examples, current projects, etc).

The page with all my code examples is here: http://www.blitz.sos-software.co.uk/code.htm

And the download for the tilemap example is here: http://www.blitz.sos-software.co.uk/downloads/TileDemoMax.zip

My example also demonstrates keeping the player central on-screen whenever possible. If the player is near the edge of the level then it's the player that moves and not the level.


Kev(Posted 2006) [#19]
@JazzieB, very nice tile map demo.


zamfir(Posted 2006) [#20]
Jazzie,

I concur, very well done!


zamfir(Posted 2006) [#21]
Jazzie,

I like your engine, I've tried using fredborg's viewport rotation function (found here: http://www.blitzbasic.com/Community/posts.php?topic=42799#478997

but it only works for a second when "A" is pressed. The code is below, I'm not sure how to put it in a code window yet:


Framework brl.glmax2d
Import brl.random
Import brl.pngloader
SetGraphicsDriver GLMax2DDriver()

' ----- CONSTANTS -----

Const scrWidth=640,scrHeight=480 ' screen resolution
Const scrDepth=32 ' bitdept, *** change as required ***
Const scrHertz=75 ' refresh rate (this is nice and smooth, but change if required).
Const tileWidth=32,tileHeight=32 ' dimension of tiles
Const tileMain=1,tileBack=2,tilePlayer=3 ' frame numbers for tiles

Const playerSpeed=4 ' pixels for player to move each update

Const mapWidth=64,mapHeight=64 ' size of map
Const marginX=(scrWidth-tileWidth)/2 ' margin between edge of screen and centre (horizontal)
Const marginY=(scrHeight-tileHeight)/2 ' margin between edge of screen and centre (vertical)

' ----- GLOBALS -----

SeedRnd MilliSecs()

Global ms#,fps#,msTot#,msCnt#,msAvg#

' ----- TYPE / METHOD DECLARATIONS -----

Type player
Field mapX,mapY ' map block co-ordinates (top left if more than one square occupied)
Field x,y ' pixel co-ordinates

Function Create:player() ' create a player with the starting x and y map co-ordinates
Local sx,sy
p:player=New player ' create player
Repeat
sx=Rand(1,mapWidth-1) ' choose random x position
sy=Rand(1,mapHeight-1) ' choose random y position
Until lev.Read(sx,sy)=0
p.mapX=sx ' set starting x (map co-ordinate)
p.mapY=sy ' set starting y (map co-ordinate)
p.x=sx*tileWidth ' set x position (pixels)
p.y=sy*tileHeight ' set y position (pixels)
Return p
End Function

Method Update()
Local dx=(KeyDown(key_right)-KeyDown(key_left))*playerSpeed ' get player input for the x direction
Local dy=(KeyDown(key_down)-KeyDown(key_up))*playerSpeed ' get player input for the y direction

If dy Then ' check vertical movement
If MapCollision(x+dx,y+dy) Then ' check if we can move
If dx Then ' can't move, but might be trying to move diagonally
If MapCollision(x,y+dy) Then ' can we move straight up or down?
dy=0 ' still can't move, so don't allow
EndIf
Else
dy=0 ' not trying to move at angle, so don't allow
EndIf
EndIf
EndIf

If dx Then ' do the same for horizontal
If MapCollision(x+dx,y+dy) Then
If dy Then
If MapCollision(x+dx,y) Then
dx=0
EndIf
Else
dx=0
EndIf
EndIf
EndIf

x:+dx ' update co-ordinates
y:+dy
mapX=Floor(x/tileWidth) ' update map position
mapY=Floor(y/tileHeight)
End Method

Method Draw(sx,sy)
DrawImage imgTiles,sx,sy,tilePlayer ' draw plater with supplied screen co-ordinates
DrawText "World position: "+x+","+y,0,0
DrawText "Map position: "+mapX+","+mapY,0,16
End Method
End Type

Type level
Field map[mapWidth,mapHeight] ' define our map array

Method Read(x,y) ' method to read tile at map position
Return map[x,y]
End Method

Method Write(x,y,z) ' method to write to a map position
map[x,y]=z
End Method

Method Draw(ox,oy) ' draw map at specified offset (calculated in RenderGame)
SetBlend solidblend ' set blending to solid to erase screen
TileImage imgTiles,ox/2,oy/2,tileBack ' draw our parallax background image
SetBlend alphablend ' set blending to alpha (not needed, as our tiles are square)
' now go through map and display each tile, but ignore any blank tiles for additional speed
For Local y=0 Until mapHeight
For Local x=0 Until mapWidth
If Read(x,y) Then DrawImage imgTiles,x*tileWidth+ox,y*tileHeight+oy,Read(x,y)
Next
Next
End Method
End Type

Rem
NOTE: The Draw() method above could be optimised further to only loop through those tiles that appear
on-screen, but is not really needed at the moment as only those images that appear on-screen will be
rendered anyway. Larger and more complicated ganes may benefit if every bit of available processing power
is needed. The rendering process of this demo runs at under 1ms on a P4 2.4GHZ with Radeon 9800 Pro.
EndRem

' ----- SET UP GRAPHICS -----

Graphics scrWidth,scrHeight,scrDepth,scrHertz ' change graphics mode
HideMouse ' hide the mouse pointer

Global imgTiles=LoadAnimImage("tiles.png",tileWidth,tileHeight,0,4) ' load the tiles

' ----- INITIALISE MAP -----

Global lev:level=New level ' create a level

For Local x=0 Until mapWidth ' draw the top and bottom borders
lev.Write(x,0,tileMain)
lev.Write(x,mapHeight-1,tileMain)
Next

For Local y=0 Until mapHeight ' draw the left and right borders
lev.Write(0,y,tileMain)
lev.Write(mapWidth-1,y,tileMain)
Next

For Local i=1 To 50 ' draws50 random lines of the wall tile
dir=Rand(0,1)
x=Rand(1,mapWidth-2)
y=Rand(1,mapHeight-2)
l=Rand(2,30)
For Local j=0 Until l
If dir Then
lev.Write(x,y,tileMain)
x:+1
If x=mapWidth Then Exit
Else
lev.Write(x,y,tileMain)
y:+1
If y=mapHeight Then Exit
EndIf
Next
Next

' ----- INITIALISE PLAYER -----

Global face:player=player.Create() ' create a player

' ----- MAIN LOOP -----

While Not KeyHit(key_escape)
face.Update ' get player input and update position
RenderGame ' render the screen
Wend

' ----- RENDER FUNCTION -----

Function RenderGame()

If KeyHit(KEY_A)
Sangle:+1
EndIf

Local time=MilliSecs() ' used to time how long this takes (before flipping, which waits for vsync)
Local t$ ' used to format render time

Local lx,ly,px,py ' level offsets and players on-screen position

' check horizontal placement
If face.x>=marginX And face.x<=((mapWidth-1)*tileWidth-marginX) Then ' if player within central area
px=marginX ' player stays in centre
lx=px-face.x ' level offset is player's x minus the margin between the screen edge and player
ElseIf face.x<marginX ' check if player near left edge
px=face.x ' yes, so on-screen position is same as x
lx=0 ' and level offset is it's left edge
Else ' otherwise player is near right edge of level
px=marginX+marginX-((mapWidth-1)*tileWidth-face.x) ' calculate player's position based on level width
lx=-mapWidth*tileWidth+scrWidth ' adjust level's position so that right edge is same as screen edge
EndIf

' check for vertical placement - same as above, but on the y axis
If face.y>=marginY And face.y<=((mapHeight-1)*tileHeight-marginY) Then
py=marginY
ly=py-face.y
ElseIf face.y<marginY
py=face.y
ly=0
Else
py=marginY+marginY-((mapHeight-1)*tileHeight-face.y)
ly=-mapHeight*tileHeight+scrHeight
EndIf

lev.Draw(lx,ly) ' draw the level at the calculated offset
face.Draw(px,py) ' draw the player at the calculated on-screen position

ms=MilliSecs()-time ' work out how long it took to draw
msTot:+ms ' add to total render time for previous frames
msCnt:+1 ' increase count (for average)
msAvg=msTot/msCnt ' calculate average render time
If msAvg Then fps=1000/msAvg ' make sure we won't get a /0 error and calculate *estimated* FPS
t=msAvg ' convert to string for displaying on screen
DrawText "Avg render time: "+t[..5]+"ms ("+Int(fps)+" FPS)",0,32 ' write to screen

SetViewRotation (Sangle)

Flip ' flip the buffers so we can see everything and clear any rubbish from memory.
End Function

' ----- CHECKS IF ANY SQUARES AT PIXEL CO-ORDINATES ARE OCCUPIED -----

Function MapCollision(x,y)
Local mx,my
mx=Floor(x/tileWidth) ' get map position
my=Floor(y/tileHeight)
If lev.Read(mx,my) Then Return True ' check top left
If (x Mod tileWidth) Then If lev.Read(mx+1,my) Then Return True ' check top right
If (y Mod tileHeight) Then
If lev.Read(mx,my+1) Then ' check bottom left
Return True
ElseIf (x Mod tileWidth) Then
If lev.Read(mx+1,my+1) Then Return True ' check bottom right#
EndIf
EndIf
Return False
End Function

Rem
bbdoc: Set the rotation of the viewport
about: SetViewRotation allows you to rotate the entire viewport, so that all
drawing is rotated according to the @angle parameter.
endrem
Function SetViewRotation(angle:Float)

Global o_angle:Float

glMatrixMode GL_PROJECTION
glRotatef angle-o_angle,0,0,1
o_angle=angle

glMatrixMode GL_MODELVIEW
glLoadIdentity

End Function

Rem
bbdoc: Set the offset of the viewport
about: SetViewOffset allows you to offset the entire viewport, so that all
drawing is offset according to the @offsetx and @offsety parameters.
endrem
Function SetViewOffset(offsetx:Float,offsety:Float)

Global o_offsetx:Float
Global o_offsety:Float

glMatrixMode GL_PROJECTION
glTranslatef offsetx-o_offsetx,offsety-o_offsety,0
o_offsetx = offsetx
o_offsety = offsety

glMatrixMode GL_MODELVIEW
glLoadIdentity

End Function

Rem
bbdoc: Set the zoom factor of the viewport
about: SetViewZoom allows you to zoom all drawn elements at once, instead
of scaling each individual element. Use the @zoom parameter to define the
level of zoom, a value of 0 is the default zoom, >0 zooms in, <0 zooms out.
endrem
Function SetViewZoom(zoom:Float)

Global o_zoom:Float

glMatrixMode GL_PROJECTION
glScalef 1.0+(zoom-o_zoom),1.0+(zoom-o_zoom),1.0+(zoom-o_zoom)
o_zoom = zoom

glMatrixMode GL_MODELVIEW
glLoadIdentity

End Function


BlackSp1der(Posted 2006) [#22]

but it only works for a second when "A" is pressed. The code is below, I'm not sure how to put it in a code window yet:


it's because your "sangle" variable is not defined as global. and allways you call rendergame the sangle variable is set to zero.

put sangle as global before main loop
' ----- MAIN LOOP -----
Global Sangle:Float


code box:
[ codebox]
[ /codebox]
(whitout spaces)

and... you don't need projection matrix. you need to work with Angle, Distance, COS and SIN, is easy. Try to make your own engine.


zamfir(Posted 2006) [#23]
thank you, that worked.


BlackSp1der(Posted 2006) [#24]
maybe you can find a better way to do it.
it's only an example.
use up,down,left,right to move.
'A' and 'S' to rotate




zamfir(Posted 2006) [#25]
Right on, that's cool. If I can get that to work with a tile map I'm golden!


zamfir(Posted 2006) [#26]
Did you see this function? This is exactly what I need it to do, just not sure where or how to implement it properly:

Rem
bbdoc: Set the rotation of the viewport
about: SetViewRotation allows you to rotate the entire viewport, so that all
drawing is rotated according to the @angle parameter.
endrem
Function SetViewRotation(angle:Float)

Global o_angle:Float

glMatrixMode GL_PROJECTION
glRotatef angle-o_angle,0,0,1
o_angle=angle

glMatrixMode GL_MODELVIEW
glLoadIdentity

End Function


Foolish(Posted 2008) [#27]
Very helpful. Thanks guys.