diff --git a/.vscode/settings.json b/.vscode/settings.json index af27022..09e40f8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,8 @@ "Lua.runtime.special": { "love.filesystem.load": "loadfile" }, - "Lua.workspace.checkThirdParty": false + "Lua.workspace.checkThirdParty": false, + "Lua.diagnostics.disable": [ + "undefined-field" + ] } \ No newline at end of file diff --git a/assets/shaders/shadow.glsl b/assets/shaders/shadow.glsl new file mode 100644 index 0000000..4daadb2 --- /dev/null +++ b/assets/shaders/shadow.glsl @@ -0,0 +1,25 @@ +extern vec2 light_dir; // направление света, нормализованное +extern float flatten; // насколько тень сплющена (0.2 - норм) +extern float softness; // степень мягкости по краям + +vec4 effect(vec4 color, Image tex, vec2 tex_coords, vec2 screen_coords) +{ + // Получаем оригинальный цвет спрайта + vec4 tex_color = Texel(tex, tex_coords); + + // Преобразуем цвет в "тень" + float alpha = tex_color.a; + vec3 shadow_color = vec3(0.0); // черная тень + + // Модифицируем UV, чтобы проецировать вниз и вбок + vec2 offset = tex_coords; + offset.y -= tex_coords.y * flatten; // сплющиваем вниз + offset.x += tex_coords.y * flatten * light_dir.x; // сдвигаем в сторону + + vec4 proj = Texel(tex, offset); + + // Мягкость края тени (можно отключить, если не нужно) + float edge_fade = smoothstep(0.0, softness, proj.a); + + return vec4(shadow_color, proj.a * edge_fade * 0.6); // альфа настраивается +} diff --git a/assets/sprites/fox/walk_e.png b/assets/sprites/fox/walk_e.png index e3470a8..ef34a8c 100644 Binary files a/assets/sprites/fox/walk_e.png and b/assets/sprites/fox/walk_e.png differ diff --git a/assets/sprites/fox/walk_n.png b/assets/sprites/fox/walk_n.png index e60f6eb..a0c99e4 100644 Binary files a/assets/sprites/fox/walk_n.png and b/assets/sprites/fox/walk_n.png differ diff --git a/assets/sprites/fox/walk_ne.png b/assets/sprites/fox/walk_ne.png index 74f0987..b50e75a 100644 Binary files a/assets/sprites/fox/walk_ne.png and b/assets/sprites/fox/walk_ne.png differ diff --git a/assets/sprites/fox/walk_nw.png b/assets/sprites/fox/walk_nw.png index 448bc53..3c9c9a2 100644 Binary files a/assets/sprites/fox/walk_nw.png and b/assets/sprites/fox/walk_nw.png differ diff --git a/assets/sprites/fox/walk_s.png b/assets/sprites/fox/walk_s.png index 7b83e61..72b2873 100644 Binary files a/assets/sprites/fox/walk_s.png and b/assets/sprites/fox/walk_s.png differ diff --git a/assets/sprites/fox/walk_se.png b/assets/sprites/fox/walk_se.png index 9c0e6a2..44a01a2 100644 Binary files a/assets/sprites/fox/walk_se.png and b/assets/sprites/fox/walk_se.png differ diff --git a/assets/sprites/fox/walk_sw.png b/assets/sprites/fox/walk_sw.png index 5b8fbc0..4c46d6c 100644 Binary files a/assets/sprites/fox/walk_sw.png and b/assets/sprites/fox/walk_sw.png differ diff --git a/assets/sprites/fox/walk_w.png b/assets/sprites/fox/walk_w.png index 50ecfd6..5e19bf3 100644 Binary files a/assets/sprites/fox/walk_w.png and b/assets/sprites/fox/walk_w.png differ diff --git a/lib/animation.lua b/lib/animation.lua index 165af67..536829a 100644 --- a/lib/animation.lua +++ b/lib/animation.lua @@ -2,7 +2,6 @@ local __Animation = { spriteSheet = nil, quads = nil, fps = 12, - currentTime = 0 } function Animation(image, width, height) @@ -21,16 +20,8 @@ function Animation(image, width, height) return setmetatable(animation, { __index = __Animation }) end -function __Animation:getQuad() - local frametime = 1 / self.fps - local frame = math.floor(self.currentTime / frametime) +function __Animation:getQuad(t) + local duration = #self.quads / self.fps + local frame = math.floor(t * duration * #self.quads) return self.quads[frame + 1] end - -function __Animation:update(dt) - self.currentTime = (self.currentTime + dt) % (#self.quads / self.fps) -end - -function __Animation:reset() - self.currentTime = 0 -end diff --git a/lib/asset_bundle.lua b/lib/asset_bundle.lua index 09991f7..867a24f 100644 --- a/lib/asset_bundle.lua +++ b/lib/asset_bundle.lua @@ -43,7 +43,10 @@ function AssetBundle.loadFile(path) img:setFilter("nearest", "nearest") return Some(img) + elseif (ext == "glsl") then + return Some(love.graphics.newShader(path)); end + return None end diff --git a/lib/entity.lua b/lib/entity.lua index 72f35a8..3e45272 100644 --- a/lib/entity.lua +++ b/lib/entity.lua @@ -6,10 +6,11 @@ __Entity = { sprite = __AnimatedSprite, position = Vec3 {}, velocity = Vec3 {}, - rotation = 0, -- clockwise radians + rotation = 0, -- clockwise radians friction = 0.98, - speed = 3, -- m/s - rotation_speed = 1 --rad/sec + speed = 3, -- m/s + rotation_speed = 1, --rad/sec + state = "walk" } function Entity(id) @@ -23,8 +24,6 @@ end function __Entity:update(dt) self:processMovement() if self.sprite then - local dir = self:namedDirection() - self.sprite.playing = "walk_" .. dir self.sprite:update(dt) end end @@ -35,29 +34,12 @@ function __Entity:processMovement() self.rotation = self.velocity:direction() end -function __Entity:namedDirection() - local get_direction_index = function(rotation) - local pi = math.pi - rotation = rotation % (2 * pi) - local shifted = (rotation + pi / 8) % (2 * pi) - local index = math.floor(shifted / (pi / 4)) + 1 - return index - end - - local lookup = { - "e", - "ne", - "n", - "nw", - "w", - "sw", - "s", - "se", - } - - return lookup[get_direction_index(self.rotation)] -end - function __Entity:lookAt(vec) - self.rotation = (vec - self.position):direction() + self.rotation = self.position:angle_to(vec) +end + +function __Entity:spriteFromAngle(angle) + local dir = math.named_direction(angle - self.rotation) + local key = self.state .. "_" .. dir + return self.sprite:getTexture(key), self.sprite:getQuad(key) end diff --git a/lib/math2.lua b/lib/math2.lua new file mode 100644 index 0000000..3ad18f7 --- /dev/null +++ b/lib/math2.lua @@ -0,0 +1,22 @@ +math.named_direction = function(rotation) + local get_direction_index = function(rotation) + local pi = math.pi + rotation = rotation % (2 * pi) + local shifted = (rotation + pi / 8) % (2 * pi) + local index = math.floor(shifted / (pi / 4)) + 1 + return index + end + + local lookup = { + "s", + "se", + "e", + "ne", + "n", + "nw", + "w", + "sw", + } + + return lookup[get_direction_index(rotation)] +end diff --git a/lib/sprite.lua b/lib/sprite.lua index 4315c8c..35ab829 100644 --- a/lib/sprite.lua +++ b/lib/sprite.lua @@ -2,34 +2,34 @@ require 'lib.asset_bundle' require 'lib.animation' -__AnimatedSprite = {} +__AnimatedSprite = { + t = 0, +} function AnimatedSprite(id) local table = {} local bundle = AssetBundle.files.sprites[id] for key, value in pairs(bundle) do - table[key] = Animation(value, 96, 96) + table[key] = Animation(value, value:getHeight(), value:getHeight()) end return setmetatable(table, { __index = __AnimatedSprite }) end function __AnimatedSprite:update(dt) - if self.playing then - self[self.playing]:update(dt) - end + self.t = (self.t + dt) % 1 end -function __AnimatedSprite:getQuad() - if self.playing then - return self[self.playing]:getQuad() +function __AnimatedSprite:getQuad(key) + if key then + return self[key]:getQuad(self.t) end return love.graphics.newQuad(0, 0, 235, 235, AssetBundle.files.sprites.fallback) end -function __AnimatedSprite:getTexture() - if self.playing then - return self[self.playing].spriteSheet +function __AnimatedSprite:getTexture(key) + if key then + return self[key].spriteSheet end return AssetBundle.files.sprites.fallback end diff --git a/lib/vec3.lua b/lib/vec3.lua index 5fd2f29..ce8c499 100644 --- a/lib/vec3.lua +++ b/lib/vec3.lua @@ -52,7 +52,7 @@ function __Vec3:normalize() end function __Vec3:direction() - return -math.atan2(self.y, self.x) + return math.atan2(self.y, self.x) end function __Vec3:dot(other) @@ -62,3 +62,7 @@ end function __Vec3:__tostring() return "Vec3{" .. self.x .. ", " .. self.y .. ", " .. self.z .. "}" end + +function __Vec3:angle_to(other) + return (other - self):direction() +end diff --git a/main.lua b/main.lua index cc937d6..d1583d9 100644 --- a/main.lua +++ b/main.lua @@ -1,12 +1,18 @@ require "lib.asset_bundle" require "lib.player" +require "lib.math2" +PIXEL_PER_METER = 48 P = nil function love.load() AssetBundle:load() + love.window.setMode(1080, 720, { resizable = true, msaa = 4 }) + P = Player('fox') + P.entity.position = Vec3 { 200, 200 } + print('ready') end function love.update(dt) @@ -14,11 +20,58 @@ function love.update(dt) end function love.draw() - love.graphics.draw(P.entity.sprite:getTexture(), P.entity.sprite:getQuad(), P.entity.position.x, P.entity.position.y, - P.entity.direction, - 2, 2, 48, 48) + love.graphics.clear(1, 1, 1) + local spriteCanvas = love.graphics.newCanvas() + spriteCanvas:setFilter("linear", "linear") + + love.graphics.setCanvas(spriteCanvas) + love.graphics.clear(1, 1, 1) + + drawShadow(P.entity, Vec3 { 200, 200 }, 200) + drawShadow(P.entity, Vec3 { 300, 200 }, 200) + drawShadow(P.entity, Vec3 { 400, 200 }, 200) + + love.graphics.setColor(1, 0, 0, 1) + love.graphics.circle("fill", 200, 200, 10) + love.graphics.circle("fill", 300, 200, 10) + love.graphics.circle("fill", 400, 200, 10) + love.graphics.setColor(1, 1, 1, 1) + + local tex, quad = P.entity:spriteFromAngle(math.pi / 2) + love.graphics.draw(tex, quad, P.entity.position.x, P.entity.position.y, + 0, + 1, 1, tex:getHeight() / 2, tex:getHeight()) + + love.graphics.setCanvas() + + local scale = math.min(love.graphics.getDimensions()) / (15 * PIXEL_PER_METER); + love.graphics.draw(spriteCanvas, 0, 0, 0, scale, scale) + + + love.graphics.setColor(1, 1, 1, 1) end function love.conf(t) t.console = true end + +function drawShadow(entity, light, radius) + local shadow_vec = light - entity.position + local dist = shadow_vec:length() + if dist > radius then return end + + love.graphics.setColor(0, 0, 0, math.min(0.5, 1 - (dist / radius))) + local tex, quad = P.entity:spriteFromAngle(shadow_vec:direction()) + love.graphics.draw(tex, quad, P.entity.position.x, P.entity.position.y - 10, + shadow_vec:direction() - math.pi / 2, + 1, math.sin(20), tex:getHeight() / 2, tex:getHeight()) + + -- love.graphics.push() + -- love.graphics.setColor(0, 0, 0, 0.5) + -- love.graphics.translate(entity.position.x, entity.position.y) + -- love.graphics.scale(1, 0.342) + -- love.graphics.circle("fill", 0, 0, 20) + -- love.graphics.pop() + + love.graphics.setColor(1, 1, 1, 1) -- сброс цвета +end