Skip to content

Instantly share code, notes, and snippets.

@clarityflowers
Created August 6, 2019 04:45
Show Gist options
  • Save clarityflowers/ba770128eb473c7c2e3819ee6686d331 to your computer and use it in GitHub Desktop.
Save clarityflowers/ba770128eb473c7c2e3819ee6686d331 to your computer and use it in GitHub Desktop.
-- Invaders!
-- A simple little "game" for an interview for the recurse center.
-- Runs uses Löve2d.
-- Download Löve at https://love2d.org/#download,
-- To run the game, rename it to "main.lua" and put it in its own folder.
-- Then navigate to that folder and run "love ."
-- All the state lives in one object.
-- I find that having this beforehand opens up possibilities
-- in the future that I always appreciate.
local state
-- CONSTANTS --
-- To gracefully handle screen-resizing, I'm using an abstract
-- units system that defines the game world as 100 units wide with
-- 5 units of padding on either side.
-- When drawing this will be scaled to the appropriate size.
local WORLD_WIDTH = 100
local WORLD_PADDING = 5
local FULL_WORLD_WIDTH = WORLD_WIDTH + WORLD_PADDING * 2
local WORLD_HEIGHT_RATIO = 0.7
local FULL_WORLD_HEIGHT = FULL_WORLD_WIDTH * WORLD_HEIGHT_RATIO
local WORLD_HEIGHT = FULL_WORLD_HEIGHT - WORLD_PADDING * 2
local PLAYER_RADIUS = 3
local PLAYER_SPEED = 30.0
local INVADER_RADIUS = 2
local INVADER_SPACING = INVADER_RADIUS * 2 + 2
local INVADER_SPEED = 10.0
-- Initialize the game
function love.load()
-- Set up the window to fit in the top-right corner of
-- my screen. Convenient for testing!
love.window.setMode(666, 666 * WORLD_HEIGHT_RATIO, {
borderless = true,
centered = false,
x = 610,
y = 0
})
state = {
player = {
x = 50.0,
moving_left = false,
moving_right = false
},
-- The invaders move as a unit, so they have a shared state
invaders = {
moving_right = true,
x = INVADER_RADIUS,
alive = {}
}
}
-- Assuming that they'll be able to be killed later, there's a 2D
-- array of booleans indicating Invader "alive" status. Maybe this
-- will want to be a full state object for each at some point! idk!
for _ = 1, 4, 1 do
local invader_row = {}
for _ = 1, 10, 1 do
table.insert(invader_row, true)
end
table.insert(state.invaders.alive, invader_row)
end
end
function love.keypressed(key)
if not state then return end
if key == "left" then
state.player.moving_left = true
elseif key == "right" then
state.player.moving_right = true
end
end
function love.keyreleased(key)
if not state then return end
if key == "left" then
state.player.moving_left = false
elseif key == "right" then
state.player.moving_right = false
end
end
function love.update(dt)
-- INVADER MOVEMENT --
local invaders = state.invaders
local invaders_count = #invaders.alive[1]
-- Change direction when we hit the right or left edge
if invaders.moving_right then
local right_edge = invaders.x + INVADER_SPACING * (invaders_count - 1) + INVADER_RADIUS
if right_edge >= WORLD_WIDTH then
invaders.moving_right = false
end
else
local left_edge = invaders.x - INVADER_RADIUS
if left_edge <= 0 then
invaders.moving_right = true
end
end
-- Move the appropriate direction
if invaders.moving_right then
invaders.x = invaders.x + dt * INVADER_SPEED
else
invaders.x = invaders.x - dt * INVADER_SPEED
end
-- PLAYER MOVEMENT --
local player = state.player
if player.moving_left then
player.x = math.max(player.x - dt * PLAYER_SPEED, PLAYER_RADIUS)
elseif player.moving_right then
player.x = math.min(player.x + dt * PLAYER_SPEED, WORLD_WIDTH - PLAYER_RADIUS)
end
end
-- Converts our abstract units to pixels
local function world_to_screen_distance(world_distance)
local screen_width = love.graphics.getWidth()
return world_distance * screen_width / FULL_WORLD_WIDTH
end
-- Converts a location in the world to a pixel-unit vector
-- padded by the world padding
local function world_location_to_screen(world_vector)
return {
world_to_screen_distance(world_vector[1] + WORLD_PADDING),
world_to_screen_distance(world_vector[2] + WORLD_PADDING)
}
end
function love.draw()
-- INVADERS --
for row_i, row in pairs(state.invaders.alive) do
for col_i, alive in pairs(row) do
if alive then
local pos = world_location_to_screen({
state.invaders.x + INVADER_SPACING * (col_i - 1),
INVADER_RADIUS + INVADER_SPACING * (row_i - 1)
})
love.graphics.circle(
"line",
pos[1],
pos[2],
world_to_screen_distance(INVADER_RADIUS)
)
end
end
end
-- PLAYER --
local player_pos = world_location_to_screen({
state.player.x,
WORLD_HEIGHT
})
local player_radius = world_to_screen_distance(PLAYER_RADIUS)
love.graphics.polygon(
"line",
player_pos[1] - player_radius, player_pos[2],
player_pos[1] + player_radius, player_pos[2],
player_pos[1], player_pos[2] - (player_radius * 2)
)
-- DEBUG --
-- show the edge of the padded world, to check if things are
-- lining up right
-- local world_top_left = world_location_to_screen({0, 0})
-- love.graphics.rectangle(
-- "line",
-- world_top_left[1],
-- world_top_left[2],
-- world_to_screen_distance(WORLD_WIDTH),
-- world_to_screen_distance(WORLD_HEIGHT)
-- )
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment