How to make client sided VFX with 2 separate characters animating?

Hello! I’m nipping the server-sided VFX habit in the bud right now before I get too far along in my scripting journey and have to relearn it all over again. The move I am currently working on has two separate effects, the initial snap and an animation that plays for the victim if someone is hit by the attack.

It is hard for me to really demonstrate but in the video below, imagine that the victim character plays an animation where they get lifted upwards and flung away (Or towards your direction because that’s how the ability works)


My concern is that I know there is an easy way to solve this by making another event that fires all clients if the hitbox on the server connects, but that sounds like it’s too easy to be the most optimal option.

Right now I am currently using 2 remote events, one that fires to the server after pressing Q which generates the hitbox and checks for cooldown and another event that fires every client to show the effects to them. Would adding a 3rd remote event hamper any performance or am I overthinking it and just need to shuffle some code around?
Screenshot_1
Alongside this! If there if there is a better solution AND there’s a better way to write this in a way that doesn’t use so many remote functions let me know! I’ve just been reading a lot of about them and heard that using too many isn’t the greatest thing.
Below is my VFX Handler (Local Script) and the script that generates the hitbox (Server Script)

Local Script:

local function onGravity(player, hitboxOffset, hitboxCFrame)
	
	local character = player.Character
	local hand = character:FindFirstChild("LeftHand")
	
	--local floating = victimCharacter.Humanoid:LoadAnimation(caughtAnim)
	local gravity = player.Character.Humanoid:LoadAnimation(fireAnim)
	
	local snapPart = Instance.new("Part")
	local snap = effectsFolder.GravityEffects:WaitForChild("Snap")
	local snapClone = snap:Clone()
	snapPart.Anchored = true
	snapPart.CanCollide = false
	snapPart.CanQuery = false
	snapPart.CanTouch = false
	snapPart.Transparency = 1
	snapPart.Size = Vector3.new(1,1,1)
	snapClone.Parent = snapPart
	
	local sparkle = effectsFolder.GravityEffects:WaitForChild("Sparks")
	local zap = effectsFolder.GravityEffects:WaitForChild("Main Core")
	local sparkleClone = sparkle:Clone()
	local zapClone = zap:Clone()
	zapClone.Parent = hand
	sparkleClone.Parent = hand
	
	gravity:Play()
	
	gravity:GetMarkerReachedSignal("GravEffect"):Connect(function()
		
		snapSound:Play()
		
		local visualizer = TER:WaitForChild("Visualizer")
		snapPart.CFrame = visualizer.CFrame
		snapPart.Parent = workspace
		
		task.wait(0.4)
		sparkleClone:Destroy()
		zapClone:Destroy()
		task.wait(0.5)

		snapPart:Destroy()
		
	end)
	
	
end
gravityVFXEvent.OnClientEvent:Connect(onGravity)

Server Script:

local function serverRecieved(player)

	print(player.Name)
	if table.find(debounce, player.Name) ~= nil then
		print("Under debounce")
	else
		print("Server Recieved")
		table.insert(debounce, player.Name)

		local character = player.Character
		local humanoid = character:FindFirstChild("Humanoid")
		local hrp = character:FindFirstChild("HumanoidRootPart")
		local characterDirection = hrp.CFrame.LookVector

		local parameters = OverlapParams.new()
		parameters.FilterType = Enum.RaycastFilterType.Exclude
		parameters.FilterDescendantsInstances = {character}

		local hitbox = hitboxModule.CreateHitbox()
		hitbox.Shape = Enum.PartType.Block
		hitbox.Size = Vector3.new(8, 8, 8)
		hitbox.CFrame = character.HumanoidRootPart
		hitbox.DetectionMode = "Default"
		hitbox.Offset = CFrame.new(0, 0, -20)
		hitbox.OverlapParams = parameters
		hitbox.Visualizer = true
		hitbox.VisualizerTransparency = 0.5
		hitbox.VelocityPrediction = true
		hitbox.VelocityPredictionTime = 0.2

		gravityVFXEvent:FireAllClients(player, hitbox.Offset, hitbox.CFrame)
		task.wait(0.4)
		hitbox:Start()
		
		hitbox.Touched:Connect(function(hit, humanoid)
	
			local victimCharacter = humanoid.Parent
			local victimPlayer = PLRS:GetPlayerFromCharacter(victimCharacter)
			local vrp = victimCharacter:FindFirstChild("HumanoidRootPart")
			local victimTorso = victimCharacter:FindFirstChild("LowerTorso")

			if victimCharacter then
				knockbackModule.knockbackAttributes(player, hrp, victimCharacter, vrp, -50, 0, 45, 0)
			end	
		end)
		
		task.wait(0.1)
		hitbox:Stop()

		task.wait(cooldown)
		debounce[table.find(debounce, player.Name)] = nil
	end


end

gravityEvent.OnServerEvent:Connect(serverRecieved)

I dont think you NEED two remotes, but overall this looks fine. Animations replicate to the server anyways. I recommend you watch the video below, as it uses the same but slightly improved system that you use.

Don’t worry too much about it, it looks good enough. Maybe just a few slight improvments (in the vid) so that you don’t have to redo your code later

Hi, thanks for the quick response!

I don’t think I fully explained it properly, or maybe I missed something in the video but I’ll try to explain it a little better.

The attack I’m doing can be used ANYWHERE and does not require a victim, the snap will always play. But if the snap DOES hit a victim, I want them to play an animation of them getting lifted upwards and have that replicated to everyone as well.

The issue is I don’t know how to fire that information to all clients to check if the hitbox hit a victim or not and am looking for how to achieve that.

Did you watch the video I sent you? Please do so, it will make life so much easier than what you are doing now.

Play the animation wherever you played the vfx. So you see where you refrenced the hitbox?

What you could do is for the vfx replicator to check for if anyone is inside the hitbox, if so then play the animation for anyone inside.

Or play it on server which isn’t recommended

ALSO, to reduce lag, refrence the hitbox once and then in the local script do .Offset and .Cframe

The more information you send the more it affects performance.

I haven’t heard from you in a while, so please mark it as solved if it has been.

I did watch the video. I actually ended up re-writing the hitbox detection because I was using a module script for it and didn’t fully understand how to implement it. The video you posted helped quite a bit after breaking it down further. Thanks!

1 Like

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