# How to replicate Unity Animation Curves?

Similar to a post here, I need animation curves that are inputted a number and returns a value of the curve at that certain index. This is needed to find co-effiecents for lift, drag and torque for realistic aerocraft physics. Here’s an example of a graph simialr to what I’m trying to replicate:

Before anyone suggests bezier curves, that would not work for my case.

If anyone knows fast modules that can perform these graph calculations, please link them below!

i got this working, sort of, heres the module. you can tinker with it. i dont use this anymore

``````-- services

-- functions
function lerp(a, b, t)
return a + (b - a) * t
end

local AnimationCurve = {}
AnimationCurve.__index = AnimationCurve

function AnimationCurve.new()
local self = setmetatable({
points = {},
size = Vector2.new(450, 450),
visualizerCount = 100,
}, AnimationCurve)
return self
end

function AnimationCurve:_sort()
table.sort(self.points, function(a, b)
return a.time < b.time
end)
end

local key = { time = time, value = value, inTangent = inTangent, outTangent = outTangent }
table.insert(self.points, key)
--self:recalculateMinMax()
self:_sort()
return key
end

function AnimationCurve:removeKey(time)
for i, point in ipairs(self.points) do
if point.time == time then
table.remove(self.points, i)
break
end
end

self:_sort()
--self:recalculateMinMax()
end

function AnimationCurve:clear()
self.points = {}
end

function AnimationCurve:evaluate(time)
if #self.points == 0 then
error("Animation curve contains no keyframes")
end

self:_sort()
if time <= self.points[1].time then
return self.points[1].value
end
if time >= self.points[#self.points].time then
return self.points[#self.points].value
end

-- find the keyframe point
local left_point, right_point = nil, nil
for i, point in ipairs(self.points) do
if point.time > time then
left_point = self.points[i - 1]
right_point = point
break
end
end
local time_range = right_point.time - left_point.time
if time_range == 0 then
return left_point.value
end
local t = (time - left_point.time) / time_range

-- cubic hermite
local t2 = t * t
local t3 = t2 * t
local a = 2 * t3 - 3 * t2 + 1
local b = -2 * t3 + 3 * t2
local c = t3 - 2 * t2 + t
local d = t3 - t2

return a * left_point.value + b * right_point.value + c * left_point.outTangent + d * right_point.inTangent
end

function AnimationCurve:recalculateMinMax()
self:_sort()
local minX, maxX, minY, maxY = math.huge, -math.huge, math.huge, -math.huge

for i = 1, self.visualizerCount do
local t = (i - 1) / (self.visualizerCount - 1)
local x = lerp(self.points[1].time, self.points[#self.points].time, t)
local y = self:evaluate(x)
minX, maxX = math.min(minX, x), math.max(maxX, x)
minY, maxY = math.min(minY, y), math.max(maxY, y)
end

self.minX, self.maxX, self.minY, self.maxY = minX, maxX, minY, maxY
end

function AnimationCurve:updateVisualizer(parent)
local size = self.size
local anchor = Vector2.new(parent.AbsoluteSize.x - size.x, parent.AbsoluteSize.y - size.y)

if self.frames then
for _,frame in ipairs(self.frames) do
frame:Destroy()
end
end
self.frames = {}

local minX, maxX, minY, maxY = self.minX, self.maxX, self.minY, self.maxY

for i = 1, self.visualizerCount do
local t = (i - 1) / (self.visualizerCount - 1)
local x = lerp(self.points[1].time, self.points[#self.points].time, t)
local y = self:evaluate(x)

local frame = Instance.new("Frame")
frame.Parent = parent
frame.Size = UDim2.new(0, 10, 0, 10)
frame.AnchorPoint = Vector2.new(0.5, 0.5)
frame.Position = UDim2.new(
0,
(x - minX) / (maxX - minX) * size.x + (anchor.x / 2),
0,
size.y - (y - minY) / (maxY - minY) * size.y + (anchor.y / 2)
)

table.insert(self.frames, frame)
end
end

function AnimationCurve:getGuiPosition(time, parent, isEvaluated)
local size = self.size
local anchor = Vector2.new(parent.AbsoluteSize.x - size.x, parent.AbsoluteSize.y - size.y)

self:_sort()
local left_time, right_time = nil, nil
for i, point in ipairs(self.points) do
if point.time > time and i ~= 1 then
left_time = self.points[i - 1].time
right_time = point.time
break
end
end

local t
if time <= self.points[1].time then
t = 0
elseif time >= self.points[#self.points].time then
t = 1
else
local time_range = right_time - left_time
if time_range == 0 then
warn('its 0')
end

t = (time - left_time) / time_range
end

local minX, maxX, minY, maxY = self.minX, self.maxX, self.minY, self.maxY

local x = time
if isEvaluated then
if time <= self.points[1].time then
x = minX
elseif time >= self.points[#self.points].time then
x = maxX
end
end

local y = self:evaluate(time)

return UDim2.new(
0,
(x - minX) / (maxX - minX) * self.size.x + (anchor.x / 2),
0,
self.size.y - (y - minY) / (maxY - minY) * self.size.y + (anchor.y / 2)
)
end

return AnimationCurve
``````
3 Likes

Roblox has a FloatCurve class that basically works the same as Unity’s Animation Curves. You create keys by using :InsertKey(FloatCurveKey) and evaluate it at a given time using :GetValueATime(). Read the documentation for more info.

1 Like