1
0

add animated bolts

This commit is contained in:
Rokas Puzonas 2022-08-05 15:38:20 +00:00
parent a445461075
commit afeb219786
8 changed files with 198 additions and 126 deletions

View File

@ -1,3 +1,86 @@
local cargo = require("lib.cargo") local cargo = require("lib.cargo")
local aseLoader = require("lib.ase-loader")
local pprint = require("lib.pprint")
return cargo.init{ dir = "data" } local function loadAsepriteSprite(filename)
local sprite = {}
local ase = aseLoader(filename)
sprite.width = ase.header.width
sprite.height = ase.header.height
sprite.variants = {}
local LAYER_CHUNK = 0x2004
local CEL_CHUNK = 0x2005
local TAG_CHUNK = 0x2018
local frames = {}
local tags = {}
local layers = {}
local first_tag
for i, ase_frame in ipairs(ase.header.frames) do
for _, chunk in ipairs(ase_frame.chunks) do
if chunk.type == CEL_CHUNK then
local cel = chunk.data
local buffer = love.data.decompress("data", "zlib", cel.data)
local data = love.image.newImageData(cel.width, cel.height, "rgba8", buffer)
local image = love.graphics.newImage(data)
local frame = frames[i]
if not frame then
frame = {
image = love.graphics.newCanvas(sprite.width, sprite.height),
duration = ase_frame.frame_duration / 1000
}
frame.image:setFilter("nearest", "nearest")
frames[i] = frame
end
-- you need to draw in a canvas before.
-- frame images can be of different sizes
-- but never bigger than the header width and height
love.graphics.setCanvas(frame.image)
love.graphics.draw(image, cel.x, cel.y)
love.graphics.setCanvas()
elseif chunk.type == TAG_CHUNK then
for j, tag in ipairs(chunk.data.tags) do
-- first tag as default
if j == 1 then
first_tag = tag.name
end
-- aseprite use 0 notation to begin
-- but in lua, everthing starts in 1
tag.to = tag.to + 1
tag.from = tag.from + 1
tag.frames = tag.to - tag.from
tags[tag.name] = tag
end
elseif chunk.type == LAYER_CHUNK then
table.insert(layers, chunk.data)
end
end
end
for _, tag in pairs(tags) do
local variant = {}
sprite.variants[tag.name] = variant
for i=tag.from,tag.to do
table.insert(variant, frames[i])
end
end
if not sprite.variants.default then
sprite.variants.default = {frames[0]}
end
return sprite
end
return cargo.init{
dir = "data",
loaders = {
aseprite = loadAsepriteSprite,
ase = loadAsepriteSprite,
}
}

Binary file not shown.

Binary file not shown.

View File

@ -7,12 +7,12 @@ function MainState:enter(_, host_socket)
local groups = { local groups = {
physical = {filter = {"pos", "vel"}}, physical = {filter = {"pos", "vel"}},
player = {filter = { player = {filter = {
"pos", "acc", "speed", "pos", "acc", "speed", "bolts"
"bolt_count", -- "bolt_count",
"bolt_cooldown", "bolt_speed", "bolt_friction" -- "bolt_cooldown", "bolt_speed", "bolt_friction"
}}, }},
controllable_player = {filter = {"controllable"}}, controllable_player = {filter = {"controllable"}},
sprite = {filter = {"sprite"}}, sprite = {filter = {"pos", "sprite"}},
bolt = {filter={"pos", "vel", "bolt"}}, bolt = {filter={"pos", "vel", "bolt"}},
collider = {filter={"pos", "collider"}} collider = {filter={"pos", "collider"}}
} }

View File

@ -4,7 +4,7 @@ local rgb = require("helpers.rgb")
local DRAW_GRID = false local DRAW_GRID = false
local GRID_COLOR = rgb(30, 30, 30) local GRID_COLOR = rgb(30, 30, 30)
local DRAW_COLLIDERS = true local DRAW_COLLIDERS = false
local COLLIDER_COLOR = rgb(200, 20, 200) local COLLIDER_COLOR = rgb(200, 20, 200)
function Debug:drawColliders() function Debug:drawColliders()

View File

@ -36,6 +36,9 @@ function Physics:removeFromGroup(group, e)
end end
function Physics:_collisionFilter(entity, other) function Physics:_collisionFilter(entity, other)
if entity.hidden then return end
if other.hidden then return end
if entity.bolt then if entity.bolt then
return "bounce" return "bounce"
elseif entity.vel or other.vel then elseif entity.vel or other.vel then

View File

@ -4,6 +4,8 @@ local Vec = require("lib.brinevector")
local controls = data.controls local controls = data.controls
local BOLT_COLLIDER = {-3, -3, 3, 3}
local function getDirection(up_key, down_key, left_key, right_key) local function getDirection(up_key, down_key, left_key, right_key)
local dx = (love.keyboard.isDown(right_key) and 1 or 0) local dx = (love.keyboard.isDown(right_key) and 1 or 0)
- (love.keyboard.isDown(left_key) and 1 or 0) - (love.keyboard.isDown(left_key) and 1 or 0)
@ -77,7 +79,7 @@ function Player:update(dt)
-- Check if the player tried to shoot a bolt -- Check if the player tried to shoot a bolt
if love.keyboard.isDown(controls.shoot) then if love.keyboard.isDown(controls.shoot) then
if self:tryShootinBolt(e) then if self:tryShootingBolt(e) then
self.pool:emit("playerShot", e) self.pool:emit("playerShot", e)
end end
end end
@ -95,8 +97,12 @@ function Player:update(dt)
-- If the player is nearby a non-moving bolt, pick it up -- If the player is nearby a non-moving bolt, pick it up
for _, bolt in ipairs(self.pool.groups.bolt.entities) do for _, bolt in ipairs(self.pool.groups.bolt.entities) do
if (e.pos-bolt.pos).length < 20 and bolt.vel.length < 3 then if (e.pos-bolt.pos).length < 20 and bolt.vel.length < 3 then
self.pool:removeEntity(bolt) bolt.collider = nil
e.bolt_count = e.bolt_count + 1 bolt.bolt = nil
bolt.sprite.variant = "idle"
bolt.hidden = true
self.pool:queue(bolt)
table.insert(e.bolts, bolt)
end end
end end
@ -105,26 +111,22 @@ function Player:update(dt)
else else
e.sprite.variant = "idle" e.sprite.variant = "idle"
end end
end
end
function Player:tryShootinBolt(player) if e.acc.x < 0 then
if (player.aim_dir.x ~= 0 or player.aim_dir.y ~= 0) and e.sprite.flip = true
player.bolt_cooldown_timer == 0 and elseif e.acc.x > 0 then
player.bolt_count > 0 e.sprite.flip = false
then end
player.bolt_cooldown_timer = player.bolt_cooldown
player.bolt_count = player.bolt_count - 1
return self.pool:queue{ if #e.bolts > 0 then
pos = player.pos + player.aim_dir * 30, local bolt = e.bolts[1]
vel = player.aim_dir * player.bolt_speed, if e.aim_dir.x ~= 0 or e.aim_dir.y ~= 0 then
friction = player.bolt_friction, bolt.hidden = nil
max_speed = 400, bolt.pos = e.pos + e.aim_dir * 15
bolt = true, else
sprite = {}, bolt.hidden = true
collider = {-3, -3, 3, 3} end
} end
end end
end end
@ -132,8 +134,41 @@ function Player:mousemoved()
self.use_mouse_aim = true self.use_mouse_aim = true
end end
function Player:tryShootingBolt(player)
if (player.aim_dir.x ~= 0 or player.aim_dir.y ~= 0) and
player.bolt_cooldown_timer == 0 and
#player.bolts > 0
then
player.bolt_cooldown_timer = player.bolt_cooldown
self:shootBolt(player)
return true
end
end
function Player:shootBolt(player)
local bolt = table.remove(player.bolts, 1)
if not bolt then return end
bolt.pos = player.pos + player.aim_dir * 30
bolt.vel = player.aim_dir * player.bolt_speed
bolt.bolt = true
bolt.sprite.variant = "active"
bolt.collider = BOLT_COLLIDER
bolt.hidden = nil
self.pool:queue(bolt)
end
function Player:storeBolt(player, bolt)
bolt.collider = nil
bolt.sprite.variant = "idle"
bolt.hidden = true
bolt.bolt = nil
self.pool:queue(bolt)
table.insert(player.bolts, bolt)
end
function Player:spawnPlayer() function Player:spawnPlayer()
return self.pool:queue{ local player = self.pool:queue{
pos = Vec(100, 100), pos = Vec(100, 100),
vel = Vec(0, 0), vel = Vec(0, 0),
acc = Vec(), acc = Vec(),
@ -144,22 +179,28 @@ function Player:spawnPlayer()
}, },
friction = 0.998, friction = 0.998,
speed = 800, speed = 800,
bolt_count = 10,
bolt_speed = 2000, bolt_speed = 2000,
bolt_cooldown = 0.2, bolts = {},
bolt_friction = 0.98, bolt_cooldown = 0.1,
collider = {-5, -5, 4, 8} collider = {-5, -5, 4, 8}
} }
end
function Player:draw() for _=1, 10 do
for _, e in ipairs(self.pool.groups.player.entities) do local bolt = self.pool:queue{
if e.aim_dir.x ~= 0 or e.aim_dir.y ~= 0 then pos = Vec(100, 100),
love.graphics.setColor(0.8, 0.2, 0.2) vel = Vec(),
local aim_pos = e.pos + e.aim_dir * 15 acc = Vec(),
love.graphics.circle("fill", aim_pos.x, aim_pos.y, 5) sprite = {
end name = "bolt",
variant = "idle",
},
friction = 0.98,
max_speed = 400,
}
self:storeBolt(player, bolt)
end end
return player
end end
return Player return Player

View File

@ -1,94 +1,30 @@
local data = require("data")
local pprint = require("lib.pprint") local pprint = require("lib.pprint")
local aseLoader = require("lib.ase-loader")
local Sprite = {} local Sprite = {}
-- TODO: Maybe add a texture atlas library for packing frame data -- TODO: Maybe add a texture atlas library for packing frame data
-- TODO: For production maybe use another type of loader? (https://github.com/elloramir/packer, https://github.com/EngineerSmith/Runtime-TextureAtlas) -- TODO: For production maybe use another type of loader? (https://github.com/elloramir/packer, https://github.com/EngineerSmith/Runtime-TextureAtlas)
local function loadAsepriteSprite(path)
local sprite = {}
local ase = aseLoader(path)
sprite.width = ase.header.width
sprite.height = ase.header.height
sprite.variants = {}
local FRAME_DATA_CHUNK = 0x2005
local FRAME_TAG_CHUNK = 0x2018
local frames = {}
local tags = {}
local default_variant
for _, frame in ipairs(ase.header.frames) do
for _, chunk in ipairs(frame.chunks) do
if chunk.type == FRAME_DATA_CHUNK then
local cel = chunk.data
local buffer = love.data.decompress("data", "zlib", cel.data)
local data = love.image.newImageData(cel.width, cel.height, "rgba8", buffer)
local image = love.graphics.newImage(data)
local canvas = love.graphics.newCanvas(sprite.width, sprite.height)
canvas:setFilter("nearest")
-- you need to draw in a canvas before.
-- frame images can be of different sizes
-- but never bigger than the header width and height
love.graphics.setCanvas(canvas)
love.graphics.draw(image, cel.x, cel.y)
love.graphics.setCanvas()
table.insert(frames, {
image = canvas,
duration = frame.frame_duration / 1000
})
elseif chunk.type == FRAME_TAG_CHUNK then
for i, tag in ipairs(chunk.data.tags) do
-- first tag as default
if i == 1 then
default_variant = tag.name
end
-- aseprite use 0 notation to begin
-- but in lua, everthing starts in 1
tag.to = tag.to + 1
tag.from = tag.from + 1
tag.frames = tag.to - tag.from
tags[tag.name] = tag
end
end
end
end
for _, tag in pairs(tags) do
local variant = {}
sprite.variants[tag.name] = variant
for i=tag.from,tag.to do
table.insert(variant, frames[i])
end
end
if not sprite.variants.default then
sprite.variants.default = {frames[0]}
end
return sprite
end
local all_sprites = {}
all_sprites.player = loadAsepriteSprite("data/sprites/player.aseprite")
function Sprite:addToWorld(group, e) function Sprite:addToWorld(group, e)
if group ~= "sprite" then return end if group ~= "sprite" then return end
local sprite = data.sprites[e.sprite.variant]
assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name))
end end
function Sprite:update(dt) function Sprite:update(dt)
for _, e in ipairs(self.pool.groups.sprite.entities) do for _, e in ipairs(self.pool.groups.sprite.entities) do
if e.sprite.name and all_sprites[e.sprite.name] then local sprite = data.sprites[e.sprite.name]
local spritesheet = all_sprites[e.sprite.name] if sprite then
local variant = spritesheet.variants[e.sprite.variant or "default"] local variant = sprite.variants[e.sprite.variant or "default"]
e.sprite.timer = (e.sprite.timer or 0) + dt e.sprite.timer = (e.sprite.timer or 0) + dt
if variant then if variant then
if e.sprite.timer > 0.1 then local frame = variant[e.sprite.frame]
if not frame then
frame = variant[1]
end
if e.sprite.timer > frame.duration then
e.sprite.timer = e.sprite.timer % 0.1 e.sprite.timer = e.sprite.timer % 0.1
e.sprite.frame = (e.sprite.frame or 1) % #variant + 1 e.sprite.frame = (e.sprite.frame or 1) % #variant + 1
end end
@ -100,19 +36,28 @@ end
function Sprite:draw() function Sprite:draw()
love.graphics.setColor(1, 1, 1) love.graphics.setColor(1, 1, 1)
for _, e in ipairs(self.pool.groups.sprite.entities) do for _, e in ipairs(self.pool.groups.sprite.entities) do
local spritesheet = all_sprites[e.sprite.name] local sprite = data.sprites[e.sprite.name]
if spritesheet then assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name))
if e.sprite.variant ~= e.sprite.last_variant then
e.sprite.frame = 1 if not e.hidden then
end local variant = sprite.variants[e.sprite.variant or "default"]
local variant = spritesheet.variants[e.sprite.variant or "default"] if variant and e.sprite.frame then
if variant then
local frame = variant[e.sprite.frame] local frame = variant[e.sprite.frame]
love.graphics.draw(frame.image, e.pos.x-spritesheet.width/2, e.pos.y-spritesheet.height/2) if not frame then
frame = variant[1]
end
local sx = 1
if e.sprite.flip then
sx = -1
end
love.graphics.draw(
frame.image,
e.pos.x,
e.pos.y,
0, sx, 1,
sprite.width/2, sprite.height/2
)
end end
e.sprite.last_variant = e.sprite.variant
else
love.graphics.circle("fill", e.pos.x, e.pos.y, 10)
end end
end end
end end