add animated bolts
This commit is contained in:
parent
a445461075
commit
afeb219786
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BIN
src/data/sprites/bolt.aseprite
Normal file
BIN
src/data/sprites/bolt.aseprite
Normal file
Binary file not shown.
Binary file not shown.
@ -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"}}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user