Is it possible to Tween C0 and bind a C0 in RenderStepped at the same time?

Currently I’m trying to tween a Motor6D C0 while forcing it to follow the mouse:

This is being binded inside a RunService.RenderStepped, such that player’s arm will follow the mouse.
["Right Shoulder"].C0 = CFrame.new(1,0.5,0) * CFrame.Angles(-math.asin((mouse.Origin.p - mouse.Hit.p).unit.y),1.55,0)

Is it possible to apply TweenService to tween CFrame.new(1,0.5,0) to CFrame.new(1,2,0) while maintaining it to follow the mouse?

Unfortunately, it won’t work the way you’re hoping. One possible solution that still uses TweenService is TweenService:GetValue(). This is a tween without actually doing the tweening. You give it the percent of time elapsed, easing direction, and easing style and it spits out how far along the tween you are.


Alpha indicates the percent of time that has elapsed in the tween. If 1 second has elapsed in a 2 second tween, it will be 0.5. If 2 seconds have elapsed in a 2 second, it will be 1.

TweenService:GetValue() gives back a number that is also between 0 and 1. This indicates how far along the tween you are.

Here is an example. This is a quad tween. As time progresses toward 1, the tween percent follows a quart easing style toward 1.

So in your RenderStep function, determine what time in the tween you are, which is alpha. Then, use TweenService:GetValue() to get the tween progression. Lerp the CFrames by this amount. Finally, multiply by your CFrame.Angles(...) and assign it to C0.

4 Likes

Using TweenService to change C0 will definitely cause issues with your C0 adjustments in RenderStepped. Try tweening the C1 property instead to something like:

CFrame.new(0, -1.5, 0)

to give the effect of C0 being set to

CFrame.new(1, 2, 0)

I still don’t understand about the relationship between C0 and C1 after I read about it in Developer Hub, mind explaining it a bit?

@ScriptSurge has a clever solution! Separate the tasks: C0 handles the position tweening and C1 handles the rotation.

Right now, you’re doing the position CFrame and the rotation CFrame both on C0.

C0 = Position * Rotation

Instead, you can separate them as

C0 = Position
C1 = Rotation:Inverse()

And then tween C0 without touching C1.

Math explanation

To explain why this works, I’ll go over the basic math without matrices.

  1. To start, all welds must follow this rule:
    image
    Part0’s CFrame times C0 must be equal to Part1’s CFrame times C1.

  2. We’re not using C1 right now, so we don’t need it.
    image

  3. Our C0 is made of a position CFrame times a rotation CFrames, so let’s plug that in for C0.
    image

  4. We want to separate R from P by putting it on the other side. To do that, multiply both sides by the inverse of R.
    image

  5. Lastly, R times its inverse cancels itself out, leaving us with
    image
    Done! The position and rotation CFrames are separated. P is our C0 and R-1 is our C1.

1 Like

Sorry about the necropost, but you could CFrame it via RenderStepped on your client, and you can fire the server which would fire everyone else’s client to tween it. It would be smoother for you, and for them.

-- server
local tiltEvent = ...

tiltEvent.OnServerEvent:Connect(function(tiltPlayer, joints, cframes)
    for _, player in ipairs(Players:GetPlayers()) do
        if player ~= tiltPlayer then
            tiltEvent:FireClient(player, joints, cframes)
        end
    end
end
-- their client
local TweenService = game:GetService("TweenService")

local tiltEvent = ...

local tweens = {}

tiltEvent.OnClientEvent:Connect(function(joints, cframes)
    for i, joint in ipairs(joints) do
        if tweens[joint:GetFullName()] then 
            tweens[joint:GetFullName()] = tweens[joint:GetFullName()]:Cancel() 
        end

        tweens[joint:GetFullName()] = TweenService:Create(joint, TweenInfo.new(0.1, Enum.EasingStyle.Quint, Enum.EasingDirection.Out), { C0 = cframes[i] })
        tweens[joint:GetFullName()]:Play()

        local conn -- NO MEMORY LEAKS!!!!!!!!
        conn = tweens[joint:GetFullName()].Completed:Connect(function()
            conn:Disconnect()
            tweens[joint:GetFullName()] = nil
        end)
    end
end)
-- your client
local tiltEvent = ...

local lastTick = tick()
RunService.RenderStepped:Connect(function()
    local x = -math.asin((mouse.Origin.p - mouse.Hit.p).unit.y)

    local _, y, z = rightShoulder.C0:ToEulerAnglesXYZ()
    local rightC0 = CFrame.new(rightShoulder.C0.Position) * CFrame.Angles(x, y, z)
    rightShoulder.C0 = rightC0

    _, y, z = leftShoulder.C0:ToEulerAnglesXYZ()
    local leftC0 = CFrame.new(leftShoulder.C0.Position) * CFrame.Angles(x, y, z)
    leftShoulder.C0 = leftC0

    if tick() - lastTick >= 0.1 then
        tiltEvent:FireServer({rightShoulder, leftShoulder}, {rightC0, leftC0})
        lastTick = tick()
    end
end)