Last active
December 22, 2015 15:09
-
-
Save dermotbalson/6490535 to your computer and use it in GitHub Desktop.
Lander2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 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