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 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 = {
physical = {filter = {"pos", "vel"}},
player = {filter = {
"pos", "acc", "speed",
"bolt_count",
"bolt_cooldown", "bolt_speed", "bolt_friction"
"pos", "acc", "speed", "bolts"
-- "bolt_count",
-- "bolt_cooldown", "bolt_speed", "bolt_friction"
}},
controllable_player = {filter = {"controllable"}},
sprite = {filter = {"sprite"}},
sprite = {filter = {"pos", "sprite"}},
bolt = {filter={"pos", "vel", "bolt"}},
collider = {filter={"pos", "collider"}}
}

View File

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

View File

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

View File

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

View File

@ -1,94 +1,30 @@
local data = require("data")
local pprint = require("lib.pprint")
local aseLoader = require("lib.ase-loader")
local Sprite = {}
-- 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)
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)
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
function Sprite:update(dt)
for _, e in ipairs(self.pool.groups.sprite.entities) do
if e.sprite.name and all_sprites[e.sprite.name] then
local spritesheet = all_sprites[e.sprite.name]
local variant = spritesheet.variants[e.sprite.variant or "default"]
local sprite = data.sprites[e.sprite.name]
if sprite then
local variant = sprite.variants[e.sprite.variant or "default"]
e.sprite.timer = (e.sprite.timer or 0) + dt
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.frame = (e.sprite.frame or 1) % #variant + 1
end
@ -100,19 +36,28 @@ end
function Sprite:draw()
love.graphics.setColor(1, 1, 1)
for _, e in ipairs(self.pool.groups.sprite.entities) do
local spritesheet = all_sprites[e.sprite.name]
if spritesheet then
if e.sprite.variant ~= e.sprite.last_variant then
e.sprite.frame = 1
end
local variant = spritesheet.variants[e.sprite.variant or "default"]
if variant then
local sprite = data.sprites[e.sprite.name]
assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name))
if not e.hidden then
local variant = sprite.variants[e.sprite.variant or "default"]
if variant and e.sprite.frame then
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
e.sprite.last_variant = e.sprite.variant
else
love.graphics.circle("fill", e.pos.x, e.pos.y, 10)
end
end
end