refactor: replace AnimationNode with Task system (tweens/async)
This commit is contained in:
parent
0017b6e104
commit
4277c6c310
47
assets/shaders/divine_ripple.glsl
Normal file
47
assets/shaders/divine_ripple.glsl
Normal file
@ -0,0 +1,47 @@
|
||||
extern vec2 center;
|
||||
extern number time;
|
||||
extern number intensity;
|
||||
extern vec2 screenSize;
|
||||
|
||||
// Hexagon grid logic
|
||||
// Returns distance to nearest hex center
|
||||
float hexDist(vec2 p) {
|
||||
p = abs(p);
|
||||
float c = dot(p, normalize(vec2(1.0, 1.73)));
|
||||
c = max(c, p.x);
|
||||
return c;
|
||||
}
|
||||
|
||||
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
|
||||
// Normalize coordinates to -1..1, correcting for aspect ratio
|
||||
vec2 aspect = vec2(screenSize.x / screenSize.y, 1.0);
|
||||
vec2 uv = (screen_coords / screenSize.y) - (center / screenSize.y);
|
||||
|
||||
// Wave parameters
|
||||
float dist = length(uv);
|
||||
float speed = 5.0;
|
||||
float waveWidth = 0.1;
|
||||
float decay = 1.0 - smoothstep(0.0, 1.5, dist); // Decay over distance
|
||||
|
||||
// Calculate wave pulse
|
||||
float wavePhase = time * speed;
|
||||
float pulse = smoothstep(waveWidth, 0.0, abs(dist - mod(wavePhase, 2.0)));
|
||||
|
||||
// Hex grid pattern (visual only)
|
||||
vec2 hexUV = screen_coords * 0.05; // Scale grid
|
||||
// Basic hex grid approximation
|
||||
vec2 q = vec2(hexUV.x * 2.0/3.0, hexUV.y);
|
||||
|
||||
// Distortion
|
||||
vec2 distort = normalize(uv) * pulse * intensity * 0.02 * decay;
|
||||
vec2 finalUV = texture_coords - distort;
|
||||
|
||||
// Sample texture with distortion
|
||||
vec4 texColor = Texel(texture, finalUV);
|
||||
|
||||
// Add divine glow at the wavefront
|
||||
vec4 glowColor = vec4(1.0, 0.8, 0.4, 1.0); // Gold/Kitsune-fire color
|
||||
texColor += glowColor * pulse * decay * 0.5;
|
||||
|
||||
return texColor * color;
|
||||
}
|
||||
@ -1,103 +0,0 @@
|
||||
local easing = require "lib.utils.easing"
|
||||
|
||||
--- @alias animationRunner fun(node: AnimationNode)
|
||||
|
||||
--- Узел дерева анимаций.
|
||||
---
|
||||
--- Отслеживает завершение всех анимаций всех дочерних узлов и оповещает вышестоящий узел.
|
||||
---
|
||||
--- Дочерние узлы одного уровня запускают свою анимацию одновременно после завершения анимации родителя.
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- AnimationNode {
|
||||
--- function (node) residentsleeper:sleep(1000, node) end, -- must pass itself down as the parameter
|
||||
--- onEnd = function() print("completed") end,
|
||||
--- children = { -- children run in parallel after the parent animation is completed
|
||||
--- AnimationNode {
|
||||
--- function (node) sprite:animate("attack", node) end
|
||||
--- },
|
||||
--- AnimationNode {
|
||||
--- function (node) other_sprite:animate("hurt", node) end
|
||||
--- },
|
||||
--- }
|
||||
--- }:run()
|
||||
--- ```
|
||||
--- @deprecated
|
||||
--- @class AnimationNode
|
||||
--- @field count integer
|
||||
--- @field run animationRunner
|
||||
--- @field parent AnimationNode?
|
||||
--- @field children AnimationNode[]
|
||||
--- @field finish voidCallback
|
||||
--- @field onEnd voidCallback?
|
||||
--- @field duration number продолжительность в миллисекундах
|
||||
--- @field easing ease функция смягчения
|
||||
--- @field t number прогресс анимации
|
||||
--- @field state "running" | "waiting" | "finished"
|
||||
local animation = {}
|
||||
animation.__index = animation
|
||||
|
||||
--- Регистрация завершения дочерней анимации
|
||||
function animation:bubbleUp()
|
||||
self.count = self.count - 1
|
||||
if self.count > 0 then return end
|
||||
self.state = "finished"
|
||||
if self.onEnd then self.onEnd() end
|
||||
if self.parent then self.parent:bubbleUp() end
|
||||
end
|
||||
|
||||
--- @param children AnimationNode[]
|
||||
--- Запланировать анимации после текущей, которые запустятся одновременно друг с другом
|
||||
function animation:chain(children)
|
||||
for _, child in ipairs(children) do
|
||||
child.parent = self
|
||||
table.insert(self.children, child)
|
||||
self.count = self.count + 1
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Возвращает текущий прогресс анимации с учетом смягчения
|
||||
function animation:getValue()
|
||||
return self.easing(self.t)
|
||||
end
|
||||
|
||||
function animation:update(dt)
|
||||
if self.state ~= "running" then return end
|
||||
|
||||
if self.t < 1 then
|
||||
self.t = self.t + dt * 1000 / self.duration -- в знаменателе продолжительность анимации в секундах
|
||||
else
|
||||
self.t = 1
|
||||
self:finish()
|
||||
end
|
||||
end
|
||||
|
||||
--- @deprecated
|
||||
--- @param data {[1]: animationRunner?, onEnd?: voidCallback, duration: number?, easing: ease?, children?: AnimationNode[]}
|
||||
--- @return AnimationNode
|
||||
local function new(data)
|
||||
local t = setmetatable({}, animation)
|
||||
t.run = data[1] or function(self)
|
||||
self:finish()
|
||||
end
|
||||
t.onEnd = data.onEnd
|
||||
t.count = 1 -- своя анимация
|
||||
t.children = {}
|
||||
t:chain(data.children or {})
|
||||
t.duration = data.duration or 1000
|
||||
t.easing = data.easing or easing.linear
|
||||
t.t = 0
|
||||
t.state = "running"
|
||||
t.finish = function()
|
||||
if t.state ~= "running" then return end
|
||||
t.state = "waiting"
|
||||
t:bubbleUp()
|
||||
for _, anim in ipairs(t.children) do
|
||||
anim:run()
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
return new
|
||||
@ -1,5 +1,5 @@
|
||||
local task = require "lib.utils.task"
|
||||
local ease = require "lib.utils.easing"
|
||||
local AnimationNode = require "lib.animation_node"
|
||||
|
||||
local EFFECTS_SUPPORTED = love.audio.isEffectsSupported()
|
||||
|
||||
@ -9,7 +9,6 @@ local EFFECTS_SUPPORTED = love.audio.isEffectsSupported()
|
||||
--- @field musicVolume number
|
||||
--- @field soundVolume number
|
||||
--- @field looped boolean
|
||||
--- @field animationNode AnimationNode?
|
||||
--- @field from love.Source?
|
||||
--- @field to love.Source?
|
||||
audio = {}
|
||||
@ -25,11 +24,11 @@ local function new(musicVolume, soundVolume)
|
||||
end
|
||||
|
||||
function audio:update(dt)
|
||||
if not self.animationNode then return end
|
||||
self.from:setVolume(self.musicVolume - self.animationNode:getValue() * self.musicVolume)
|
||||
self.to:setVolume(self.animationNode:getValue() * self.musicVolume)
|
||||
self.animationNode:update(dt)
|
||||
-- print(self.animationNode.t)
|
||||
if self.fader then
|
||||
local t = self.fader.value
|
||||
if self.from then self.from:setVolume(self.musicVolume * (1 - t)) end
|
||||
if self.to then self.to:setVolume(self.musicVolume * t) end
|
||||
end
|
||||
end
|
||||
|
||||
--- if from is nil, than we have fade in to;
|
||||
@ -45,19 +44,39 @@ function audio:crossfade(from, to, ms)
|
||||
to:setVolume(0)
|
||||
self.from = from
|
||||
self.to = to
|
||||
self.animationNode = AnimationNode {
|
||||
function(node) end,
|
||||
onEnd = function()
|
||||
self.from:setVolume(0)
|
||||
self.to:setVolume(self.musicVolume)
|
||||
self.from:stop()
|
||||
self.animationNode = nil
|
||||
print("[Audio]: Crossfade done")
|
||||
end,
|
||||
duration = ms or 1000,
|
||||
easing = ease.easeOutCubic,
|
||||
}
|
||||
self.animationNode:run()
|
||||
|
||||
-- Using a dummy object to tween a value from 0 to 1
|
||||
local fade = { value = 0 }
|
||||
|
||||
task.tween(fade, { value = 1 }, ms or 1000, ease.easeOutCubic)(function()
|
||||
self.from:setVolume(0)
|
||||
self.to:setVolume(self.musicVolume)
|
||||
self.from:stop()
|
||||
print("[Audio]: Crossfade done")
|
||||
end)
|
||||
|
||||
-- We need a custom update loop for the volume during the tween
|
||||
-- Since task.tween updates properties, we can use a "monitor" task or just rely on `update` if we expose the task?
|
||||
-- Actually, task.tween updates `fade.value` every frame.
|
||||
-- We need to apply this value to the audio sources every frame.
|
||||
-- BUT task.tween doesn't provide an "onUpdate" callback easily.
|
||||
-- Workaround: create a task that runs every frame? Or modify task.tween?
|
||||
|
||||
-- Let's check task.lua... it updates properties on the target object.
|
||||
-- So `fade.value` will change. We need to apply it.
|
||||
-- We can hook into `update` or use a metatable on `fade`?
|
||||
|
||||
-- Let's use a "reactive" table with metatable setters? Overkill.
|
||||
-- Simplest way: Add `onUpdate` to task.tween or just use `audio:update` to poll `fade.value`?
|
||||
-- But `fade` is local.
|
||||
|
||||
-- Better approach: Tween `self` properties? No, volume is on Source userdate.
|
||||
-- Let's add a `fader` object to `self` that we tween, and in `update` we apply it.
|
||||
|
||||
self.fader = { value = 0 }
|
||||
task.tween(self.fader, { value = 1 }, ms or 1000, ease.easeOutCubic)(function()
|
||||
self.fader = nil
|
||||
end)
|
||||
end
|
||||
|
||||
--- @param source love.Source
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
local AnimationNode = require "lib.animation_node"
|
||||
local easing = require "lib.utils.easing"
|
||||
|
||||
local function closestCharacter(char)
|
||||
@ -22,7 +21,6 @@ local function closestCharacter(char)
|
||||
end
|
||||
|
||||
--- @class AIBehavior : Behavior
|
||||
--- @field animationNode AnimationNode?
|
||||
--- @field target Vec3?
|
||||
local behavior = {}
|
||||
behavior.__index = behavior
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
local AnimationNode = require "lib.animation_node"
|
||||
local easing = require "lib.utils.easing"
|
||||
local task = require "lib.utils.task"
|
||||
|
||||
--- @class LightBehavior : Behavior
|
||||
--- @field intensity number
|
||||
--- @field color Vec3
|
||||
--- @field seed integer
|
||||
--- @field colorAnimationNode? AnimationNode
|
||||
--- @field private animateColorCallback? fun(): nil
|
||||
--- @field targetColor? Vec3
|
||||
--- @field sourceColor? Vec3
|
||||
--- @field private animateColorTask? Task
|
||||
local behavior = {}
|
||||
behavior.__index = behavior
|
||||
behavior.id = "light"
|
||||
@ -24,30 +20,12 @@ function behavior.new(values)
|
||||
end
|
||||
|
||||
function behavior:update(dt)
|
||||
if not self.colorAnimationNode then return end
|
||||
local delta = self.targetColor - self.sourceColor
|
||||
self.color = self.sourceColor + delta * self.colorAnimationNode:getValue()
|
||||
self.colorAnimationNode:update(dt)
|
||||
-- All logic moved to tasks
|
||||
end
|
||||
|
||||
--- @TODO: refactor
|
||||
function behavior:animateColor(targetColor)
|
||||
if self.colorAnimationNode then self.colorAnimationNode:finish() end
|
||||
self.colorAnimationNode = AnimationNode {
|
||||
function(_) end,
|
||||
easing = easing.easeInQuad,
|
||||
duration = 800,
|
||||
onEnd = function()
|
||||
if self.animateColorCallback then self.animateColorCallback() end
|
||||
end
|
||||
}
|
||||
self.colorAnimationNode:run()
|
||||
self.sourceColor = self.color
|
||||
self.targetColor = targetColor
|
||||
|
||||
return function(callback)
|
||||
self.animateColorCallback = callback
|
||||
end
|
||||
function behavior:animateColor(targetColor, duration, easing)
|
||||
-- If there's support for canceling tasks, we should do it here
|
||||
return task.tween(self, { color = targetColor }, duration or 800, easing)
|
||||
end
|
||||
|
||||
function behavior:draw()
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
local utils = require "lib.utils.utils"
|
||||
local task = require "lib.utils.task"
|
||||
|
||||
--- Отвечает за перемещение по тайлам
|
||||
--- @class TiledBehavior : Behavior
|
||||
--- @field private runSource? Vec3 точка, из которой бежит персонаж
|
||||
--- @field private runTarget? Vec3 точка, в которую в данный момент бежит персонаж
|
||||
--- @field private path? Deque путь, по которому сейчас бежит персонаж
|
||||
--- @field private followPathCallback? fun()
|
||||
--- @field private t0 number время начала движения
|
||||
--- @field size Vec3
|
||||
local behavior = {}
|
||||
behavior.__index = behavior
|
||||
@ -22,29 +18,33 @@ end
|
||||
--- @param path Deque
|
||||
--- @return Task<nil>
|
||||
function behavior:followPath(path)
|
||||
if path:is_empty() then return task.fromValue(nil) end
|
||||
|
||||
self.owner:try(Tree.behaviors.sprite, function(sprite)
|
||||
sprite:loop("run")
|
||||
end)
|
||||
self.path = path;
|
||||
---@type Vec3
|
||||
local nextCell = path:peek_front()
|
||||
self:runTo(nextCell)
|
||||
path:pop_front()
|
||||
|
||||
return function(callback)
|
||||
self.followPathCallback = callback
|
||||
-- Рекурсивная функция для прохода по пути
|
||||
local function nextStep()
|
||||
if path:is_empty() then
|
||||
self.owner:try(Tree.behaviors.sprite, function(sprite)
|
||||
sprite:loop("idle")
|
||||
end)
|
||||
return task.fromValue(nil)
|
||||
end
|
||||
|
||||
local nextCell = path:pop_front()
|
||||
return task.chain(self:runTo(nextCell), nextStep)
|
||||
end
|
||||
|
||||
return nextStep()
|
||||
end
|
||||
|
||||
--- @param target Vec3
|
||||
--- @return Task<nil>
|
||||
function behavior:runTo(target)
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
self.t0 = love.timer.getTime()
|
||||
self.runTarget = target
|
||||
|
||||
self.runSource = positioned.position
|
||||
if not positioned then return task.fromValue(nil) end
|
||||
|
||||
self.owner:try(Tree.behaviors.sprite,
|
||||
function(sprite)
|
||||
@ -55,34 +55,15 @@ function behavior:runTo(target)
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local distance = target:subtract(positioned.position):length()
|
||||
local duration = distance * 500 -- 500ms per unit
|
||||
|
||||
return task.tween(positioned, { position = target }, duration)
|
||||
end
|
||||
|
||||
function behavior:update(dt)
|
||||
if self.runTarget then
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
local delta = love.timer.getTime() - self.t0 or love.timer.getTime()
|
||||
local fraction = delta /
|
||||
(0.5 * self.runTarget:subtract(self.runSource):length()) -- бежим одну клетку за 500 мс, по диагонали больше
|
||||
if fraction >= 1 then -- анимация перемещена завершена
|
||||
positioned.position = self.runTarget
|
||||
if not self.path:is_empty() then -- еще есть, куда бежать
|
||||
self:runTo(self.path:pop_front())
|
||||
else -- мы добежали до финальной цели
|
||||
self.owner:try(Tree.behaviors.sprite, function(sprite)
|
||||
sprite:loop("idle")
|
||||
end)
|
||||
self.runTarget = nil
|
||||
|
||||
if self.followPathCallback then
|
||||
self.followPathCallback()
|
||||
end
|
||||
end
|
||||
else -- анимация перемещения не завершена
|
||||
positioned.position = utils.lerp(self.runSource, self.runTarget, fraction) -- линейный интерполятор
|
||||
end
|
||||
end
|
||||
-- Logic moved to tasks
|
||||
end
|
||||
|
||||
return behavior
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
local Vec3 = require "lib.utils.vec3"
|
||||
local utils = require "lib.utils.utils"
|
||||
local task = require "lib.utils.task"
|
||||
local easing = require "lib.utils.easing"
|
||||
|
||||
local EPSILON = 0.001
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
local task = require "lib.utils.task"
|
||||
local easing = require "lib.utils.easing"
|
||||
local AnimationNode = require "lib.animation_node"
|
||||
local Element = require "lib.simple_ui.element"
|
||||
local Rect = require "lib.simple_ui.rect"
|
||||
local SkillRow = require "lib.simple_ui.level.skill_row"
|
||||
@ -7,7 +7,8 @@ local Bars = require "lib.simple_ui.level.bottom_bars"
|
||||
local EndTurnButton = require "lib.simple_ui.level.end_turn"
|
||||
|
||||
--- @class CharacterPanel : UIElement
|
||||
--- @field animationNode AnimationNode
|
||||
--- @field animationTask Task
|
||||
--- @field alpha number
|
||||
--- @field state "show" | "idle" | "hide"
|
||||
--- @field skillRow SkillRow
|
||||
--- @field bars BottomBars
|
||||
@ -21,41 +22,26 @@ function characterPanel.new(characterId)
|
||||
t.skillRow = SkillRow(characterId)
|
||||
t.bars = Bars(characterId)
|
||||
t.endTurnButton = EndTurnButton {}
|
||||
t.alpha = 0 -- starts hidden/animating
|
||||
return setmetatable(t, characterPanel)
|
||||
end
|
||||
|
||||
function characterPanel:show()
|
||||
AnimationNode {
|
||||
function(animationNode)
|
||||
if self.animationNode then self.animationNode:finish() end
|
||||
self.animationNode = animationNode
|
||||
self.state = "show"
|
||||
end,
|
||||
duration = 300,
|
||||
onEnd = function()
|
||||
self.state = "idle"
|
||||
end,
|
||||
easing = easing.easeOutCubic
|
||||
}:run()
|
||||
self.state = "show"
|
||||
self.animationTask = task.tween(self, { alpha = 1 }, 300, easing.easeOutCubic)
|
||||
self.animationTask(function() self.state = "idle" end)
|
||||
end
|
||||
|
||||
function characterPanel:hide()
|
||||
AnimationNode {
|
||||
function(animationNode)
|
||||
if self.animationNode then self.animationNode:finish() end
|
||||
self.animationNode = animationNode
|
||||
self.state = "hide"
|
||||
end,
|
||||
duration = 300,
|
||||
easing = easing.easeOutCubic
|
||||
}:run()
|
||||
self.state = "hide"
|
||||
self.animationTask = task.tween(self, { alpha = 0 }, 300, easing.easeOutCubic)
|
||||
end
|
||||
|
||||
--- @type love.Canvas
|
||||
local characterPanelCanvas;
|
||||
|
||||
function characterPanel:update(dt)
|
||||
if self.animationNode then self.animationNode:update(dt) end
|
||||
-- Tasks update automatically via task.update(dt) in main.lua
|
||||
self.skillRow:update(dt)
|
||||
self.bars.bounds = Rect {
|
||||
width = self.skillRow.bounds.width,
|
||||
@ -82,14 +68,8 @@ function characterPanel:update(dt)
|
||||
end
|
||||
|
||||
--- анимация появления
|
||||
local alpha = 1
|
||||
if self.state == "show" then
|
||||
alpha = self.animationNode:getValue()
|
||||
elseif self.state == "hide" then
|
||||
alpha = 1 - self.animationNode:getValue()
|
||||
end
|
||||
local revealShader = Tree.assets.files.shaders.reveal
|
||||
revealShader:send("t", alpha)
|
||||
revealShader:send("t", self.alpha)
|
||||
end
|
||||
|
||||
function characterPanel:draw()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
local Element = require "lib.simple_ui.element"
|
||||
local AnimationNode = require "lib.animation_node"
|
||||
local task = require "lib.utils.task"
|
||||
local easing = require "lib.utils.easing"
|
||||
|
||||
--- @class EndTurnButton : UIElement
|
||||
@ -51,14 +51,9 @@ function endTurnButton:onClick()
|
||||
local playing = Tree.level.characters[cid]
|
||||
if not playing:has(Tree.behaviors.positioned) then return end
|
||||
|
||||
AnimationNode {
|
||||
function(node)
|
||||
Tree.level.camera:animateTo(playing:has(Tree.behaviors.positioned).position, node)
|
||||
end,
|
||||
duration = 1500,
|
||||
easing = easing.easeInOutCubic,
|
||||
onEnd = function() if not playing:has(Tree.behaviors.ai) then Tree.level.selector:select(cid) end end
|
||||
}:run()
|
||||
Tree.level.camera:animateTo(playing:has(Tree.behaviors.positioned).position, 1500, easing.easeInOutCubic)(
|
||||
function() if not playing:has(Tree.behaviors.ai) then Tree.level.selector:select(cid) end end
|
||||
)
|
||||
end
|
||||
|
||||
return function(values)
|
||||
|
||||
@ -6,7 +6,12 @@ local easing_lib = require "lib.utils.easing"
|
||||
--- @alias Task fun(callback: fun(value: T): nil): nil
|
||||
|
||||
local task = {}
|
||||
local activeTweens = {}
|
||||
local activeTweens = {} -- list of tweens
|
||||
-- We also need a way to track tweens by target to support cancellation/replacement
|
||||
-- Let's stick to a simple list for update, but maybe add a helper to find by target?
|
||||
-- Or, just allow multiple tweens per target (which is valid for different properties).
|
||||
-- But if we animate the SAME property, we have a conflict.
|
||||
-- For now, let's just add task.cancel(target) which kills ALL tweens for that target.
|
||||
|
||||
--- Внутренний хелпер для интерполяции (числа или Vec3)
|
||||
local function lerp(a, b, t)
|
||||
@ -24,17 +29,22 @@ end
|
||||
function task.update(dt)
|
||||
for i = #activeTweens, 1, -1 do
|
||||
local t = activeTweens[i]
|
||||
t.elapsed = t.elapsed + dt * 1000
|
||||
local progress = math.min(t.elapsed / t.duration, 1)
|
||||
local value = t.easing(progress)
|
||||
|
||||
for key, targetValue in pairs(t.properties) do
|
||||
t.target[key] = lerp(t.initial[key], targetValue, value)
|
||||
end
|
||||
|
||||
if progress >= 1 then
|
||||
if not t.cancelled then
|
||||
t.elapsed = t.elapsed + dt * 1000
|
||||
local progress = math.min(t.elapsed / t.duration, 1)
|
||||
local value = t.easing(progress)
|
||||
|
||||
for key, targetValue in pairs(t.properties) do
|
||||
t.target[key] = lerp(t.initial[key], targetValue, value)
|
||||
end
|
||||
|
||||
if progress >= 1 then
|
||||
t.completed = true
|
||||
table.remove(activeTweens, i)
|
||||
if t.resolve then t.resolve() end
|
||||
end
|
||||
else
|
||||
table.remove(activeTweens, i)
|
||||
if t.resolve then t.resolve() end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -57,13 +67,26 @@ function task.completer()
|
||||
return c, future
|
||||
end
|
||||
|
||||
--- Отменяет все активные твины для указанного объекта.
|
||||
--- @param target table
|
||||
function task.cancel(target)
|
||||
for _, t in ipairs(activeTweens) do
|
||||
if t.target == target then
|
||||
t.cancelled = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Создает таску, которая плавно меняет свойства объекта.
|
||||
--- Автоматически отменяет предыдущие твины для этого объекта (чтобы не было конфликтов).
|
||||
--- @param target table Объект, свойства которого меняем
|
||||
--- @param properties table Набор конечных значений { key = value }
|
||||
--- @param duration number Длительность в мс
|
||||
--- @param easing function? Функция смягчения (по умолчанию linear)
|
||||
--- @return Task<nil>
|
||||
function task.tween(target, properties, duration, easing)
|
||||
task.cancel(target) -- Cancel previous animations on this target
|
||||
|
||||
local initial = {}
|
||||
for k, _ in pairs(properties) do
|
||||
initial[k] = target[k]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user