From afeb21978632720be2e4b73b4505a8e3fda1f634 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Fri, 5 Aug 2022 15:38:20 +0000 Subject: [PATCH] add animated bolts --- src/data/init.lua | 85 +++++++++++++++++++++- src/data/sprites/bolt.aseprite | Bin 0 -> 857 bytes src/data/sprites/player.aseprite | Bin 1297 -> 1328 bytes src/states/main.lua | 8 +-- src/systems/debug.lua | 2 +- src/systems/physics.lua | 3 + src/systems/player.lua | 107 ++++++++++++++++++--------- src/systems/sprite.lua | 119 +++++++++---------------------- 8 files changed, 198 insertions(+), 126 deletions(-) create mode 100644 src/data/sprites/bolt.aseprite diff --git a/src/data/init.lua b/src/data/init.lua index b992120..eac5d38 100644 --- a/src/data/init.lua +++ b/src/data/init.lua @@ -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, + } +} diff --git a/src/data/sprites/bolt.aseprite b/src/data/sprites/bolt.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..ac62429d2ef8b46fba305c9f0051717a5d620f12 GIT binary patch literal 857 zcmcJNO-K}B7{{M&-SM)BC)vXEwNDR)>ryMyu3FH7EtL#mXx3_@!c4Lvh=^iD1%X&g z8AWYSlpTWVT6FN@MFjmtkag)9=EpnleEfgUyl;zF_zfRqS-@k?7ozG3snGC|3n@JeAtDqU*0Yi~mSYO`{gY6gL zspKNG(i$daKEc=Ten3r$q@TTjm(O2;w&TE-W)lt^8ih|oAvk~1f^Uak!PM;|aI`n5 z9U3Tb|GQ#96Lf$+?ucWJGQP-SiYAT-Vuu=DNMVGI>!LLlD3DJ$$;6ULAZbLEx=D)V zUM&>JKH6a?8yE!?OYx^F5WBOd%OiP@7}8<&+XG!D@A=g7%X;-0cTWx8v)UVcvG6T@ zz1mxJ;d#;hy3Wl0SgbOTE^BjUmx>>iq@$M_qfdVA+#PAWT4lcR%JjjRrL%l2oAKoK zg}WR6mQXV_uD4r8shr+gH!}V4+}K!CFdTh3_wY+aY;Snj7#}i!#*Yo&*pir-D3sP~ Xz7vVOgyURq)PtUte{H~jrghJ63sBXQ literal 0 HcmV?d00001 diff --git a/src/data/sprites/player.aseprite b/src/data/sprites/player.aseprite index 70456de778fbe73507b35d9fbebd2486d2f05671..5c3a681089de4c9f5e796696a61e391b50056113 100644 GIT binary patch delta 91 zcmbQpwSkM#U?O8ZZyOT>!>^C*3@Ho@4D1_wV;SY;fh-mUW*`Xz|A7p42A{;r)FK7L e$*&j_7#SyfGi?Hrg)9pu&tnz?vbHkshVTJ%mlXE^ delta 67 zcmdnMHIa)^a3W(pZ#okL!>^BQ3@Ho@3~U>FV;LuNG9@rFOwMB31SBW0ESS8PS&Wr| SfsuiAvLbWxWNVfXh&TX3R}k6& diff --git a/src/states/main.lua b/src/states/main.lua index 97d72d3..f7f81c0 100644 --- a/src/states/main.lua +++ b/src/states/main.lua @@ -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"}} } diff --git a/src/systems/debug.lua b/src/systems/debug.lua index 7952057..b41d1d0 100644 --- a/src/systems/debug.lua +++ b/src/systems/debug.lua @@ -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() diff --git a/src/systems/physics.lua b/src/systems/physics.lua index a3cb31c..8426cef 100644 --- a/src/systems/physics.lua +++ b/src/systems/physics.lua @@ -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 diff --git a/src/systems/player.lua b/src/systems/player.lua index d3e520d..672a772 100644 --- a/src/systems/player.lua +++ b/src/systems/player.lua @@ -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 diff --git a/src/systems/sprite.lua b/src/systems/sprite.lua index 2791e7c..9aecfe6 100644 --- a/src/systems/sprite.lua +++ b/src/systems/sprite.lua @@ -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