I am creating a plugin that allows me to make quick Bezier camera tracks for tweening the camera along (video below). There are no issues, but there is one final feature I want to implement.
Please take a look at 0:27 - 0:30 in the video above and notice how the white track points all rotate when I rotate the first black buffer plate.
What I want to do: I want to create goals of CFrame rotation at each black buffer plate and then interpolate the white points to have that rotation in between each sett of black plates.
In the image above, you can see I labeled location along the track. The first 33% of white parts I want to interpolate from 0%'s rotation to 33%'s rotation (the percents identify which black plate I labeled)
Problem (I think this is how I should do it?)
I have a table of CFrames. Each CFrame has a percent associated with it. Like shown below:
So you want to see the percentage of animation complete at each plate?
I think getting magnitude between each point and them summing them will get you the length, and then you compare the length between a black plate and another to the whole track’s length
I think you would calculate the length between each point and the start point and store it in a table, then get the percentage you want and multiply it by total length, then find which 2 points are after and before that point then lerp their CFrame, for example this table
local Points = { -- Total of 6 straight points, with path length 10 studs
[1] = 0,
[2] = 2,
[3] = 4, -- Length between point 1 and point 3 is 4 studs
[4] = 6,
[5] = 8,
[6] = 10
}
And if the percentage you want is 54%, 0.54 * 10 is 5.4, we get point 3 and point 4 as 5.4 is in the middle, then you lerp the CFrame’s of both points, I think the way you would do it is this
local Point1 = Points[3]
local Point2 = Points[4]
local Studs = 5.4 -- In studs
Studs = Studs - Point1 -- 5.4 - 4 = 1.4
local OffsetCFrame = getCFrameOfPoint(3):Lerp(getCFrameOfPoint(4), Studs / (Point2 - Point1))
local Points = {}
local TotalLength = 0
local CalcPoint = workspace.Points["Point1"].Position
for i = 1, 6 do
local PointPos = workspace.Points["Point" .. i].Position
local Length = (PointPos - CalcPoint).Magnitude
TotalLength = TotalLength + Length
Points[i] = TotalLength
CalcPoint = PointPos
end
local function getCFrameOfPoint(i)
return workspace.Points["Point" .. i].CFrame
end
local P = workspace.Points.Point1:Clone()
P.Name = "Offset"
P.Parent = workspace
P.Color = Color3.fromRGB(255,0,0)
local Dir = 1
local Percent = 0
while true do
if Percent >= 1 then
Dir = -1
elseif Percent <= 0 then
Dir = 1
end
Percent = Percent + (0.01 * Dir)
local Studs = math.max(math.min(1, Percent), 0.005) * TotalLength
local Point1 = 0
local Point2 = 1
for i,v in pairs(Points) do
if v >= Studs then
Point1 = i - 1
Point2 = i
break
end
end
Studs = Studs - Points[Point1]
local OffsetCFrame = getCFrameOfPoint(Point1):Lerp(getCFrameOfPoint(Point2), Studs / (Points[Point2] - Points[Point1]))
P.CFrame = OffsetCFrame
wait()
end
I appreciate that solution, but unfortunately it has two issues:
It does not actually solve my problem.
It relies a lot on part properties/distances where the parts are just data representations in my example, not really there.
The white parts are rendered and should not be used to base anything off of. The black parts are a visual representation of the multiple CFrame goals I’d like to get one single, linear track of data to interpolate through.
The most straightforward solution is to step through your goals in ascending order of percentage to find the ‘upper bound’, and then step through them again but in reverse (descending) order to find the ‘lower bound’.
The dictionary-style structure of your table makes this more difficult because order isn’t guaranteed when iterating. I have suggested a restructuring below.
Having found the two CFrames, you probably also want to compute an interpolated value between them according to where the point is. I have suggested some code for that too.
-- suggested restructuring: {percentage, cframe}
local goals = {
{0, CFrame.new()},
{25, CFrame.new()},
{50, CFrame.new()},
{100, CFrame.new()},
}
-- get CFrame at percentage `p`
local function getCFrame(p)
local below
local above
-- step forward through the goals
-- the first value that is greater than or equal to `p`
-- ... must be the upper-border value
for i = 1, #goals do
if goals[i][1] >= p then
above = goals[i]
break
end
end
-- step backward through the goals
-- the first value that is less than or equal to `p`
-- ... must be the lower-border value
for i = #goals, 1, -1 do
if goals[i][1] <= p then
below = goals[i]
break
end
end
-- handle on-goal values (e.g. 25) where no interpolation is needed
if above == below then
return above[2]
end
-- find interpolated cframe
local range = above[1] - below[1]
local alpha = (p - below[1]) / range
return below[2]:Lerp(above[2], alpha)
end
Note that this will error for values of p that are outside of the range of your goals (e.g. -5, 105). You would need to add some extra logic to check for that.