singe/docs/learn/13-game-folder.singe
2026-09-22 21:57:42 -05:00

249 lines
5.5 KiB
Text

-- Lesson 13: A Game Folder. The finished script, exactly as the lesson ends.
dofile("Singe/Framework.singe")
local SHIP_WIDTH = 32
local SHIP_HEIGHT = 24
local SHIP_PAD_X = 6
local SHIP_PAD_Y = 5
local SHIP_SPEED = 3
local ROCK_WIDTH = 24
local ROCK_HEIGHT = 24
local ROCK_PAD = 4
local ROCK_SPEED = 1
local ROCK_CHANCE = 40
local ROCK_POINTS = 10
local SHOT_WIDTH = 4
local SHOT_HEIGHT = 10
local SHOT_SPEED = 6
local START_LIVES = 3
local TITLE = "title"
local PLAYING = "playing"
local OVER = "over"
local shipSprite = spriteLoad(DIR .. "art/ship.png")
local rockSprite = spriteLoad(DIR .. "art/rock.png")
local shotSprite = spriteLoad(DIR .. "art/shot.png")
local shootSound = soundLoad(DIR .. "art/shoot.wav")
local boomSound = soundLoad(DIR .. "art/boom.wav")
local state = TITLE
local score = 0
local lives = START_LIVES
local best = saveGet("highScore", 0)
local shipX = (overlayGetWidth() - SHIP_WIDTH) / 2
local shipY = overlayGetHeight() - SHIP_HEIGHT - 8
local movingLeft = false
local movingRight = false
local rocks = {}
local shots = {}
local function addRock()
local rock = {}
rock.x = math.random(0, overlayGetWidth() - ROCK_WIDTH)
rock.y = -ROCK_HEIGHT
rocks[#rocks + 1] = rock
end
local function fireShot()
local shot = {}
shot.x = shipX + SHIP_WIDTH / 2 - SHOT_WIDTH / 2
shot.y = shipY - SHOT_HEIGHT
shots[#shots + 1] = shot
soundPlay(shootSound)
end
local function rockBox(rock)
return rock.x + ROCK_PAD, rock.y + ROCK_PAD, ROCK_WIDTH - ROCK_PAD * 2, ROCK_HEIGHT - ROCK_PAD * 2
end
local function shipBox()
return shipX + SHIP_PAD_X, shipY + SHIP_PAD_Y, SHIP_WIDTH - SHIP_PAD_X * 2, SHIP_HEIGHT - SHIP_PAD_Y * 2
end
local function shipHitsRock(rock)
local sx, sy, sw, sh = shipBox()
local rx, ry, rw, rh = rockBox(rock)
return collideRects(sx, sy, sw, sh, rx, ry, rw, rh)
end
local function shotHitsRock(shot, rock)
local rx, ry, rw, rh = rockBox(rock)
return collidePointRect(shot.x + SHOT_WIDTH / 2, shot.y, rx, ry, rw, rh)
end
local function startGame()
score = 0
lives = START_LIVES
rocks = {}
shots = {}
shipX = (overlayGetWidth() - SHIP_WIDTH) / 2
movingLeft = false
movingRight = false
state = PLAYING
end
local function endGame()
if score > best then
best = score
saveSet("highScore", best)
saveFlush()
end
state = OVER
end
local function moveShip()
if movingLeft then
shipX = shipX - SHIP_SPEED
end
if movingRight then
shipX = shipX + SHIP_SPEED
end
if shipX < 0 then
shipX = 0
end
if shipX > overlayGetWidth() - SHIP_WIDTH then
shipX = overlayGetWidth() - SHIP_WIDTH
end
end
local function moveRocks()
if math.random(ROCK_CHANCE) == 1 then
addRock()
end
for i = #rocks, 1, -1 do
local rock = rocks[i]
rock.y = rock.y + ROCK_SPEED
if rock.y > overlayGetHeight() then
table.remove(rocks, i)
end
end
end
local function moveShots()
for i = #shots, 1, -1 do
local shot = shots[i]
shot.y = shot.y - SHOT_SPEED
if shot.y + SHOT_HEIGHT < 0 then
table.remove(shots, i)
end
end
end
local function checkHits()
for s = #shots, 1, -1 do
for r = #rocks, 1, -1 do
if shotHitsRock(shots[s], rocks[r]) then
table.remove(rocks, r)
table.remove(shots, s)
soundPlay(boomSound)
score = score + ROCK_POINTS
break
end
end
end
for r = #rocks, 1, -1 do
if shipHitsRock(rocks[r]) then
table.remove(rocks, r)
soundPlay(boomSound)
lives = lives - 1
if lives <= 0 then
endGame()
end
end
end
end
local function drawWorld()
spriteDraw(shipSprite, shipX, shipY)
for _, rock in ipairs(rocks) do
spriteDraw(rockSprite, rock.x, rock.y)
end
for _, shot in ipairs(shots) do
spriteDraw(shotSprite, shot.x, shot.y)
end
end
local function drawScore()
overlayPrint(1, 1, "SCORE " .. score)
overlayPrint(25, 1, "BEST " .. best)
overlayPrint(48, 1, "LIVES " .. lives)
end
local function updateTitle()
overlayPrint(25, 5, "R O C K S")
overlayPrint(24, 8, "BEST " .. best)
overlayPrint(21, 11, "PRESS FIRE TO PLAY")
end
local function updatePlaying()
moveShip()
moveRocks()
moveShots()
checkHits()
drawWorld()
drawScore()
end
local function updateOver()
drawWorld()
drawScore()
overlayPrint(25, 8, "GAME OVER")
overlayPrint(21, 11, "PRESS FIRE TO PLAY")
end
function onInputPressed(what)
if state == PLAYING then
if what == SWITCH_LEFT then
movingLeft = true
elseif what == SWITCH_RIGHT then
movingRight = true
elseif what == SWITCH_BUTTON1 then
fireShot()
end
elseif what == SWITCH_BUTTON1 or what == SWITCH_START1 then
startGame()
end
end
function onInputReleased(what)
if what == SWITCH_LEFT then
movingLeft = false
elseif what == SWITCH_RIGHT then
movingRight = false
end
end
function onOverlayUpdate()
overlayClear()
if state == TITLE then
updateTitle()
elseif state == PLAYING then
updatePlaying()
else
updateOver()
end
return OVERLAY_UPDATED
end