Animations Playing for wrong player's when Play(), AdjustSpeed() or stop() methods are used

  1. What do you want to achieve?
  • When the event is activated, I want the animations to act on the clients that activated it only but still so other players can see it.
  1. What is the issue?
  • Currently when a server event is triggered, when either the Play, AdjustSpeed or Stop methods are called for the already loaded animations, they fire on every user that also has the animations loaded, but I only want the animation to act on the player that activated the Serverevent. They must be visible to other players too (hence why not in a local script).
  1. What solutions have you tried so far?
  • Creating and loading the animationtracks client side, and passing them in as arguments in the events, didn’t work.

  • Creating and loading the animationtracks locally in one of the serverevents, and then calling a function from that event which will return “animationTrackA” and “animationTrackB”, this worked but didn’t solve my issue with the animations playing and stopping for the wrong people.

  • Tried various other different things with the animationTrack variables.

  • I’ve scanned other similar forum posts, tried google and asked AI but still came to no progress. I’m not very good when it comes to scripting so I apologies in advance if my code is bad.

  1. Extra Information:
  • I believe part of the issue is because the animations aren’t local to the event, and they are being used across them globally. I found this out because out of the three different animationtracks I am running, the third one (animationTrackC) is local because I don’t need to access it anywhere else, and I don’t get any issues with it running for other players when I don’t want it to.

  • If you hadn’t guessed yet, I’m making a Detain system for someone.

Code snippets:

  • There is more code other than whats below, it’s just this snippet I’ve taken out which shows the problematic code:
ClickEvent.OnServerEvent:Connect(function(Player, clickedPlayer)
	if Player.Character:FindFirstChild(ToolName) then
		PUT = Player.Character:FindFirstChild("Torso") or Player.Character:FindFirstChild("UpperTorso")
		CPOT = clickedPlayer.Character:FindFirstChild("Torso") or clickedPlayer.Character:FindFirstChild("UpperTorso")
		local Tool = Player.Character:FindFirstChild(ToolName)
		if clickedPlayer.Character:FindFirstChild("Humanoid") then
			clickedPlayer.Character.Humanoid.WalkSpeed = 0
			clickedPlayer.Character.Humanoid.UseJumpPower = true
			clickedPlayer.Character.Humanoid.JumpPower = 0
			clickedPlayer.Character.Humanoid.PlatformStand = true
		end

		animationTrackA = Instance.new("Animation")
		animationTrackA = clickedPlayer.Character.Humanoid:LoadAnimation(Detain)
		animationTrackB = Instance.new("Animation")
		animationTrackB = Player.Character.Humanoid:LoadAnimation(MoveCuff)	

		DisableReset:FireClient(clickedPlayer)
		clickedPlayer.DevEnableMouseLock = false
		SharedClickedPlayer = clickedPlayer

		if not PUT:FindFirstChild("Detained") then
			for _, i in pairs(clickedPlayer.Character:GetDescendants()) do
				if i:IsA("BasePart") then
					i.Massless = true
					i:SetNetworkOwner(Player)
				end
			end
			local WeldA = Instance.new("Weld", PUT)
			WeldA.Name = "Detained" 
			WeldA.Part0 = PUT
			WeldA.Part1 = CPOT
			WeldA.C1 = WeldA.C1 * CFrame.new(-0.75, 0.25, 2)
			local StringValue = Instance.new("StringValue", CPOT)
			StringValue.Name = "Cuffed"
			StringValue.Value = "Cuffed"
		end		 

		animationTrackA:Play()
		animationTrackB:Play()
		repeat wait() until animationTrackA.Length > 0
		wait(animationTrackA.Length - 0.05)
		animationTrackA:AdjustSpeed(0)
		animationTrackB:AdjustSpeed(0)
	end
end)

DReleaseEvent.OnServerEvent:Connect(function(Player, clickedPlayer)
	local WeldA = Player.Character:FindFirstChild("Torso"):FindFirstChild("Detained") or Player.Character:FindFirstChild("UpperTorso"):FindFirstChild("Detained")
	local StringValue = clickedPlayer.Character:FindFirstChild("Torso"):FindFirstChild("Cuffed") or clickedPlayer.Character:FindFirstChild("UpperTorso"):FindFirstChild("Cuffed")
	if clickedPlayer.Character:FindFirstChild("Humanoid") then
		clickedPlayer.Character.Humanoid.WalkSpeed = 10
		clickedPlayer.Character.Humanoid.UseJumpPower = false
		clickedPlayer.Character.Humanoid.JumpPower = 50
		clickedPlayer.Character.Humanoid.PlatformStand = false
	end

	clickedPlayer.DevEnableMouseLock = true

	for _, i in pairs(clickedPlayer.Character:GetDescendants()) do
		if i:IsA("BasePart") then
			i.Massless = false
			i:SetNetworkOwner(clickedPlayer)
		end
	end

	WeldA:Destroy()
	StringValue:Destroy()
	if animationTrackB ~= nil then
		animationTrackB:AdjustSpeed(-1)
		animationTrackB:Destroy()
	end
end)
2 Likes

It is because you are defining variables in the global scope:

function MyFunc()
    a = 10
end

print(a) -- Prints 10

function MyFunc2()
    local b = 2
end

print(b) -- Prints nil (because b is not defined in this scope)

You need to prefix your variable declarations with local.

Why are you doing = Instance.new("Animation")? You are just creating a useless Animation object, it gets overriden directly after. Additionally, you should call :LoadAnimation on the Animator of the Humanoid, as Humanoid:LoadAnimation is deprecated (not recommended, not as supported).

Here is the corrected code for this specific snippet:

local DetaineeAnimator = clickedPlayer.Character.Humanoid.Animator
local CopAnimator = Player.Character.Humanoid.Animator
local animationTrackA = DetaineeAnimator:LoadAnimation(Detain)
local animationTrackB = Cop:LoadAnimation(MoveCuff)

Then, the question arises, how do we end the animation? How do we destroy the Welds and the StringValue? It’s quite simple, we put them in a dictionary.

local DetainInfoDict = {}

Then, in the detaining code, we assign the values to keep track of our instances:

animationTrackA:Play()
animationTrackB:Play()

DetainInfoDict[Player] = {
    animationA = animationA,
    animationB = animationB,
    WeldA = WeldA,
    StringValue = StringValue
}

Then, we simply reference these values in the Release code:

→

local DetainInfo = DetainInfoDict[Player]
if DetainInfo then
    DetainInfo.animationTrackB:Destroy()
    
    -- Remove references to the instances so that they get Destroy'ed properly
    DetainInfoDict[Player] = nil
end

There’s no point calling :AdjustSpeed because we are removing the animation right after anyway.

You do not need the pairs and can simply leave it like so:

for _, i in clickedPlayer.Character:GetDescendants() do

Other nitpicks are that you could use more variables, and give the existing ones better names (like PUT and CPOT, those two are not very descriptive).

Additionally, you should do extra distance checks to ensure player’s can only detain others nearby, to prevent exploits.

Hope this helps!

1 Like

Thanks Judgy, working sound! Absolute legend.
I will make sure to use better naming conventions in the future! Although I have annotated the code now explaining what it means.

Yeah in the local script I have included a distance check when an officer clicks so thats all good.
But yeah I really appreciate it thankyou for taking the time to help me :slight_smile:
Have a good day.

LocalScripts are on the client, therefore they can be manipulated. You need to do the distance checks on the Server as well if you want to prevent exploits with this system.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.