Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 12:40 06 May 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : CMM2 tile-based game code example

Author Message
capsikin
Guru

Joined: 30/06/2020
Location: Australia
Posts: 341
Posted: 01:55am 22 Sep 2020
Copy link to clipboard 
Print this post

I've made some example code for a simple tile-based game. It has lots of comments and is intended to be easily readable, and extendable into a good game along the lines of Pengo or Mr Wiz (a Mr Do clone with more restrictive tile-based movement), or maybe something like Mr Do or Dig Dug, with some adjustments to the movement routines.

It could use more support for animation and making different sounds than it has so far though. Animations could each have an array of sprite numbers. Sounds could each have arrays of frequencies and volumes. Also there should be a way of having multiple active objects of the same type, probably involving "SPRITE COPY". For example in Pengo there can be 4 (I think) Sno-Bees active at the same time.

You can use, modify and and copy this with no licence restrictions from me, so people can use it in their own games.

Zip file with game code and 2 sprites:
tunnelchase-1.zip

Game code follows

''''''''''''''''''''''''''''''

option explicit


''''''''''''''''''''''''''''''''
' Video mode and frame counter '
''''''''''''''''''''''''''''''''

' Increase frames at every screen blanking interval, so the main program knows when to redraw
' the sprites. Main program then resets frames counter to zero.

dim frames
frames=0

sub countframes
 frames=frames+1
end sub

mode 2,12,0,countframes


''''''''''''
' Tile map '
''''''''''''

dim xtiles
dim ytiles

xtiles=15
ytiles=15

dim tiles(xtiles,ytiles)

''''''''''''''
' Tile types '
''''''''''''''
dim tile.empty, tile.earth, tile.player, tile.jewel, tile.monster, tile.wall, tile.playersmile
dim tile.max

tile.empty=1
tile.earth=2
tile.player=3
tile.jewel=4
tile.monster=5
tile.wall=6
tile.playersmile=7

tile.max=7

''''''''''''''''''''''''''''''''''''''
' Set a colour for each tile, in     '
' case we don't have a custom sprite '
''''''''''''''''''''''''''''''''''''''

dim tile.colour(tile.max)

tile.colour(tile.empty)=RGB(black)
tile.colour(tile.earth)=RGB(blue)
tile.colour(tile.player)=RGB(255,255,0) ' magenta
tile.colour(tile.jewel)=RGB(128,0,192) ' violet
tile.colour(tile.monster)=RGB(yellow)
tile.colour(tile.wall)=RGB(0,192,192) ' cyan, partial brightness.

''''''''''''''''''''''''''''''
' Initialise sprite graphics '
''''''''''''''''''''''''''''''
dim spritesize

dim tile.sprite(tile.max)

spritesize=16

' Load custom sprites
tile.sprite(tile.player)=1
sprite load "fuzz1.spr",tile.sprite(tile.player)

tile.sprite(tile.playersmile)=2
sprite load "fuzz2.spr",tile.sprite(tile.playersmile)

' Set generic coloured sprites for other objects
dim n,spritenumber
for n=1 to tile.max
 if tile.sprite(n)=0 then
   spritenumber=10+n
   tile.sprite(n)=spritenumber
   rbox 0,0,spritesize,spritesize,4,tile.colour(n),tile.colour(n)
   sprite read spritenumber,0,0,spritesize,spritesize
 end if
next n
cls

'''''''''''''''''
' Tile routines '
'''''''''''''''''

sub tile.setanddraw t,tx,ty
 tiles(tx,ty)=t
 sprite write tile.sprite(t),tx*spritesize,ty*spritesize
end sub

sub tile.draw t,tx,ty
 sprite write tile.sprite(t),tx*spritesize,ty*spritesize
end sub

sub tile.undraw t,tx,ty
 box tx*spritesize,ty*spritesize,spritesize,spritesize,,rgb(black),rgb(black)
end sub

''''''''''''''''''''''''''
' Mobs - movable objects '
''''''''''''''''''''''''''

dim mob.player,mob.jewel,mob.monster
dim mob.max

mob.player=1
mob.jewel=2
mob.monster=3
mob.max=3

' Pixel-precision locations of mobs for drawing sprites
dim mob.px(mob.max)
dim mob.py(mob.max)

' Pixel-precision velocities for smooth movement. In pixels per update.
dim mob.vpx(mob.max)
dim mob.vpy(mob.max)

' Number of steps the mob has left to move
dim mob.movesteps(mob.max)

' Number of steps per move for future moves
dim mob.stepspermove(mob.max)

mob.stepspermove(mob.player)=8
' Jewel takes less time to move
mob.stepspermove(mob.jewel)=4
' Monster takes more time to move
mob.stepspermove(mob.monster)=16

' Tile-precision locations of mobs for game logic
dim mob.tx(mob.max)
dim mob.ty(mob.max)

' Whether the mob is stored on the "tiles" map and clears spaces it moves on
dim mob.onmap(mob.max)

' Which tile is stored on the map for the mob (if it is stored)
dim mob.tile(mob.max)

' Mobs are drawn on page 1, background tiles are drawn on page 0
dim mob.sprite(mob.max)

sub mob.initanddraw m,t,tx,ty,onmap
 mob.tx(m)=tx
 mob.ty(m)=ty
 mob.px(m)=tx*spritesize
 mob.py(m)=ty*spritesize
 mob.tile(m)=t
 mob.sprite(m)=tile.sprite(t)
 mob.movesteps(m)=0
 mob.onmap(m)=onmap

 page write 1
 sprite show mob.sprite(m),mob.px(m),mob.py(m),0

 if onmap then
   ' Erase any background tile underneath
   page write 0
   tile.undraw tile.empty,tx,ty
 end if

 tiles(tx,ty)=t
end sub

sub mob.startmove m,dtx,dty,steps
 ' Set px and py so it doesn't start from part way through a previous move
 mob.px(m)=mob.tx(m)*spritesize
 mob.py(m)=mob.ty(m)*spritesize

 local newtx,newty

 newtx=mob.tx(m)+dtx
 newty=mob.ty(m)+dty

 mob.vpx(m)=dtx*spritesize\steps
 mob.vpy(m)=dty*spritesize\steps
 mob.movesteps(m)=steps

 mob.checkcollisions m,newtx,newty

 if mob.onmap(m) then
   tiles(mob.tx(m),mob.ty(m))=tile.empty
   tiles(newtx,newty)=mob.tile(m)
 end if

 mob.tx(m)=newtx
 mob.ty(m)=newty
end sub

sub mob.move m
 mob.px(m)=mob.px(m)+mob.vpx(m)
 mob.py(m)=mob.py(m)+mob.vpy(m)
 mob.movesteps(m)=mob.movesteps(m)-1

 if mob.onmap(m) then
   ' At the end of moving, erase any background tile underneath
   page write 0
   if mob.movesteps(m)=0 then
     tile.undraw tile.empty, mob.tx(m),mob.ty(m)
   end if
 end if

 page write 1
 sprite next mob.sprite(m),mob.px(m),mob.py(m)

end sub

sub mob.changesprite m,spr
 if mob.sprite(m) <> spr then
   sprite swap mob.sprite(m),spr
   mob.sprite(m)=spr
 end if
end sub

' Check for collisions as mob m moves to location tx,ty
sub mob.checkcollisions m,tx,ty
 local t

 ' Add any collisions we care about to the checks below

 ' First check for collisions on "tiles" map
 t=tiles(tx,ty)
 if m=mob.monster then
   if t=tile.player then
     collide.monster.player
   end if
 end if

 ' Next check for collisions with things that aren't stored on the map
 if m=mob.player then
   if mob.at(mob.jewel,tx,ty) then
     collide.player.jewel
   end if
 end if

end sub

'''''''''''''''''''''''''''''''
' Game logic helper functions '
'''''''''''''''''''''''''''''''

sub mob.trymove m,dx,dy
 local t, moveok
 t=tiles(mob.tx(m)+dx,mob.ty(m)+dy)
 moveok=(t <> tile.wall)
 if moveok then
   mob.startmove m,dx,dy,mob.stepspermove(m)
 end if
end sub

sub mob.trymovelazy m,dx,dy
 local t, moveok
 t=tiles(mob.tx(m)+dx,mob.ty(m)+dy)
 if t=tile.wall then
   moveok=0
 else if t=tile.earth then
   if rnd(1) < 0.01 then
     moveok=1
   else
     moveok=0
   end if
 else
   moveok=1
 end if
 if moveok then
   mob.startmove m,dx,dy,mob.stepspermove(m)
 end if
end sub

' Check if a specific mob is at specific tile coordinates
function mob.at(m,tx,ty)
 mob.at = (tx = mob.tx(m)) and (ty=mob.ty(m))
end function

'''''''''''''''''''''''''''
' Game logic for each mob '
'''''''''''''''''''''''''''

' Flags for when the player catches the jewel or is caught by the
' monster could be used in future.
dim monstercaughtplayer,playercaughtjewel

monstercaughtplayer=0
playercaughtjewel=0

dim asc.left, asc.right, asc.up, asc.down

asc.left=130
asc.right=131
asc.up=128
asc.down=129

sub player.decide
 local m = mob.player
 if keydown(1)=asc.up then
   mob.trymove m,0,-1
 else if keydown(1)=asc.down then
   mob.trymove m,0,1
 else if keydown(1)=asc.left then
   mob.trymove m,-1,0
 else if keydown(1)=asc.right then
   mob.trymove m,1,0
 end if
end sub

sub jewel.decide
 local m = mob.jewel
 local r = rnd(1)
 if r < 0.2 then
   mob.trymovelazy m,0,-1
 else if r < 0.4 then
   mob.trymovelazy m,0,1
 else if r < 0.6 then
   mob.trymovelazy m,-1,0
 else if r < 0.8 then
   mob.trymovelazy m,1,0
 end if
end sub

sub monster.decide
 local m = mob.monster
 local r = rnd(1)
 if r < 0.5 then
   if mob.tx(mob.player) < mob.tx(mob.monster) then
     mob.trymovelazy m,-1,0
   else
     mob.trymovelazy m,1,0
   end if
 else
   if mob.ty(mob.player) < mob.ty(mob.monster) then
     mob.trymovelazy m,0,-1
   else
     mob.trymovelazy m,0,1
   end if
 end if
end sub

sub mob.decide m
 if m=mob.player then
   player.decide
 else if m=mob.player then
   player.decide
 else if m=mob.jewel then
   jewel.decide
 else if m=mob.monster then
   monster.decide
 end if
end sub

''''''''''''''''''''''''
' Collision game logic '
''''''''''''''''''''''''

sub collide.player.jewel
 playercaughtjewel=1
 sounds.startgoodsound
 mob.changesprite mob.player, tile.sprite(tile.playersmile)
end sub

sub collide.monster.player
 monstercaughtplayer=1
 sounds.startbadsound
end sub

''''''''''''''''''''''
' Main sound effects '
''''''''''''''''''''''

dim goodsound,badsound,walksound

goodsound=0
badsound=0

sub sounds.startgoodsound
 goodsound=25
end sub

sub sounds.startbadsound
 badsound=5
end sub

sub sounds.startwalksound
 walksound=1
end sub

sub sounds.next

 ' Play sound if player caught jewel
 if goodsound then
   ' Volume reduces as goodsound reduces
   play sound 2,B,Q,2000,goodsound
   goodsound=goodsound-1
 else
   'Silence sound 2
   play sound 2,B,O,100
 end if

 ' Play sound if monster caught player
 if badsound then
   ' Frequency reduces as goodsound reduces
   play sound 3,B,Q,300*badsound
   badsound=badsound-1
 else
   ' Silence sound 3
   play sound 3,B,O,100
 end if

 ' Player walking sound
 if walksound then
   play sound 1,B,Q,1500,10
   walksound=0
 else
   ' Silence sound 1
   play sound 1,B,O,100
 end if

end sub  


''''''''''''''''''
' Initialise map '
''''''''''''''''''

dim x,y

' Fill map with earth tiles
for x=1 to xtiles
 for y=1 to ytiles
   tile.setanddraw tile.earth,x,y
 next y
next x

' Put walls at top and bottom
for x=1 to xtiles
 tile.setanddraw tile.wall,x,1
 tile.setanddraw tile.wall,x,ytiles
next x

' Put walls at left and right edges
for y=1 to ytiles
 tile.setanddraw tile.wall,1,y
 tile.setanddraw tile.wall,xtiles,y
next y

'''''''''''''''''''
' Initialise mobs '
'''''''''''''''''''

mob.initanddraw mob.player,tile.player,3,3,1
mob.initanddraw mob.monster,tile.monster,10,10,1

' Jewel has "onmap" set to 0, to it doesn't clear a trail as it moves
mob.initanddraw mob.jewel,tile.jewel,13,13,0



''''''''''''''''''
' Main game loop '
''''''''''''''''''

' mob index
dim mi

do

 ' Run the "decide" sub for all mobs that aren't partway through a move.
 for mi=1 to mob.max
   if not mob.movesteps(mi) then
     mob.decide mi
   end if
 next mi

 ' Prepare to play sound at the start of player move
 if mob.movesteps(mob.player)=mob.stepspermove(mob.player) then sounds.startwalksound

 ' Play any sounds that have been prepared
 sounds.next

 ' Process any moving mobs
 for mi=1 to mob.max
   if mob.movesteps(mi) then
     mob.move mi
   end if
 next mi

 ' Wait before next game loop
 do
 loop until frames>=2
 frames=0

 ' Draw any moved sprites
 page write 1
 sprite move

loop until 0


edited to add: weird, the first two spaces of indentation only seem to show as one in the forum post. It should be correct in the attachment.
Edited 2020-09-22 12:00 by capsikin
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024