Struggling with a particular Animation problem

  1. What do you want to achieve?
    Basically, I’m trying to achieve a working animation to be played on an NPC that will be showed on all clients at the same time.

  2. What is the issue?
    No matter what I’ve tried so far, the animation I made hasn’t even moved. The layout of my animation is in a screenshot below.

  3. What solutions have you tried so far?
    I’ve been struggling for a while now trying to use different pieces of code edit into my script to make this work, but so far no luck yet. I looked into the Developer Hub, even though I’m not the best scripter, I still gave it a shot. Nothing in specific helped me out on this topic, unless I missed it.

on = false

function onClicked()
	if on == false then
		
		on = true
		script.Parent.BackgroundColor3 = Color3.fromRGB(0, 255, 0)
        -- I attempted multiple codes in here
		script.Parent.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
		on = false	

	end
end

script.Parent.MouseButton1Click:Connect(onClicked)

Note: I appreciate any responses in attempt to help me solve this problem I’ve been having.

2 Likes

If you want it to be showed on all clients at the same time, use a remote event.

You will need to fire the remote from the client in the onClicked() function.

In the server script you must use the .OnServerEvent() event to detect when the remote has been fired from a client. In the function in your server script you will :FireAllClients(). The LocalScripts should have a function bound to .OnClientEvent() to detect when the remote has been fired from the server to their client.

In the receiving function in the LocalScript you can play the animation.

Here’s an example:

Local Script
local remote = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvent")

on = false

local function onClicked()
    if on == false then
	
	    on = true
	    script.Parent.BackgroundColor3 = Color3.fromRGB(0, 255, 0)
        remote:FireServer() -- Firing remote to the server
	    script.Parent.BackgroundColor3 = Color3.fromRGB(255, 0, 0)
	    on = false	

    end
end

script.Parent.MouseButton1Click:Connect(onClicked)


local function onRemoteFiredFromServer() -- Function for when remote is fired at client
    -- play animation
end

remote.OnClientEvent:Connect(onRemoteFiredFromServer) -- Will call onRemoteFiredFromServer() when the remote is fired from the server
Server Script
local remote = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvent")

local function onRemoteFiredFromClient(player) -- Function for when remote is fired at server
    remote:FireAllClients()
end

remote.OnServerEvent:Connect(onRemoteFiredFromClient) -- Will call onRemoteFiredFromClient() when the remote is fired from the client

This is a basic explanation of their use, I hope it helps!

edit: changed function names for clarity

3 Likes

Thanks for the thorough explanation on my problem.

I’ve done all the steps so far except for the last part on playing the animation. I’m not sure on how you would go about playing the animation. When I did an attempt on doing so, I got this error.

  22:04:06.634 - Play is not a valid member of KeyframeSequence

You need to load the animation onto the humanoid before you play it.

local animationTrack = NPC_humanoid:LoadAnimation(animation)
1 Like

That’s strange. I did that and I get yet another error…

 22:10:38.286 - Unable to cast value to Object

This is also the script where the error got caused in. I’m not sure if I made any mistakes so if possible correct me if I’m wrong.

local NPCevent = game:GetService("ReplicatedStorage"):WaitForChild("NPCevent")

local function onRemoteFiredFromServer() -- Function for when remote is fired at client
   local animationTrack = game.Workspace.RobertJr_YT.Humanoid:LoadAnimation(4820283660)
   animationTrack:Play()
end

NPCevent.OnClientEvent:Connect(onRemoteFiredFromServer) -- Will call onRemoteFired() when the remote is fired from the server

Change the ID you currently have in :LoadAnimation() to the animation’s instance:

local animationTrack = game.Workspace.RobertJr_YT.Humanoid:LoadAnimation(animationInstance)
1 Like

It works, thank you so much!

The slight problem that I noticed when the animation was done is that it teleported back to the start when it was over. Any way to fix this issue?

In order to keep the NPC from reverting back to how it was before, place this in the onRemoteFiredFromServer() function. It will listen for when the animation stops, and when it does it will freeze the animation at the last frame.

local connection

local function freezeAnimationOnLastFrame()
    animationTrack.Speed = 0 -- Sets its speed to zero so it will not change the frame it is on
    animationTrack.TimePosition = animationTrack.Length -- Sets the frame it is stuck on to the last frame
    connection:Disconnect() -- For efficiency
end

connection = animationTrack.Stopped:Connect(freezeAnimationOnLastFrame) -- Does the exact same as normally connecting a function, but we can call upon that connection object later to disconnect it
1 Like

Hmm, nothing seems to have happened and it gave me a new error.

Here is the code:

local NPCevent = game:GetService("ReplicatedStorage"):WaitForChild("NPCevent")

local function onRemoteFiredFromServer() -- Function for when remote is fired at client
    local Humanoid = game.Workspace.RobertJr_YT:FindFirstChild("Humanoid")
	 
    local Animation = Instance.new("Animation")
    Animation.AnimationId = "http://www.roblox.com/asset/?id=4820283660"
    local AnimationTrack = Humanoid:LoadAnimation(Animation)
    AnimationTrack:Play()
	
	local connection

	local function freezeAnimationOnLastFrame()
	    AnimationTrack.Speed = 0 -- Sets its speed to zero so it will not change the frame it is on
	    AnimationTrack.TimePosition = AnimationTrack.Length -- Sets the frame it is stuck on to the last frame
	    connection:Disconnect() -- For efficiency
	end
	
	connection = AnimationTrack.Stopped:Connect(freezeAnimationOnLastFrame) -- Does the exact same as normally connecting a function, but we can call upon that connection object later to disconnect it
	
end

NPCevent.OnClientEvent:Connect(onRemoteFiredFromServer) -- Will call onRemoteFired() when the remote is fired from the server

Here is the error:

  22:41:22.914 - can't set value

Sorry, you need to use GetTimeOfKeyFrame.

AnimationTrack.TimePosition = AnimationTrack:GetTimeOfKeyFrame(AnimationTrack.Length)
1 Like

Strange. I replaced that line of code, but you might’ve missed that the error was caused on this line.

AnimationTrack.Speed = 0 -- Sets its speed to zero so it will not change the frame it is on

Here we go, time to code for a bit! This should work my man. Place it in the OnRemoteFiredServer function as we did before.

As I understand it, you should be checking when it reaches the last frame instead checking when it ends because you can only adjust the speed when you initially Play() the anim (through the Play() parameters) or while it is playing.

This code identifies the last frame of the animation and creates a KeyframeMarker for the last frame which will be used to call reachedLastFrame().

local keyframes = Animation:GetKeyframes() -- gets all keyframes from the anim and places them in an array
local lastframe = keyframes[#keyframes] -- identifies the last frame

local marker = Instance.new("KeyframeMarker") -- creates the marker
marker.Name = "ReachedLastFrame"
lastframe:AddMarker(marker) -- parents the marker to the last keyframe



local function reachedLastFrame()
    AnimationTrack:AdjustSpeed(0)
end


local onLastFrame = AnimationTrack:GetMarkerReachedSignal("ReachedLastFrame")

There are other methods of doing this, but this is a surefire way to identify a specific frame. It also has an easy-to-understand event when it reaches that marker.

1 Like

I seriously appreciate you helping with this. Sadly, yet again another issue with the code.

The code:

local NPCevent = game:GetService("ReplicatedStorage"):WaitForChild("NPCevent")

local Humanoid = game.Workspace.RobertJr_YT:FindFirstChild("Humanoid")
local Animation = Instance.new("Animation")
Animation.AnimationId = "http://www.roblox.com/asset/?id=4820283660"
local AnimationTrack = Humanoid:LoadAnimation(Animation)


local function onRemoteFiredFromServer() -- Function for when remote is fired at client
	AnimationTrack:Play()
	
	local keyframes = Animation:GetKeyframes() -- gets all keyframes from the anim and places them in an array
	local lastframe = keyframes[#keyframes] -- identifies the last frame
	
	local marker = Instance.new("KeyframeMarker") -- creates the marker
	marker.Name = "ReachedLastFrame"
	lastframe:AddMarker(marker) -- parents the marker to the last keyframe
	
	
	
	local function reachedLastFrame()
	    AnimationTrack:AdjustSpeed(0)
	end
	
	
	local onLastFrame = AnimationTrack:GetMarkerReachedSignal("ReachedLastFrame")
end


NPCevent.OnClientEvent:Connect(onRemoteFiredFromServer) -- Will call onRemoteFired() when the remote is fired from the server

The error:

 23:23:53.366 - GetKeyframes is not a valid member of Animation

I’m happy to help man!
Okay, you need to get the Keyframes from your KeyframeSequence instead.

1 Like

Well, this time there is no error. Which I’m not sure if that’s a good or bad thing. Anyways, it still teleports back to the start. EDIT: The keyframe layout for the KeyframeSequence is in the picture I sent way above.

The code:

local NPCevent = game:GetService("ReplicatedStorage"):WaitForChild("NPCevent")

local Humanoid = game.Workspace.RobertJr_YT:FindFirstChild("Humanoid")
local Animation = Instance.new("Animation")
Animation.AnimationId = "http://www.roblox.com/asset/?id=4820283660"
local AnimationTrack = Humanoid:LoadAnimation(Animation)


local function onRemoteFiredFromServer() -- Function for when remote is fired at client
	AnimationTrack:Play()
	
	local keyframes = game.Workspace.RobertJr_YT.OfficialDrummer_RobertJr_YT:GetKeyframes() -- gets all keyframes from the anim and places them in an array
	local lastframe = keyframes[#keyframes] -- identifies the last frame
	
	local marker = Instance.new("KeyframeMarker") -- creates the marker
	marker.Name = "ReachedLastFrame"
	lastframe:AddMarker(marker) -- parents the marker to the last keyframe
	
	
	
	local function reachedLastFrame()
	    AnimationTrack:AdjustSpeed(0)
	end
	
	
	local onLastFrame = AnimationTrack:GetMarkerReachedSignal("ReachedLastFrame")
end


NPCevent.OnClientEvent:Connect(onRemoteFiredFromServer) -- Will call onRemoteFired() when the remote is fired from the server

Try changing

local lastframe = keyframes[#keyframes]

to

local lastframe = keyframes[#keyframes - 1]

and tell me what happens

1 Like

Same effect, and still no error.

Whoops, I forgot to connect the event to the function!

Add this to the very end of the function:

onLastFrame:Connect(reachedLastFrame)
1 Like

Unless I added the strip of code in the wrong area, there is still the same effect and no error.

The code:

local NPCevent = game:GetService("ReplicatedStorage"):WaitForChild("NPCevent")

local Humanoid = game.Workspace.RobertJr_YT:FindFirstChild("Humanoid")
local Animation = Instance.new("Animation")
Animation.AnimationId = "http://www.roblox.com/asset/?id=4820283660"
local AnimationTrack = Humanoid:LoadAnimation(Animation)


local function onRemoteFiredFromServer() -- Function for when remote is fired at client
	AnimationTrack:Play()
	
	local keyframes = game.Workspace.RobertJr_YT.OfficialDrummer_RobertJr_YT:GetKeyframes() -- gets all keyframes from the anim and places them in an array
	local lastframe = keyframes[#keyframes - 1] -- identifies the last frame
	
	local marker = Instance.new("KeyframeMarker") -- creates the marker
	marker.Name = "ReachedLastFrame"
	lastframe:AddMarker(marker) -- parents the marker to the last keyframe
	
	
	
	local function reachedLastFrame()
	    AnimationTrack:AdjustSpeed(0)
	end
	
	
	local onLastFrame = AnimationTrack:GetMarkerReachedSignal("ReachedLastFrame")
	onLastFrame:Connect(reachedLastFrame)
end


NPCevent.OnClientEvent:Connect(onRemoteFiredFromServer) -- Will call onRemoteFired() when the remote is fired from the server

No, that is the right place to put it.

Try adding

marker.Value = 100

after you set marker.Name.

1 Like