diff --git a/assets/shaders/shadow.glsl b/assets/shaders/shadow.glsl deleted file mode 100644 index 4daadb2..0000000 --- a/assets/shaders/shadow.glsl +++ /dev/null @@ -1,25 +0,0 @@ -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/lib/camera.lua b/lib/camera.lua index b5dcb4a..bfbcfc9 100644 --- a/lib/camera.lua +++ b/lib/camera.lua @@ -1,8 +1,13 @@ -require 'lib.entity' - -__Camera = { - pivotPosition = Vec3 {}, +Camera = { + target = Vec3 {}, position = Vec3 {}, - velocity = Vec3 {}, - fixed = false, + lerp_speed = 5.0 } + +function Camera:update(dt) + if not self.target then return end + + -- Плавное движение камеры к цели + local to_target = self.target - self.position + self.position = self.position + to_target:scale(dt * self.lerp_speed) +end diff --git a/lib/entity.lua b/lib/entity.lua index 3e45272..bc2aae2 100644 --- a/lib/entity.lua +++ b/lib/entity.lua @@ -4,12 +4,11 @@ require 'lib.vec3' __Entity = { id = nil, sprite = __AnimatedSprite, - position = Vec3 {}, + position = Vec3 {}, -- world position velocity = Vec3 {}, rotation = 0, -- clockwise radians friction = 0.98, speed = 3, -- m/s - rotation_speed = 1, --rad/sec state = "walk" } @@ -30,8 +29,14 @@ end function __Entity:processMovement() self.position = self.position + self.velocity - self.velocity = self.velocity * self.friction - self.rotation = self.velocity:direction() + + --self.velocity = self.velocity * self.friction + if self.velocity:length() > self.speed then + self.velocity = self.velocity:normalize() * self.speed + end + if (self.velocity:length() ~= 0) then + self.rotation = self.velocity:direction() + end end function __Entity:lookAt(vec) diff --git a/lib/light_source.lua b/lib/light_source.lua new file mode 100644 index 0000000..1e38dfe --- /dev/null +++ b/lib/light_source.lua @@ -0,0 +1,14 @@ +__LightSource = { + position = Vec3 {}, + power = 0, -- in meters + color = Vec3 {} --r, g, b +} + +function LightSource(position, power, color) + local l = { + position = position, + power = power, + color = color + } + return setmetatable(l, { __index = __LightSource }) +end diff --git a/lib/math2.lua b/lib/math2.lua index 3ad18f7..d3e858b 100644 --- a/lib/math2.lua +++ b/lib/math2.lua @@ -20,3 +20,8 @@ math.named_direction = function(rotation) return lookup[get_direction_index(rotation)] end + +-- Округляет вниз n до самого большого k * step, большего или равного n +math.step_floor = function(n, step) + return math.floor(n / step) * step +end diff --git a/lib/player.lua b/lib/player.lua index 3158a93..21cc312 100644 --- a/lib/player.lua +++ b/lib/player.lua @@ -25,10 +25,8 @@ function __Player:update(dt) acc = acc + Vec3 { 0, 1 } end - self.entity.velocity = self.entity.velocity - + ((acc:normalize() or Vec3 {}) - * self.entity.speed - * dt) + self.entity.velocity = ((acc:normalize() or Vec3 {}) + * self.entity.speed * dt) self.entity:update(dt) diff --git a/main.lua b/main.lua index d1583d9..93a749e 100644 --- a/main.lua +++ b/main.lua @@ -1,77 +1,105 @@ require "lib.asset_bundle" require "lib.player" require "lib.math2" +require "lib.light_source" +require "lib.camera" -PIXEL_PER_METER = 48 +PIXELS_PER_METER = 48 P = nil +Entities = {} +Lights = { + LightSource(Vec3 { 1, 1 }, 5), + -- LightSource(Vec3 { 300, 200 }, 5), + -- LightSource(Vec3 { 400, 200 }, 5) +} + +function love.conf(t) + t.console = true +end + function love.load() AssetBundle:load() love.window.setMode(1080, 720, { resizable = true, msaa = 4 }) P = Player('fox') - P.entity.position = Vec3 { 200, 200 } + P.entity.position = Vec3 { 7, 7 } + Entities.player = P.entity + + Camera.position = Vec3 { 0, 0 } print('ready') end function love.update(dt) P:update(dt) + + Camera.target = P.entity.position + Camera:update(dt) end +-- Преобразует вектор мировых координат (в метрах) в вектор экранных координат (в пикселях) +local function worldToScreen(worldPos) + return (worldPos - Camera.position) * PIXELS_PER_METER +end + + +local 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 = entity:spriteFromAngle(shadow_vec:direction()) + + love.graphics.draw(tex, quad, + worldToScreen(entity.position).x, + worldToScreen(entity.position).y - 10, + math.step_floor(shadow_vec:direction() - math.pi / 2, math.pi / 4), + 1, math.sin(20), tex:getHeight() / 2, tex:getHeight()) + + love.graphics.setColor(1, 1, 1, 1) +end + + function love.draw() love.graphics.clear(1, 1, 1) local spriteCanvas = love.graphics.newCanvas() + local width, height = spriteCanvas:getDimensions() 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.push() + love.graphics.translate(width / 2, height / 2) -- теперь экранный ноль координат будет в центре экрана + for _, e in pairs(Entities) do + for _, l in pairs(Lights) do + drawShadow(e, l.position, l.power) + end + + local tex, quad = e:spriteFromAngle(math.pi / 2) + love.graphics.draw(tex, quad, + worldToScreen(e.position).x, + worldToScreen(e.position).y, + 0, + 1, 1, tex:getHeight() / 2, tex:getHeight()) + end + 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.push() + love.graphics.translate(worldToScreen(Lights[1].position).x, worldToScreen(Lights[1].position).y) + love.graphics.circle("fill", 0, 0, 0.1 * PIXELS_PER_METER) + love.graphics.pop() + love.graphics.pop() 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); + local scale = math.min(love.graphics.getDimensions()) / (15 * PIXELS_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