Automatic Animation Sync

  1. What do you want to achieve?

Just like every other Vibe/TTD/Emote… Game.

I was really curious how it is possible to make Automatic Syncing.

As in

If a player syncs with someone, it will automatically dance them to the player. Though if the player that is being synced with CHANGES their dance, it will AUTOMATICALLY change the person syncing with that one player dance.

For instance

/sync (name) ← this will sync you with player and keep you syncing with them until you do -
/unsync

  1. What is the issue?

I can’t figure out how it is possible to keep the player syncing with the other player, if they change their dance.

  1. What solutions have you tried so far?

I HAVE A FULLY WORKING SYNC SYSTEM ALREADY.

Only issue, is I haven’t added the detection to automatically sync them if they change the dance, and they are still synced up with someone.

I was going about with this idea →

When a player joins the game :

local SyncValue = Instance.new("StringValue") -- Creating String Value
	SyncValue.Name = "SyncValue"
	SyncValue.Parent = localplr
	SyncValue.Value = nil -- Default Nil

When the player does /sync :

if player2 and player2:IsA("Player") then
   SyncValue.Value = player2.Name -- Sync Value set to Player Name	
   require(script.ModuleSync):Sync(localplr,player2)
end

When the player does /unsync :

SyncValue.Value = nil -- Sync Value set to Default

How could I go about, checking if the value is the targets name and seeing if it changes. If the player2 changes their dance, then it will change the player1 dance as will (IF THEY ARE STILL IN SYNC).

Would this be done using WhileTrueDo in another script? My knowledge is short on this. Could this help?

3 Likes

What I would do is

Give every player an animation folder and under the attributes put
•animationId

Player syncs with another

Player starts an attribute changed event of the players animations folder

Attribute changed then the player starts the same animation

I would have a SyncValue, being in the player that got synced, and have the value being the player that syncs to that player.

I would probably be using Animator.AnimationPlayed to detect animation playing, and then stop the animation the player that sync is currently playing, and instead replace them with the animation track Animator.AnimationPlayed returned.

-- Player that got sync's Animator
Animator.AnimationPlayed:Connect(function(animationtrack) -- fire when new animation played
    if Player.SyncValue.Value then  -- check if anyone is syncing to this person
        local player = Player:FindFirstChild(SyncValue.Value)  -- find synched player
        if player and player.Character then  -- check if there is character
            local syncAnimator = player.Character.Humanoid.Animator  -- animator of synched person
            for _,v in ipairs(syncAnimator:GetPlayingAnimationTracks()) do  -- stopping all animation
                v:Stop()
            end
            syncAnimator:LoadAnimation(animationtrack.Animation):Play()
        end
    end
end)
2 Likes

But you don’t need to stop all animations, do you? I’m pretty sure it overrides any current one playing

It should be if the priority is the same or higher but just in case. Stopping all the animation is probably not needed and you could remove that if you want.

2 Likes

That’s cool. I didn’t know you could do an animation played event until now, thanks

I like the idea your going about this. Never thought you could use AnimationPlayed

Ill see how I can implement this code, in the mean time thank you.

--SERVER

local players = game:GetService("Players")
local replicated = game:GetService("ReplicatedStorage")
local remote = replicated.RemoteEvent

local function onPlayerAdded(player)
	local sync = Instance.new("ObjectValue")
	sync.Name = "Sync"
	sync.Parent = player
	
	local function onPlayerChatted(message)
		local splitMessage = message:lower():split(" ")
		if #splitMessage >= 2 then
			if splitMessage[1]:match("^/sync$") then
				local syncedPlayers = {}
				for _, otherPlayer in ipairs(players:GetPlayers()) do
					if otherPlayer ~= player then
						if otherPlayer.Name:lower():match(splitMessage[2]) then
							table.insert(syncedPlayers, otherPlayer)
						end
					end
				end
				
				if #syncedPlayers == 1 then
					local syncedPlayer = syncedPlayers[1]
					sync.Value = syncedPlayer
				end
			end
		end
	end
	
	local function onCharacterAdded(character)
		local function onAnimationPlayed(animationTrack)
			for _, syncedPlayer in ipairs(players:GetPlayers()) do
				if syncedPlayer ~= player then
					if syncedPlayer.Sync.Value == player then
						local syncedCharacter = syncedPlayer.Character
						local syncedHumanoid = syncedCharacter.Humanoid
						if syncedHumanoid.Health > 0 then
							print(animationTrack.Animation)
							remote:FireClient(syncedPlayer, animationTrack.Animation.AnimationId)
						end
					end
				end
			end
		end
		
		local humanoid = character:WaitForChild("Humanoid")
		local animator = humanoid:WaitForChild("Animator")
		animator.AnimationPlayed:Connect(onAnimationPlayed)
	end
	
	player.Chatted:Connect(onPlayerChatted)
	player.CharacterAdded:Connect(onCharacterAdded)
end

players.PlayerAdded:Connect(onPlayerAdded)
--LOCAL

local replicated = game:GetService("ReplicatedStorage")
local remote = replicated:WaitForChild("RemoteEvent")
local players = game:GetService("Players")
local player = players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")
local animation = Instance.new("Animation")

local function onRemoteFired(animationId)
	animation.AnimationId = animationId
	local animationTrack = animator:LoadAnimation(animation)
	repeat task.wait() until animationTrack.Length > 0
	animationTrack:Play()
end

remote.OnClientEvent:Connect(onRemoteFired)

I just wrote this system which achieves this (shared/synced animations). This implementation makes use of a single RemoteEvent instance placed inside the ReplicatedStorage folder.

7 Likes

This seems to be a great fix to this, though how am I able to make them /unsync? I tried importing this into my code, and having troubles. Seems like the sync is working but its still not playing the animations correctly.

StarterPlayerScripts -

--[[ ENABLE GUI ]]--

local StarterGui = game:GetService("StarterGui") -- StarterGui
StarterGui:SetCore("AvatarContextMenuEnabled", true) -- Enabling AvatarContextMenu
StarterGui:SetCore("RemoveAvatarContextMenuOption", Enum.AvatarContextMenuOption.Emote) -- Disabling AvatarContextMenuEmote

--[[ GUI FUNCTION ]]--

local be = Instance.new("BindableEvent") -- New Event

local function sync(t) -- New Function To Fire Server (Sync)
	game.ReplicatedStorage.EVENTS:FindFirstChild("Sync"):FireServer(t)
end

be.Event:Connect(sync) -- Fires server when clicked

--[[ CREATING SYNC CURRENT DANCE BUTTON ]]--

StarterGui:SetCore("AddAvatarContextMenuOption", {"Sync Current Dance", be}) -- Adding Sync Dance Context
local replicated = game:GetService("ReplicatedStorage")

local remote = replicated.EVENTS:WaitForChild("NewSync")

local players = game:GetService("Players")

local player = players.LocalPlayer

local character = player.Character or player.CharacterAdded:Wait()

local humanoid = character:WaitForChild("Humanoid")

local animator = humanoid:WaitForChild("Animator")

local animation = Instance.new("Animation")

local function onRemoteFired(animationId)

animation.AnimationId = animationId

local animationTrack = animator:LoadAnimation(animation)

repeat task.wait() until animationTrack.Length > 0

animationTrack:Play()

end

remote.OnClientEvent:Connect(onRemoteFired)


ServerScriptService -

--[[
	@author TwinPlayzDev_YT
	@since 2/21/2022
	This script will let players sync animations with other players.
	Place this script in ServerScriptService.
--]]

--[ SERVICES ]--

local Players = game:GetService("Players")

--[ MAIN LOCALS ]--

local PlayerUsage = {} -- Player Table
local syncedPlayers = {}
local player2

local replicated = game:GetService("ReplicatedStorage")
local remote = replicated.EVENTS.NewSync

--[ FUNCTIONS ]--

game.Players.PlayerAdded:Connect(function(localplr)
	
	local sync = Instance.new("ObjectValue")
	sync.Name = "Sync"
	sync.Parent = localplr
	
	coroutine.resume(coroutine.create(function()
		localplr.Chatted:Connect(function(msg)
			
			local lowerString = string.lower(msg)
			local args = string.split(lowerString," ")
			
			if args[1] == "/sync" then
				
				for _, otherPlayer in ipairs(Players:GetPlayers()) do
					if otherPlayer ~= localplr then
						if otherPlayer.Name:match(args[2]) then
							table.insert(syncedPlayers, otherPlayer)
							require(script.ModuleSync):Sync(localplr,otherPlayer)
						end
					end
				end

				if #syncedPlayers == 1 then
					local syncedPlayer = syncedPlayers[1]
					sync.Value = syncedPlayer
				end
				
			elseif args[1] == "/unsync" then
				
				for _, otherPlayer in ipairs(Players:GetPlayers()) do
					if otherPlayer ~= localplr then
						if otherPlayer.Name:match(args[2]) then
							table.remove(syncedPlayers, otherPlayer)
						end
					end
				end
				
			end
			
		end)
	end))
	
	local function onCharacterAdded(character)
		local function onAnimationPlayed(animationTrack)
			for _, syncedPlayer in ipairs(Players:GetPlayers()) do
				if syncedPlayer ~= localplr then
					if syncedPlayer.Sync.Value == localplr then
						local syncedCharacter = syncedPlayer.Character
						local syncedHumanoid = syncedCharacter.Humanoid
						if syncedHumanoid.Health > 0 then
							print(animationTrack.Animation)
							remote:FireClient(syncedPlayer, animationTrack.Animation.AnimationId)
						end
					end
				end
			end
		end

		local humanoid = character:WaitForChild("Humanoid")
		local animator = humanoid:WaitForChild("Animator")
		animator.AnimationPlayed:Connect(onAnimationPlayed)
	end

	localplr.CharacterAdded:Connect(onCharacterAdded)

end)

game.ReplicatedStorage.EVENTS.Sync.OnServerEvent:Connect(function(player, player2)
	
	if player2 and player2:IsA("Player") then
		
		table.insert(syncedPlayers, player)
		
		local syncValue = player:FindFirstChild("Sync")
		
		if #syncedPlayers == 1 then
			local syncedPlayer = syncedPlayers[1]
			syncValue.Value = syncedPlayer
		end
		
		local now = tick()
		local LastUsed = PlayerUsage[player] or 0
		if now - LastUsed > 3 then
			PlayerUsage[player] = now
			require(script.ModuleSync):Sync(player,player2)
		end
		
	end
	
end)


Any idea about this?

1 Like

I apologise for never responding but it should be as simple as adding the following.

elseif splitMessage[1]:match("^/unsync$") then
	if sync.Value.Name:lower():match(splitMessage[2]) then
		sync.Value = nil
	end
end

This code simply sets the ‘sync’ value’s ‘Value’ to nil.

5 Likes