Skip to content

Instantly share code, notes, and snippets.

@dermotbalson
Last active December 22, 2015 15:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dermotbalson/6490535 to your computer and use it in GitHub Desktop.
Save dermotbalson/6490535 to your computer and use it in GitHub Desktop.
Lander2
--# Main
-- Lander2
--This version of the lunar lander tries to build it in a more structured way
--The first tab just creates all the game states
--The second tab adds the ship
--The third tab adds rocks and stats
--Rewriting code to be better structured is called "refactoring"
--Nobody likes doing it, but look at the difference in readability of the code
--And it makes it much easier to change code in future
--ignore the lines below,they help manage the code tabs
tabs={} f=""
for i=1,3 do f=f.."c"..i.."={} table.insert(tabs,c"..i..") " end
ff=loadstring(f) ff() ff=nil f=nil
--This code manages which Code tab is run
--it remembers your last choice, and if you select a different one, it runs that instead
function setup()
LastCode=readProjectData("Code") or 1
parameter.integer("Choose_a_tab",1,#tabs,LastCode,RunCode)
end
function RunCode()
output.clear()
saveProjectData("Code",Choose_a_tab)
print("CODE "..Choose_a_tab)
c=tabs[Choose_a_tab]
c.setup()
end
function draw()
c.draw()
end
function touched(touch)
if c.touched then c.touched(touch) end
end
--# 1_States
--This code sets up the different "states" of the game
--Ready (to start)
--Playing
--Landed
--Crashed
--These mainly affect what we draw on the screen
function c1.setup()
state="Ready" --ready to start
print("Controlling the game state")
end
--NB You'll see \n in some of the message text. This just inserts a new line
function c1.draw()
background(0) --black
if state=="Ready" then c1.Message("Touch the screen to begin playing")
elseif state=="Playing" then
if Thruster~=nil then c1.Message("Firing "..Thruster.." thruster!")
else c1.Message("Playing\nPress to fire thrusters\nTouch top to crash\nTouch bottom to land")
end
elseif state=="Crashed" then c1.Message("Crashed!\nTouch the screen to play again")
elseif state=="Landed" then c1.Message("Landed\nTouch the screen to play again")
end
end
--if the function below runs, then the user has touched the screen
--This function is provided by Codea and is triggered automatically
--the variable touch.state tells us what happened. It has three values
-- BEGAN when the user first touches
-- MOVING if the user is holding their finger down
-- ENDED when the user lifts their finger
--Every touch creates at least a BEGAN and ENDED event, so usually we look for one or the other, but not both
--There is an alternative. The CurrentTouch variable gives you information about the most recent touch
--but it starts with a state of BEGAN, so it's impossible to know if there has actually been a touch or not
--and after the first touch is ended, the state of CurrentTouch stays as ENDED until the next touch, so
--again, it's impossible to know if a touch has just ended or not.
--So the best approach is usually to use the touched function, because this definitely traps touches as they happen
--We want touches to do the following
-- 1. Start the game if it's only just loaded, or if we've crashed or landed
-- For this, it's best to use the ENDED event
-- If we used BEGAN, then the user might be slow to lift their finger and we'd get a MOVING event
-- that would fire the thrusters and confuse the user!
-- 2. Fire the thrusters, if we're playing
-- For this, we should use BEGAN and MOVING, ie fire thrusters as long as the finger is down
function c1.touched(touch)
if state~="Playing" and touch.state==ENDED then c1.StartGame() --start game if not already playing
elseif state=="Playing" then c1.FireThrusters(touch) --fire thrusters if playing
end
--test the landing and crashing states - this code is JUST for testing
--crash if we touch very top of screen, land if we touch very bottom of screen
--later we'll replace this with code in the draw function that tests for collisions and landing safely
if touch.y>HEIGHT-50 and state=="Playing" then c1.Crashed()
elseif touch.y<50 and state=="Playing" then c1.Landed()
end
end
function c1.StartGame()
state="Playing"
Thruster=nil --set thruster state to nil, nothing happening yet
end
function c1.FireThrusters(touch)
if touch.state==ENDED then Thruster=nil --if user lifts finger, stop thrusting
else --otherwise figure out which thruster to use
if touch.x<WIDTH/4 then Thruster="Left"
elseif touch.x>WIDTH*3/4 then Thruster="Right"
else Thruster="Main"
end
end
end
function c1.Crashed()
state="Crashed"
end
function c1.Landed()
state="Landed"
end
--putting the message in a function makes it easy to change the style of all our messages
function c1.Message(m)
pushStyle()
strokeWidth(3) --text thickness
font("Courier-Bold")
fontSize(30)
textMode(CENTER)
text(m,WIDTH/2,HEIGHT/2)
popStyle()
end
--# 2_Ship
--This code manages the ship
--I haven't marked all the changes because there are so many
function c2.setup()
state="Ready" --ready to start
print("Controlling the ship")
end
function c2.draw()
background(0) --black
if state=="Ready" then --show message and exit if waiting to start
c2.Message("Touch the screen to begin playing")
return
end
if state=="Playing" then
if Thruster==nil then
thrust=0
else
sound(SOUND_HIT, 16609)
if Thruster=="Left" then
fuel = fuel -.5
shipX=shipX+1
sprite(imgSideThrust,shipX-shipsize/2,shipY-shipsize*.2,shipsize/3)
elseif Thruster=="Main" then
sprite(imgThrust,shipX,shipY-shipsize,shipsize,shipsize)
thrust = math.min(maxThrust,thrust + 0.005)
fuel = fuel -1
elseif Thruster=="Right" then
fuel = fuel - .5
shipX=shipX-1
sprite(imgSideThrust,shipX+shipsize/2,shipY-shipsize*.2,shipsize/3)
end
end
speed=math.max(-maxSpeed,speed+gravity+thrust)
shipY=shipY+speed+0.5*(gravity+thrust)
sprite("Space Art:Red Ship",shipX,shipY,shipsize,shipsize)
--test for landing or collision
if shipY-shipsize/2<groundlevel then
if speed>-1 then
state="Landed"
else
state="Crashed"
sound(SOUND_EXPLODE, 23062)
end
shipY=groundlevel+shipsize/2
speed=0
thrust=0
end
elseif state=="Crashed" then
sprite("Tyrian Remastered:Explosion Huge",
shipX,shipY,shipsize+math.random(10),shipsize+math.random(10))
c2.Message("Crashed!\nTouch the screen to play again")
elseif state=="Landed" then
c2.Message("Landed\nTouch the screen to play again")
sprite("Space Art:Red Ship",shipX,shipY,shipsize,shipsize)
end
--draw ground
strokeWidth(3)
stroke(48, 216, 25, 255)
line(0,groundlevel,WIDTH,groundlevel)
stroke(255,0,0,255) --red
line(targetX-50,groundlevel,targetX+50,groundlevel)
end
function c2.touched(touch)
if state~="Playing" and touch.state==ENDED then c2.StartGame() --start game if not already playing
elseif state=="Playing" then c1.FireThrusters(touch) --fire thrusters if playing
end
end
function c2.StartGame()
state="Playing"
shipX=WIDTH/2 --starting position of ship
shipY=HEIGHT-100
shipsize=40
groundlevel=50
speed=0
gravity=-0.02
thrust=0
fuel=500
maxSpeed=3 --pixels per 1/60 of second
maxThrust=0.05
imgShip=readImage("Space Art:Red Ship")
imgThrust=readImage("Tyrian Remastered:Flame 2")
imgSideThrust=readImage("Tyrian Remastered:Bullet Fire B")
targetX=math.random(100,WIDTH-100)
Thruster=nil --set thruster state to nil, nothing happening yet
end
function c2.FireThrusters(touch)
if touch.state==ENDED then Thruster=nil --if user lifts finger, stop thrusting
else --otherwise figure out which thruster to use
if touch.x<WIDTH/4 then Thruster="Left"
elseif touch.x>WIDTH*3/4 then Thruster="Right"
else Thruster="Main"
end
end
end
--putting the message in a function makes it easy to change the style of all our messages
function c2.Message(m)
pushStyle()
strokeWidth(3) --text thickness
font("Courier-Bold")
fontSize(30)
textMode(CENTER)
text(m,WIDTH/2,HEIGHT/2)
popStyle()
end
--# 3_Rocks
--This code adds falling rocks - new lines shown with ****
function c3.setup()
state="Ready" --ready to start
timer=0 -- ********
rockTimer=0 -- ********
print("Rocks!")
end
function c3.draw()
background(0) --black
if state=="Ready" then --show message and exit if waiting to start
c3.Message("Touch the screen to begin playing")
return
end
if state=="Playing" then
timer=timer+DeltaTime --add time since last redraw, to timer ****NEW
if timer>rockTimer then --time for a rock! ****NEW
c3.AddRock() --use a function for this to avoid clutter ****NEW
timer=timer-rockTimer --reset timer ****NEW
end -- ****NEW
--draw rocks
for i,r in pairs(rocks) do -- ****NEW
--first check for collisions
if vec2(r.x,r.y):dist(vec2(shipX,shipY))<shipsize/2+r.size/2 then -- ****NEW
sound(SOUND_EXPLODE, 23062) --we hit a rock
state="Crashed"
end
--adjust rock position if not already on the ground
if r.y>groundlevel then
r.x=r.x+r.dx
r.y=r.y+r.dy
end
--draw rock
sprite(imgRock,r.x,r.y,r.size)
end -- **** end of NEW rock code
if Thruster==nil then
thrust=0
else
sound(SOUND_HIT, 16609)
if Thruster=="Left" then
fuel = fuel -.5
shipX=shipX+1
sprite(imgSideThrust,shipX-shipsize/2,shipY-shipsize*.2,shipsize/3)
elseif Thruster=="Main" then
sprite(imgThrust,shipX,shipY-shipsize,shipsize,shipsize)
thrust = math.min(maxThrust,thrust + 0.005)
fuel = fuel -1
elseif Thruster=="Right" then
fuel = fuel - .5
shipX=shipX-1
sprite(imgSideThrust,shipX+shipsize/2,shipY-shipsize*.2,shipsize/3)
end
end
speed=math.max(-maxSpeed,speed+gravity+thrust)
shipY=shipY+speed+0.5*(gravity+thrust)
sprite("Space Art:Red Ship",shipX,shipY,shipsize,shipsize)
--test for landing or collision
if shipY-shipsize/2<groundlevel then
if speed>-1 then
state="Landed"
else
state="Crashed"
sound(SOUND_EXPLODE, 23062)
end
shipY=groundlevel+shipsize/2
speed=0
thrust=0
end
elseif state=="Crashed" then
sprite("Tyrian Remastered:Explosion Huge",
shipX,shipY,shipsize+math.random(10),shipsize+math.random(10))
c3.Message("Crashed!\nTouch the screen to play again")
elseif state=="Landed" then
c3.Message("Landed\nTouch the screen to play again")
sprite("Space Art:Red Ship",shipX,shipY,shipsize,shipsize)
end
--draw ground
strokeWidth(3)
stroke(48, 216, 25, 255)
line(0,groundlevel,WIDTH,groundlevel)
stroke(255,0,0,255) --red
line(targetX-50,groundlevel,targetX+50,groundlevel)
--draw stats
c3.Stats()
end
function c3.touched(touch)
if state~="Playing" and touch.state==ENDED then c3.StartGame() --start game if not already playing
elseif state=="Playing" then c1.FireThrusters(touch) --fire thrusters if playing
end
end
function c3.StartGame()
state="Playing"
shipX=WIDTH/2 --starting position of ship
shipY=HEIGHT-100
shipsize=40
groundlevel=50
speed=0
gravity=-0.02
thrust=0
fuel=500
maxSpeed=3 --pixels per 1/60 of second
maxThrust=0.05
imgShip=readImage("Space Art:Red Ship")
imgThrust=readImage("Tyrian Remastered:Flame 2")
imgSideThrust=readImage("Tyrian Remastered:Bullet Fire B")
imgRock=readImage("Tyrian Remastered:Rock 3") -- *****
targetX=math.random(100,WIDTH-100)
Thruster=nil --set thruster state to nil, nothing happening yet
rocks={} --create a table to hold the rocks that will fall ****
rockTimer=1 --time between rocks, in seconds ****
timer=0 --timer that will tell us if it's time for a new rock ****
end
function c3.FireThrusters(touch)
if touch.state==ENDED then Thruster=nil --if user lifts finger, stop thrusting
else --otherwise figure out which thruster to use
if touch.x<WIDTH/4 then Thruster="Left"
elseif touch.x>WIDTH*3/4 then Thruster="Right"
else Thruster="Main"
end
end
end
function c3.AddRock() -- all new ****
--select random point at top of screen, and random point on ground, and move from one to the other
--we'll create all the settings for a rock, then add it to our table of rocks
r={} --all the settings for the new rock will be in this table.
--get random x, y will be at top of screen
r.x=math.random(0,WIDTH)
r.y=HEIGHT+20 --just push it off the top of the screen a little
r.gx=r.x+400*(math.random()-.5) --impact point on ground will be within 200 pixels either side of start x
r.speed=math.random(50,100) --speed in pixels per second
--figure out what fraction of the distance between start and finish will be covered in each redraw
--we know we are drawing 60 times a second
--so first get the distance we are going to travel
r.dist=vec2(r.x,r.y):dist(vec2(r.gx,groundlevel)) --distance
r.redraws=(r.dist/r.speed)*60 --number of redraws taken to get to ground
r.dx = (r.gx-r.x)/r.redraws --movement in x on each draw
r.dy = (groundlevel-r.y)/r.redraws --movement in y on each draw
r.size=math.random(5,25) --random size
table.insert(rocks,r) --add to rocks table (yes, you can add a table to another table)
end
--putting the message in a function makes it easy to change the style of all our messages
function c3.Message(m)
pushStyle()
strokeWidth(3) --text thickness
font("Courier-Bold")
fontSize(30)
textMode(CENTER)
text(m,WIDTH/2,HEIGHT/2)
popStyle()
end
function c3.Stats()
font("Courier-Bold")
fontSize(30)
pushStyle()
textMode(CORNER) --left align the text
--print in the top left corner of the screen. Use .. to join bits of string tp parameters
text("Fuel:"..fuel,50,HEIGHT-50)
text("Height:"..math.floor(shipY-groundlevel-12),50,HEIGHT-80)
text("Speed:"..math.floor(-speed),50,HEIGHT-110)
text("Target:"..math.floor(shipX-targetX),50,HEIGHT-140)
popStyle()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment