Don’t suppose you could provide a file with the keyframe sequence and door?
You can simplify the door if you don’t wish to share the details, but then id reccoment testing to ensure 100% that the door copy has the bug too
Don’t suppose you could provide a file with the keyframe sequence and door?
You can simplify the door if you don’t wish to share the details, but then id reccoment testing to ensure 100% that the door copy has the bug too
What’s the verdict? Did it work? Did it not work? @kalet14
As you can see i’m on the edge of my seat
Well, I’m still stuck since I still have no idea how to use the module due to it still giving me errors, but from what I can tell this is supposed to work through a local script which is something that I’m not looking for due to the fact that I’m making a multiplayer game and I need the doors to open for everyone.
local CutsceneAnimation = require(game.ReplicatedStorage.CutsceneAnimation)
local pathToKeyframeSequence = script.Parent.AnimSaves.Untitled
local DoorOpen = CutsceneAnimation.new(pathToKeyframeSequence)
script.Parent.Door:WaitForChild("Handle").ProximityPrompt.Triggered:Connect(function()
DoorOpen:Play(script.Parent)
end)
I got you covered replace the module you have with this
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local CutsceneAnimation = {}
CutsceneAnimation.__index = CutsceneAnimation
function CutsceneAnimation.new(sequence : KeyframeSequence)
local self = {}
setmetatable(self, CutsceneAnimation)
self.KeyframeSequence = sequence
self.Keyframes = sequence:GetKeyframes()
self.Frame = 1
self.Motors = {}
return self
end
function CutsceneAnimation:UpdatePose(frame, DT : number)
for _, pose : Pose in ipairs(frame:GetDescendants()) do
if pose:IsA("Pose") then
local Motor = self.Motors[pose.Name]
if Motor then
local x,y,z = pose.CFrame:ToEulerAnglesXYZ()
if x == 0 and y == 0 and z == 0 and pose.CFrame.Position.Magnitude == 0 then
continue
end
Motor.Transform:Lerp(pose.CFrame, DT)
end
end
end
end
function CutsceneAnimation:Play(Rig : Model, startCFrame : CFrame)
local CutsceneRig = Rig
self.CutsceneRig = CutsceneRig
local accumulatedTime = self.AccumulatedTime or 0
for _, motor in ipairs(self.CutsceneRig:GetDescendants()) do
if motor:IsA("Motor6D") then
self.Motors[motor.Name] = motor
end
end
if not startCFrame then
startCFrame = CutsceneRig.PrimaryPart.CFrame
end
CutsceneRig.PrimaryPart.Anchored = true
CutsceneRig.PrimaryPart.CFrame = startCFrame
CutsceneAnimation[Rig] = true
table.sort(self.Keyframes, function(a, b)
return a.Time < b.Time
end)
self:UpdatePose(self.Keyframes[self.Frame])
local Connection = nil
Connection = RunService.Heartbeat:Connect(function(DT)
if self.Paused then return end
accumulatedTime += DT
while self.Frame < #self.Keyframes and accumulatedTime >= self.Keyframes[self.Frame + 1].Time do
self.Frame += 1
self:UpdatePose(self.Keyframes[self.Frame], DT * 7)
end
if accumulatedTime >= self.Keyframes[self.EndKeyframe or #self.Keyframes].Time then
Connection:Disconnect()
Connection = nil
CutsceneAnimation[Rig] = false
end
end)
end
return CutsceneAnimation
I won’t give up on this, we’re going to get this working
Alright so I’m guessing the ProximityPrompt is on the client, okay so all you need to do is create a remote event and then tell it which door to open and then you do the door module stuff setup on the server and play it on the server
I won’t be providing a file for that due to the fact that the issue doesn’t lie within the animation itself since it works perfectly when I import it through Blender to Studio and in the playback/Moon animator.
This is all the of what I’m able to provide right now simply because I do not like giving out my models to people rather than scripts. I’ve also seen multiple people have an issue exactly like mines but they didn’t seem to get any help at all despite having different rigs on their end.
Unfortunately it still gives me the same error without any changes made to the script.
local CutsceneAnimation = require(game.ReplicatedStorage.CutsceneAnimation)
local pathToKeyframeSequence = script.Parent.AnimSaves.Untitled
local DoorOpen = CutsceneAnimation.new(pathToKeyframeSequence)
script.Parent.Door:WaitForChild("Handle").ProximityPrompt.Triggered:Connect(function()
DoorOpen:Play(script.Parent)
end)
Okay hold on let’s just work through this let’s first get it working on the client so we know it can work and then i’ll help you with having it animate for all clients!
Okay replace the module with this
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local CutsceneAnimation = {}
CutsceneAnimation.__index = CutsceneAnimation
function CutsceneAnimation.new(sequence : KeyframeSequence)
local self = {}
setmetatable(self, CutsceneAnimation)
self.KeyframeSequence = sequence
self.Keyframes = sequence:GetKeyframes()
self.Frame = 1
self.Motors = {}
return self
end
function CutsceneAnimation:UpdatePose(frame, DT : number)
for _, pose : Pose in ipairs(frame:GetDescendants()) do
if pose:IsA("Pose") then
local Motor = self.Motors[pose.Name]
if Motor then
local x,y,z = pose.CFrame:ToEulerAnglesXYZ()
if x == 0 and y == 0 and z == 0 and pose.CFrame.Position.Magnitude == 0 then
continue
end
Motor.Transform = Motor.Transform:Lerp(pose.CFrame, DT)
end
end
end
end
function CutsceneAnimation:Play(Rig : Model, startCFrame : CFrame)
local CutsceneRig = Rig
self.CutsceneRig = CutsceneRig
local accumulatedTime = self.AccumulatedTime or 0
for _, motor in ipairs(self.CutsceneRig:GetDescendants()) do
if motor:IsA("Motor6D") then
self.Motors[motor.Name] = motor
end
end
if not startCFrame then
startCFrame = CutsceneRig.PrimaryPart.CFrame
end
CutsceneRig.PrimaryPart.Anchored = true
CutsceneRig.PrimaryPart.CFrame = startCFrame
CutsceneAnimation[Rig] = true
table.sort(self.Keyframes, function(a, b)
return a.Time < b.Time
end)
self:UpdatePose(self.Keyframes[self.Frame], 0)
local Connection = nil
Connection = RunService.Heartbeat:Connect(function(DT)
if self.Paused then return end
accumulatedTime += DT
while self.Frame < #self.Keyframes and accumulatedTime >= self.Keyframes[self.Frame + 1].Time do
self.Frame += 1
self:UpdatePose(self.Keyframes[self.Frame], DT * 7)
end
if accumulatedTime >= self.Keyframes[self.EndKeyframe or #self.Keyframes].Time then
Connection:Disconnect()
Connection = nil
CutsceneAnimation[Rig] = false
end
end)
end
return CutsceneAnimation
Fair enough. Hope it gets solved, but if you change your mind then lemme know, you can just strip off anything unnecessary if you don’t wish to share the model, assuming the bug persists with a stripped down version. Even if the animation itself isn’t the issue, I’d still need it to check issues with the model/script.
I guess it somewhat moves the door but it doesn’t really do much other than leave it like this…
Not to mention its movement is really laggy.
Could you please show a video?
Try this, this will tell us how much frames there are in the sequence and also what frame it ended at
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local CutsceneAnimation = {}
CutsceneAnimation.__index = CutsceneAnimation
function CutsceneAnimation.new(sequence : KeyframeSequence)
local self = {}
setmetatable(self, CutsceneAnimation)
self.KeyframeSequence = sequence
self.Keyframes = sequence:GetKeyframes()
self.Frame = 1
self.Motors = {}
print(#self.Keyframes.. "keyframes")
return self
end
function CutsceneAnimation:UpdatePose(frame, DT : number)
for _, pose : Pose in ipairs(frame:GetDescendants()) do
if pose:IsA("Pose") then
local Motor : Motor6D = self.Motors[pose.Name]
if Motor then
local x,y,z = pose.CFrame:ToEulerAnglesXYZ()
if x == 0 and y == 0 and z == 0 and pose.CFrame.Position.Magnitude == 0 then
continue
end
Motor.Transform = Motor.Transform:Lerp(pose.CFrame, DT)
end
end
end
end
function CutsceneAnimation:Play(Rig : Model, startCFrame : CFrame)
local CutsceneRig = Rig
self.CutsceneRig = CutsceneRig
local accumulatedTime = self.AccumulatedTime or 0
for _, motor in ipairs(self.CutsceneRig:GetDescendants()) do
if motor:IsA("Motor6D") then
self.Motors[motor.Name] = motor
end
end
if not startCFrame then
startCFrame = CutsceneRig.PrimaryPart.CFrame
end
CutsceneRig.PrimaryPart.Anchored = true
CutsceneRig.PrimaryPart.CFrame = startCFrame
CutsceneAnimation[Rig] = true
table.sort(self.Keyframes, function(a, b)
return a.Time < b.Time
end)
self:UpdatePose(self.Keyframes[self.Frame], 0)
local Connection = nil
Connection = RunService.Heartbeat:Connect(function(DT)
if self.Paused then return end
accumulatedTime += DT
while self.Frame < #self.Keyframes and accumulatedTime >= self.Keyframes[self.Frame + 1].Time do
self.Frame += 1
self:UpdatePose(self.Keyframes[self.Frame], DT * 7)
end
if accumulatedTime >= self.Keyframes[self.EndKeyframe or #self.Keyframes].Time then
print("Stopped at"..self.Frame)
Connection:Disconnect()
Connection = nil
CutsceneAnimation[Rig] = false
end
end)
end
return CutsceneAnimation
It says that there’s 5 keyframes in the animation.
Am I missing something? To me, it seems like the speed is the same, it just has maybe a slight playing delay at the beginning, which is probably due to the fact that animations has to replicate to other clients.
You can run the animation locally for every player and see if that helps.
Hold up I’m going to do some testing on a rigged door I just got out of toolbox, just HOLD ON
Nothing was mentioned about the speed alone of the door. In the studio playback you can see it doesn’t fully go into the wall, but in the playtest it goes beyond the wall.
I wasn’t gonna give up on you that easily @kalet14 ,
Alright so get this awesome spring module and put it inside of ReplicatedStorage
(I didn’t create)
spr (Spring Module).rbxm (9.6 KB)
It’ll allow super smooth movement with some nice spring action, also it’s safe so dont worry about that
And then use this updated module
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local spr = require(ReplicatedStorage.spr)
local RunService = game:GetService("RunService")
local CutsceneAnimation = {}
CutsceneAnimation.__index = CutsceneAnimation
function CutsceneAnimation.new(sequence : KeyframeSequence)
local self = {}
setmetatable(self, CutsceneAnimation)
self.KeyframeSequence = sequence
self.Keyframes = sequence:GetKeyframes()
self.Frame = 1
self.Motors = {}
return self
end
function CutsceneAnimation:Play(Rig : Model, startCFrame : CFrame)
local CutsceneRig = Rig
self.CutsceneRig = CutsceneRig
local accumulatedTime = self.AccumulatedTime or 0
for _, motor in ipairs(self.CutsceneRig:GetDescendants()) do
if motor:IsA("Motor6D") then
self.Motors[motor.Name] = motor
end
end
print(self.Motors)
if not startCFrame then
startCFrame = CutsceneRig.PrimaryPart.CFrame
end
CutsceneRig.PrimaryPart.Anchored = true
CutsceneRig.PrimaryPart.CFrame = startCFrame
CutsceneAnimation[Rig] = true
table.sort(self.Keyframes, function(a, b)
return a.Time < b.Time
end)
local Connection = nil
local function updatePose()
for _, pose : Pose in ipairs(self.Keyframes[self.Frame]:GetDescendants()) do
if pose:IsA("Pose") then
local Motor : Motor6D = self.Motors[pose.Name]
if Motor then
local x,y,z = pose.CFrame:ToEulerAnglesXYZ()
if x == 0 and y == 0 and z == 0 and pose.CFrame.Position.Magnitude == 0 then
continue
end
spr.target(Motor, .4, 1,{
Transform = pose.CFrame
})
end
end
end
end
Connection = RunService.Heartbeat:Connect(function(DT)
if self.Paused then return end
accumulatedTime += DT
while self.Frame < #self.Keyframes and accumulatedTime >= self.Keyframes[self.Frame + 1].Time do
self.Frame += 1
updatePose()
end
if accumulatedTime >= self.Keyframes[self.EndKeyframe or #self.Keyframes].Time then
print("Animation ended")
Connection:Disconnect()
Connection = nil
CutsceneAnimation[Rig] = false
end
end)
end
return CutsceneAnimation
Unfortunately, I found a solution that isn’t really that well and limits my animating abilities for any future animations I make with the door model and that is using the animation editor that Studio has built in and I had to make a ton of keyframes since using any sort of easing at all will break the doors animation.