diff options
| author | cursed22bc <admin@pixeldawn.org> | 2026-03-01 12:30:05 +0200 |
|---|---|---|
| committer | cursed22bc <admin@pixeldawn.org> | 2026-03-01 12:30:05 +0200 |
| commit | de94226e1b302cee2a006f78f0153aa5fa081f47 (patch) | |
| tree | 31859ec7de767dafe5e7474e14f74f473eab6ba8 /player.lua | |
| parent | 8f9182baff36d33c07c5eb8df795e3cfcd5307a5 (diff) | |
player and animation
Diffstat (limited to 'player.lua')
| -rw-r--r-- | player.lua | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/player.lua b/player.lua new file mode 100644 index 0000000..4e39f21 --- /dev/null +++ b/player.lua @@ -0,0 +1,181 @@ +local Entity = require("entity") +local Animation = require("animation") + +local Player = {} +Player.__index = Player +setmetatable(Player, { __index = Entity }) + +local MOVE_SPEED = 70 +local JUMP_FORCE = -200 +local GROUND_LAYER = "ground" + +function Player.new(world, spawnX, spawnY) + local w, h = 16, 16 + local pw, ph = 8, 8 -- physics body size + local self = setmetatable(Entity:new(spawnX or 0, spawnY or 0, w, h, pw, ph), Player) + + self.directionState = { 'side', 'up', 'down', 'side_up', 'side_down' } + self.directionIndex = 1 + self.direction = self.directionState[self.directionIndex] + + self.availableJumps = 0 + self:enablePhysics(world, "dynamic") + + self.animations = { + idle = Animation.new("assets/player/idle.png", 16, 16, 0.6), + running = Animation.new("assets/player/running.png", 16, 16, 1), + going_up = Animation.new("assets/player/going_up.png", 16, 16, 0.8), + going_down = Animation.new("assets/player/going_down.png", 16, 16, 0.5), + stop_running = Animation.new("assets/player/stop_running.png", 16, 16, 0.3), + ground_hit = Animation.new("assets/player/ground_hit.png", 16, 16, 0.3) + } + if love and love.filesystem and love.filesystem.getDirectoryItems then + local ok, items = pcall(love.filesystem.getDirectoryItems, "assets/player") + if ok and items then + for _, filename in ipairs(items) do + if filename:match("%.png$") then + local name = filename:gsub("%.png$", "") + if not self.animations[name] then + self.animations[name] = Animation.new("assets/player/" .. filename, 16, 16, 0.3) + end + end + end + end + end + + self.animations.ground_hit.looping = false + self.animations.stop_running.looping = false + + self.currentAnim = self.animations.idle + + self.lastFacing = 1 + + self.state = "idle" + self.wasOnFloor = false + + self.vx = 0 + self.vy = 0 + + self.grounded = false + return self +end + +function Player:trackDirectionByKeyPressed() + local left = love.keyboard.isDown("left", "a") + local right = love.keyboard.isDown("right", "d") + local up = love.keyboard.isDown("up", "w") + local down = love.keyboard.isDown("down", "s") + + if up and left then + self.directionIndex = 4 + elseif up and right then + self.directionIndex = 4 + elseif down and left then + self.directionIndex = 5 + elseif down and right then + self.directionIndex = 5 + + elseif up then + self.directionIndex = 2 + elseif down then + self.directionIndex = 3 + elseif left then + self.directionIndex = 1 + elseif right then + self.directionIndex = 1 + end + + self.direction = self.directionState[self.directionIndex] +end + +function Player:update(dt) + self:syncFromPhysicsBody() + self:trackDirectionByKeyPressed() + + local vx, vy = self.body:getLinearVelocity() + self.vx = vx + self.vy = vy + + local c = self.contact or { floor = 0, wall = 0, ceiling = 0 } + local onFloor = c.floor == 1 + local prevOnFloor = self.wasOnFloor + local prevState = self.state + + local move = 0 + if love.keyboard.isDown("left", "a") then move = move - 1 end + if love.keyboard.isDown("right", "d") then move = move + 1 end + + if onFloor then + self.availableJumps = 1 + if move ~= 0 then + self.lastFacing = move + self.body:setLinearVelocity(move * MOVE_SPEED, vy) + else + self.body:setLinearVelocity(0, vy) + end + else + if move ~= 0 then self.lastFacing = move end + self.body:setLinearVelocity(move * MOVE_SPEED * 0.7, vy) + end + + local desiredState + if onFloor then + desiredState = (move ~= 0) and "running" or "idle" + else + desiredState = (vy < 0) and "jumping" or "falling" + end + + if self.state == "ground_hit" or self.state == "stop_running" then + if not onFloor or self.currentAnim:isFinished() then + self.state = desiredState + end + else + local wasAirborne = prevState == "jumping" or prevState == "falling" + if onFloor and not prevOnFloor and wasAirborne then + self.state = "ground_hit" + self.animations.ground_hit:reset() + elseif onFloor and prevState == "running" and move == 0 then + self.state = "stop_running" + self.animations.stop_running:reset() + else + self.state = desiredState + end + end + + self.wasOnFloor = onFloor + + local animMap = { + idle = self.animations.idle, + running = self.animations.running, + jumping = self.animations.going_up, + falling = self.animations.going_down, + stop_running = self.animations.stop_running, + ground_hit = self.animations.ground_hit, + } + self.currentAnim = animMap[self.state] or self.animations.idle + self.currentAnim:update(dt) +end + +function Player:jump() + local c = self.contact or { floor = 0, wall = 0, ceiling = 0 } + local canJump = (c.floor == 1 or c.wall == 1) and (self.availableJumps or 0) > 0 + if canJump then + self.body:setLinearVelocity(self.body:getLinearVelocity(), JUMP_FORCE) + self.state = "jumping" + self.animations.going_up:reset() + self.availableJumps = self.availableJumps - 1 + return true + end + return false +end + +function Player:draw() + local flip = (self.lastFacing < 0) + self.currentAnim:draw(self.x, self.y, flip) +end + +function Player:getBody() + return self.body +end + +return Player |
