3
\$\begingroup\$

I ported a little game I originally made in Macroquad to Love2d. But I have also added additional features, like additional animations and screen shake. I have used HUMP for various utilities. shack for screen shake. HC for collision and tiny-ecs for an entity component system. You are a circle with a gun that shoots automatically at other enemies. The enemies follow you around and your goal is to dodge them. The game looks like this:

Game

Here is the code:

local Gamestate = require("hump.gamestate")
local Vector = require("hump.vector")
local Timer = require("hump.timer")
local Camera = require("hump.camera")
local HC = require("HC")
local tiny = require("tiny")
local screen = require("shack")
-- Different scenes for the game.
local game = {}
local gameover = {}
-- Processing logic here for enemies, bullets, and tank.
local processEnemies = tiny.processingSystem()
processEnemies.filter = tiny.filter("radius&pos&angle&speed&shape&!gun_length")
function processEnemies:onAdd(e)
 Timer.tween(0.5, e, {angle=2*math.pi}, 'linear', function() 
 e.speed = 50 
 end) 
end
function processEnemies:process(e, dt)
 love.graphics.setLineWidth(5)
 love.graphics.arc( "line", "open", 
 e.pos.x, e.pos.y,
 e.radius, 0, e.angle)
 e.pos = e.pos + (tank.pos - e.pos):normalizeInplace() * dt * e.speed
 e.shape:moveTo(e.pos.x, e.pos.y)
end
local processBullet = tiny.processingSystem()
processBullet.filter = tiny.filter("pos&shape&speed&bullet")
function processBullet:onAdd(e)
 e.pos = tank.pos
 e.shape:moveTo(e.pos.x, e.pos.y)
 e.dir = tank.gun_vec:normalized()
end
function processBullet:process(e, dt)
 love.graphics.setLineWidth(5)
 love.graphics.circle("fill", e.pos.x, e.pos.y, 5)
 e.pos = e.pos + e.dir * dt * e.speed
 e.shape:moveTo(e.pos.x, e.pos.y)
 for shape, delta in pairs(HC.collisions(e.shape)) do
 if enemies[shape] ~= nil then
 world:remove(enemies[shape])
 table.remove(enemies[shape])
 end
 end
end
local processTank = tiny.processingSystem()
processTank.filter = tiny.filter("radius&pos&angle&gun_length&gun_vec")
function processTank:onAdd(e)
 local drawGun = function()
 Timer.tween(0.5, e, {gun_length=20}, 'linear',
 function()
 e.speed = 100
 end)
 end
 Timer.tween(0.5, e, {angle=2*math.pi}, 'linear', drawGun)
end
function tankKeypress(e, dt)
 -- Handle key presses.
 local delta = Vector(0, 0)
 if love.keyboard.isDown('left') then
 delta.x = -1
 elseif love.keyboard.isDown('right') then
 delta.x = 1
 end
 if love.keyboard.isDown('up') then
 delta.y = -1
 elseif love.keyboard.isDown('down') then
 delta.y = 1
 end
 delta:normalizeInplace()
 e.pos = e.pos + delta * dt * e.speed 
end
function tankCollision(e, dt)
 -- Update collision information.
 e.shape:moveTo(e.pos.x, e.pos.y)
 e.shot_radius:moveTo(e.pos.x, e.pos.y)
 local gun_angle = 0
 for shape, delta in pairs(HC.collisions(e.shot_radius)) do
 if enemies[shape] ~= nil then
 local sx, sy = shape:center()
 gun_angle = math.atan2(sy - e.pos.y, sx - e.pos.x)
 break
 end
 end
 for shape, delta in pairs(HC.collisions(e.shape)) do
 if enemies[shape] ~= nil then
 screen:setShake(20)
 local new_health = healthbar.health - 0.5
 Timer.tween(0.5, healthbar, {
 health=math.max(new_health, 0)
 }, "linear")
 if healthbar.health <= 0 then
 Gamestate.switch(gameover)
 end
 break
 end
 end
 return gun_angle
end
function processTank:process(e, dt)
 tankKeypress(e, dt)
 local gun_angle = tankCollision(e, dt)
 -- Draw tank.
 e.gun_vec = Vector(e.gun_length, 0):rotated(gun_angle)
 love.graphics.setLineWidth(5)
 love.graphics.arc( "line", "open", 
 e.pos.x, e.pos.y,
 e.radius, 0, e.angle)
 love.graphics.line(e.pos.x, e.pos.y,
 e.pos.x + e.gun_vec.x,
 e.pos.y + e.gun_vec.y)
end
function game:draw()
 -- Draw healthbar.
 love.graphics.line(10, 10,
 10 + healthbar.health, 10)
 camera:attach()
 screen:apply()
 world:update(love.timer.getDelta())
 camera:detach()
end
function game:update(dt)
 -- Handle timers and screen shake libraries.
 Timer.update(dt)
 screen:update(dt)
 -- Handle camera.
 local CAMERA_SLOW_FACTOR = 100
 local dx, dy = tank.pos.x - camera.x, tank.pos.y - camera.y
 camera:move(dx / CAMERA_SLOW_FACTOR, dy / CAMERA_SLOW_FACTOR) 
end
function gameover:draw()
 love.graphics.print('The End', 
 love.graphics.getWidth() / 2, 
 love.graphics.getHeight() / 2)
end
function game:init()
 -- For checking if an enemy is in range of a player.
 tank = {
 pos=Vector(300, 400), gun_vec=Vector(1,0),
 gun_length=0, speed=0, angle=0, radius=20,
 shape=HC.circle(300, 400, 20),
 shot_radius=HC.circle(300, 400, 50)
 }
 enemies = {}
 healthbar = {health = 60}
 world = tiny.world(processTank, processEnemies, processBullet, tank)
 camera = Camera(tank.pos.x, tank.pos.y)
 -- Spawn enemies.
 Timer.every(1, function()
 local angle = love.math.random()
 local x = tank.pos.x + 300 * math.cos(2 * math.pi * angle)
 local y = tank.pos.y + 300 * math.sin(2 * math.pi * angle)
 local shape = HC.circle(x, y, 20)
 local enemy = {
 pos=Vector(x, y), speed=0, angle=0, radius=20,
 shape=shape
 }
 tiny.add(world, enemy)
 enemies[shape] = enemy
 end)
 -- Spawn bullets.
 Timer.every(5, function()
 tiny.add(world, {
 speed=100, 
 shape=HC.point(0,0),
 pos=Vector(0, 0),
 bullet = {}
 })
 end)
end
function love.load()
 Gamestate.registerEvents()
 Gamestate.switch(game)
end
toolic
14.9k5 gold badges29 silver badges206 bronze badges
asked Apr 1 at 2:12
\$\endgroup\$

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.