Skip to content

Instantly share code, notes, and snippets.

@Westenburg
Created June 9, 2013 13:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Westenburg/5743643 to your computer and use it in GitHub Desktop.
Save Westenburg/5743643 to your computer and use it in GitHub Desktop.
Basic version of Foggy Bummer - a side scrolling game written in Codea
-- Foggy Bummer
-- By West
--Game overview: A sideways scroller where the player controls a bumblebee. Touch screen to provide upward thrust to the bee. Collect as many rings as you can in 24 hours or until you complete the full course. Don't hit the top or bottom of the screen.
--Feel free to use this code as you wish to help you learn and understand codea. Most of the images are either hand drawn or sections extracted from photos - feel free to use, mangle and adapt the sprite sheet too. Just please don't take the code and images wholesale and upload them to the apple store!
--Often a good way to learn to program is to take existing code, tweak it and build on it. Hopefully this will give someone a foot up towards making their own sideways scroller. This is the most basic version, but I've also added the following if anyone is interested
--Added bonuses
--Added particle effects
--Added different weather
--Basic Challenges
--Make spiders move up and down the screen and kill the player if they hit them
--Swap the moon image for an image of a planet sized space station (That's no moon...)
--Add a bonus which reverses the direction of gravity for a short period of time
--Add an explosion of flower particles when bonuses are picked up
--Change the control of the bee to use a tilt input from the ipad instead of touches
--More advanced challenges
--Make the bee fire a globule of pollen if the screen is double tapped - this can be used to kill spiders
--Add a hi-score table
--Re-cast the game as an underwater adventure with the main character as a submarine picking up jellyfish and avoiding sharks. Different weather effects would be required, for example bubbles rising to the surface instead of raindrops
--Add "the flight of the bumblebee" as a soundtrack - the faster you go, the faster the soundtrack plays
--Add vertical scrolling and hills rather than a flat terrain.
--Have fun! Any questions, then drop me (@West) on the Codea forums
--Set up screen and fix orientation to landscape
--for development change to FULLSCREEN to make it easier to get back to the development environment. A triple tap while in FULLSCREEN_NO_BUTTONS will bring up the buttons to allow you to exit, but this can be a pain.
displayMode(FULLSCREEN_NO_BUTTONS)
supportedOrientations(LANDSCAPE_ANY)
backingMode(STANDARD)
function setup()
--Set up constants to hold the various game states
MENU=1 --the main start screen
PLAY=2 --the game is being played
GAMEOVER=3 --the game has been lost
WON=4 -- the game has been won
gamestate=MENU --set the current state of the game to menu
--an array to hold the touches
touches={}
--A variable to hold different game over messages
msg=""
--set initial sizes for the bee
beebodysizex=100
beebodysizey=100
beewingsizex=130
beewingsizey=130
beescalefactor=0.5
--Sprite sheet information
--The spritesheet image contains all the graphical elements of the game
--The spritesheet is split into 100 by 100 pixel blocks (in the main)
--download the spritesheet from here:https://www.dropbox.com/s/ztwc51abhl3ks1k/foggyspritesheet%402x.png
--update the next line to point to the spritesheet file
spritesheetimg=readImage("Dropbox:foggyspritesheet")
cols=10 --number of columns thatmakeupthe spritesheef
rows=6 -- number of rows that make up the spritesheet
--background is made up of a number of repeating objects which scroll across the screen at different speeds
numlayers=8
bgxsize={WIDTH,800,20,200,30,40,65,98}
bgysize={HEIGHT,450,120,60,90,114,195,289}
bgxpos={WIDTH/2,0,0,0,0,0,0,0}
bgypos={120,80,100,50,20,15,10,100}
--x,y,width and height arrays of each background object with reference to the appropriate spritesheet 100 by 100 block
--layers are: mountain, mountain, fence, wall, flowers x4
bgx={6,6,4,0,5,5,5,5}
bgy={0,0,0,3,0,0,0,0}
bgw={4,4,0.5,3,1,1,1,1}
bgh={3,3,2,1,3,3,3,3}
scrollspeed={0,0.2,0.3,0.4,0.6,0.7,0.8,0.9} -- the speed at which the object moves across the screen - 0 means it doesn't move
overlap={1.5,1.9,1,1,1,1,1,1}--the amount an object overlaps the next object
layerdata={}
for i=1,numlayers do
layerdata[i]={} --an array for holding a list of the objects. If a 0 is stored the then object will be drawn. If it is a 0 then it won't
for j=1,1000 do
--for the nearest layer (large flower) don't draw 14 out of 15 (on average)
if i==8 then
layerdata[i][j]=math.random(15)-1
if layerdata[i][j]>1 then
layerdata[i][j]=1
end
--for the second nearest layer don't draw half of them (on average)
elseif i==7 then
layerdata[i][j]=math.random(2)-1
else
layerdata[i][j]=0
end
end
end
--a mesh to hold all the graphical elements. The order in which the objects are added to the mesh matters, so in draw the scene will be built up from the back forward. The sky elements will be drawn first, followed by the mountains then clouds, then fence and wall, etc
mainmesh=mesh()
mainmesh.texture=spritesheetimg
end
function initialise()
--initialise variables - called when the game is reset
sunAngle=0
globalx=150
anim=0
speed=8
minspeed=8
upthrust=0
beex=150
grav=-12
beevel=0
t=0.1
beey=HEIGHT/2
topSpeed=30
score=0
numrings=250
ringx={}
ringy={}
ringmarker={}
ringscale={}
ringcount=0
ringy[1]=HEIGHT/2
b=0
--generate 500 rings at random. Each subsequent ring should be displaced vertically by a random distance between -50 and +50
for i=1,numrings do
ringx[i]=500*i --each ring is placed 500 pixels apart
ringmarker[i]=0
ringscale[i]=2+math.random(6)/2
if i>1 then
ringy[i]=ringy[i-1]-100+math.random(200)
if ringy[i]>HEIGHT-100 then
ringy[i]=HEIGHT-100
end
if ringy[i]<100 then
ringy[i]=100
end
end
end
end
-- This function gets called once every frame
function draw()
if gamestate==MENU then
--main menu screen - not very exciting but does the job. Double tap to play the game
background(33, 46, 69, 255)
font("ArialRoundedMTBold")
fontSize(72)
text("Foggy Bummer",WIDTH/2,HEIGHT/2)
fontSize(36)
text("by West",WIDTH/2,HEIGHT/2-120)
text("Double tap screen to start",WIDTH/2,HEIGHT/2-240)
fontSize(12)
--check for a double tap
for k,touch in pairs(touches) do
if touch.tapCount==2 then
initialise()
gamestate=PLAY
end
end
elseif gamestate==GAMEOVER then
--Display a message to the user stating the game is over along with the score
background(63, 32, 32, 255)
fill(230, 165, 61, 255)
font("ArialRoundedMTBold")
fontSize(72)
text("Game Over",WIDTH/2,HEIGHT/2+120)
fontSize(36)
text(msg,WIDTH/2,HEIGHT/2)
text("Your score was: "..score,WIDTH/2,HEIGHT/2-120)
fontSize(12)
--check for as double tap to restart
for k,touch in pairs(touches) do
if touch.tapCount==2 then
initialise()
gamestate=PLAY
end
end
elseif gamestate==WON then
--Display a message to the user stating they have won
background(87, 246, 23, 255)
fill(57, 58, 220, 255)
font("ArialRoundedMTBold")
fontSize(72)
text("Congratulations you won!",WIDTH/2,HEIGHT/2)
fontSize(36)
text("Your score was: "..score,WIDTH/2,HEIGHT/2-120)
fontSize(12)
--check for a double tap to play again
for k,touch in pairs(touches) do
if touch.tapCount==2 then
initialise()
gamestate=PLAY
end
end
elseif gamestate==PLAY then
--Clear the mesh
mainmesh:clear()
background(60, 120, 240, 255)
sunAngle = sunAngle - 0.001
time=360-math.deg(sunAngle)+270
if time>360 then
time = time- 360
end
--Calculate an equivalent time
hours=math.floor(24*time/360)
mins=math.floor(60*((24*time/360)-hours))
if mins<10 then
timestr=hours..":0"..mins
else
timestr=hours..":"..mins
end
if sunAngle<0 then
sunAngle=math.rad(360)
end
--Deal with the user interaction
--If the screen is currently being touched
if CurrentTouch.state==MOVING or CurrentTouch.state==BEGAN then
upthrust = upthrust + 10 --cumulatively add some upward force - alter the 10 to get different levels of force
--limit the force
if upthrust>30 then
upthrust=30
end
end
--decrease the upward force when the finger is removed, but not immediately
if CurrentTouch.state==ENDED then
upthrust=upthrust-5
if upthrust<0 then
upthrust=0
end
accx=0
end
acc=upthrust+grav -- change the vertical displacemnt according to the amount of force
--equation of motion to calculate the displacement in the y direction s=ut+0.5at*t
beey=beey+beevel*t+0.5*acc*t*t
--equation of motion for the new velocity v=u+at
beevel=beevel+acc*t
speed=speed-0.01
if speed>topSpeed then
speed=topSpeed
end
if speed<minspeed then
speed=minspeed
end
globalx = globalx + speed
--Check to see if the bee hits the top of the screen - if so then game over
if beey>HEIGHT-20 then
beey=HEIGHT-20
upthrust=0
gamestate=GAMEOVER
msg="Like Icarus, you flew too high"
end
--check to see if the been hits the ground - if so then game over
if beey<20 then
beey=20
speed=speed-0.5
gamestate=GAMEOVER
msg="Bees cannot run"
end
--create the meshes for the scrolling background layer by layer
for i=1,numlayers do
for j=1,overlap[i]*(WIDTH/bgxsize[i])+3 do
if layerdata[i][j]==0 then
local gid = mainmesh:addRect(((bgxpos[i])+bgxsize[i]*j-bgxsize[i])/overlap[i],bgypos[i],bgxsize[i],bgysize[i])
mainmesh:setRectTex(gid, bgx[i]/cols, bgy[i]/rows, bgw[i]/cols, bgh[i]/rows)
end
end
end
--calculate and shift the layers depending on speed
for i=1,numlayers do
bgxpos[i]=bgxpos[i]-(scrollspeed[i]*speed)
if bgxpos[i]>bgxsize[i] then
bgxpos[i]=0
temp=layerdata[i][#layerdata[i]]
for c=1,#layerdata[i] do
layerdata[i][#layerdata[i]-c+1]=layerdata[i][#layerdata[i]-c]
end
layerdata[i][1]=temp
end
if bgxpos[i]<-1*bgxsize[i] then
bgxpos[i]=0
temp=layerdata[i][1]
for c=1,#layerdata[i]-1 do
layerdata[i][c]=layerdata[i][c+1]
end
layerdata[i][#layerdata[i]]=temp
end
end
--draw the rings. The rings are split into 2 to give the impression that the bee is flying through the rings rather than in front or behind them
for i=1,numrings do
if ringx[i]>globalx-(WIDTH) and ringx[i]<globalx+(WIDTH) then
local idx = mainmesh:addRect(WIDTH/2+ringx[i]-globalx,ringy[i],(107/10)*ringscale[i],(224/5)*ringscale[i])
mainmesh:setRectTex(idx, 3/cols, 0/rows, 0.5/cols, 2/rows)
mainmesh:setRectColor(idx,255,255,255,90)
--check to see if the bee has hit a ring
if globalx>ringx[i]-20+WIDTH/2-150 and globalx<ringx[i]+20+WIDTH/2-150 and beey>ringy[i]-25*ringscale[i] and beey<ringy[i]+25*ringscale[i] and ringmarker[i]==0 then
--increase the score
score = score + (6-ringscale[i])*10
--increase the speed
speed = speed*2
ringmarker[i]=1
ringcount = ringcount + 1
sound(SOUND_PICKUP, 49740)
end
end
end
--always be slowing the bee
speed = speed*0.99
--add the bee body to the mesh
local idx = mainmesh:addRect(beex,beey,beebodysizex*beescalefactor,beebodysizey*beescalefactor)
mainmesh:setRectTex(idx, 0/cols, 1/rows, 1/cols, 1/rows)
--add the bee wing to the mesh. When adding rotate by anim degrees (use math.rad to convert to the equivalent im radians)
local idw = mainmesh:addRect(beex,beey+28*beescalefactor,beewingsizex*beescalefactor,beewingsizey*beescalefactor,math.rad(anim))
mainmesh:setRectTex(idw, 1/cols, 1/rows, 1/cols, 1/rows)
mainmesh:setRectColor(idw,255,255,255,180)
--cycle the bee wing animation marker
anim = anim + 6
if anim>27 then
anim=-6
end
--draw the second half of the rings which should appear in front of the bee
for i=1,numrings do
if ringx[i]>globalx-(WIDTH) and ringx[i]<globalx+(WIDTH) then
local idx = mainmesh:addRect(WIDTH/2+ringx[i]-globalx+(105/10)*ringscale[i]+1,ringy[i],(107/10)*ringscale[i],(224/5)*ringscale[i])
mainmesh:setRectTex(idx, 3.5/cols, 0/rows, 0.5/cols, 2/rows)
mainmesh:setRectColor(idx,255,255,255,90)
end
end
--if the full distance is covered then the player wins
if globalx>ringx[numrings]+500 then
gamestate=WON
end
--if time runs out then the player loses
if hours==5 and mins==59 then
gamestate=GAMEOVER
msg="You ran out of time"
end
--draw the mesh on the screen
mainmesh:draw()
font("ArialRoundedMTBold")
fontSize(36)
fill(213, 198, 89, 255)
--add the score and time to the screen
text("Score: "..score,100,730)
text(timestr,900,730)
end
end
function touched(touch)
--handle the touches
if touch.state == ENDED then
touches[touch.id] = nil
else
touches[touch.id] = touch
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment