- added world coordinates

- added camera following some target world position
This commit is contained in:
Ivan Yuriev 2025-04-18 22:28:21 +03:00
parent 6a8bc1e07c
commit 7aefcd4331
7 changed files with 109 additions and 79 deletions

View File

@ -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); // альфа настраивается
}

View File

@ -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

View File

@ -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)

14
lib/light_source.lua Normal file
View File

@ -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

View File

@ -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

View File

@ -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)

108
main.lua
View File

@ -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