local RS = game:GetService("ReplicatedStorage")
local rE = RS:WaitForChild("RemoteEvent")
local SS = game:GetService("ServerStorage")
local sphere = SS:WaitForChild("Part")
while true do
task.wait(5)
for i, v in pairs(game:GetService("Players"):GetPlayers()) do
local char = v.Character or v.CharacterAdded:Wait()
local hrp = char:FindFirstChild("HumanoidRootPart")
local clonedSphere = sphere:Clone()
clonedSphere.Parent = workspace
clonedSphere.CFrame = workspace.cylinder.CFrame
task.wait(0.1)
rE:FireAllClients(clonedSphere, hrp)
task.wait(1)
clonedSphere.CFrame = hrp.CFrame
task.wait(0.3)
clonedSphere:Destroy()
end
end
And I have a local script in the StarterCharacterScripts
local RS = game:GetService("ReplicatedStorage")
local rE = RS:WaitForChild("RemoteEvent")
local TS = game:GetService("TweenService")
local function tween(object, charHrp)
local tweeningInfo = TweenInfo.new(1.5)
local goal = {CFrame = charHrp.CFrame}
TS:Create(object, tweeningInfo, goal):Play()
end
rE.OnClientEvent:Connect(function(object, charHrp)
tween(object, charHrp)
end)
So I was just testing on making boss fight attacks, I want to make the tween smooth but also no delay between the server and the client when the sphere moves. I searched up on Youtube and people tween it on the server so this won’t be a problem for them, but the tween will definitely lag.
Like when tweening the size of an expanding disc that damages players.
I also have a damage script in the sphere, and you can see that I need to synchronize between the server and the client for when the sphere moves. I literally got hit when I’m far away. I was wondering of using a remote function to fire from the server to client to server, but it is dangerous. Or I could tween it on the server? but it lags.
Synchronizing between the client and the server is tricky because there are many things to take into consideration. What I would do is:
Instead of the server cloning the sphere I would have the client clone it, although for security the server will still need to calculate the origin and destination for the sphere and send them to every client using FireAllClients
You’re already playing the Tween in the client which is good practice and will easily allow you to implement the suggestion above
The benefit of my suggestion is the more you find ways to lessen the load on the server and the less data you need to send, the smaller the delay you will have between the client and the server. My suggestion isn’t 100% perfect though as if a client has a poor network connection, they will still experience a noticeable delay before the sphere is created and tweened, but fortunately on average there shouldn’t be much issues
Wait, if I were to have an expandable damaging disc that is tweened on the client, I don’t want to depend the position of the disc on the client. Because, it is not moving at all on the server, not expanding too. So, what should I do on the server to get it work?
There’s no need to create the sphere in the server if you create it for each client. I’ll try to write an example code but I need to know what triggers the sphere to be created in the first place
-- When the server recieves the signal to send sphere data
local players = game:GetService"Players"
local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent
local origin = -- Where you want the sphere to originate from
local target = -- Where you want the sphere to go
for _, player in players:GetPlayers() do
remoteEvent:FireAllClients(player, origin, target)
end
LocalScript:
local tweenService = game:GetService"TweenService"
local tweenInfo = TweenInfo.new(1.5)
local sphere = script:WaitForChild"Sphere" -- The client won't find the sphere if it's left in ServerStorage!
game:GetService"ReplicatedStorage".RemoteEvent.OnClientEvent:Connect(function(origin, target)
local sphereClone = sphere:Clone()
sphereClone.CFrame = origin
sphereClone.Parent = workspace
local tween = tweenService:Create(sphereClone, tweenInfo, {CFrame = target})
tween:Play()
end)
I always recommend using WaitForChild in LocalScripts, unless you’re accesing something from ReplicatedStorage (I’m not 100% sure on this, so take caution but it’s working at the moment because Instances in RS seem to load first the moment the client joins the game) or your using script.Parent because the script won’t run if it’s ancestors haven’t loaded yet
The reason why it’s better to clone the sphere in the client is because for the server it requires more data and processing to send information about the sphere like color or size to every client instead of 2 CFrame values of where the sphere needs to start and go which will cause a delay depending on how many players are in your game
Depends on where they are, if the ModuleScript is inside ReplicatedStorage then yes but if it’s a child of a LocalScript that’s a child of a Gui for example then you will need to use WaitForChild to retrieve it
For the server though it’s a different story because everything is loaded the moment the game starts unless the object it needs to access is created by the client (in this case the server will never see it), created by a server script or has the chance of being destroyed when the script needs to access it so sometimes you need to use FindFirstChild
I don’t recommend using WaitForChild in server scripts because it could cause a ton of problems
No, there’s no need to move or tween the object on the server. As a matter of fact, the server won’t even know the sphere clone has been created but there’s no need to worry about this in your case
Since the object is being created on each client, if an exploiter deletes the sphere it will still exist unaltered for the other players, they’d only delete their own sphere
local origin = CFrame.new(0,4,0)
local target = CFrame.new(0,4,-28)
local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent
remoteEvent.OnServerEvent:Connect(function()
local result = workspace:Spherecast(origin.Position, 4, target.Position) -- Change 4 to radius of your sphere
if result then
local humanoid = result.Instance.Parent and result.Instance.Parent:FindFirstChildWhichIsA"Humanoid"
if humanoid then humanoid:TakeDamage(20) end
remoteEvent:FireAllClients(origin, CFrame.new(result.Position))
else
remoteEvent:FireAllClients(origin, target)
end
end)
Client:
local tweenInfo = TweenInfo.new(1.5)
local tweenService = game:GetService"TweenService"
local debris = game:GetService"Debris"
local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent
game:GetService"ContextActionService":BindAction("TEST", function(_, is)
if is == Enum.UserInputState.Begin then
remoteEvent:FireServer() -- Signal for server to start process
end
end, false, Enum.UserInputType.MouseButton1)
local sphere = script:WaitForChild"Part"
remoteEvent.OnClientEvent:Connect(function(origin, target)
local sphere = sphere:Clone()
sphere.CFrame = origin
sphere.Parent = workspace
local tween = tweenService:Create(sphere, tweenInfo, {CFrame = target})
tween:Play()
tween.Completed:Wait()
debris:AddItem(sphere, 0)
end)
Working on a way to delay the raycast to match the tween duration though
Oof I accidentally caused a chicken and the egg situation where I need the raycast data before the delay but the raycast needs to happen after the delay. The only way to solve this is to raycast twice and damage the humanoid in the second cast but it’s not an ideal solution
local origin = CFrame.new(0,4,0)
local target = CFrame.new(0,4,-28)
local remoteEvent = game:GetService"ReplicatedStorage".RemoteEvent
remoteEvent.OnServerEvent:Connect(function()
local result = workspace:Spherecast(origin.Position, 4, target.Position) -- Change 4 to radius of your sphere
remoteEvent:FireAllClients(origin, result and CFrame.new(result.Position) or target)
task.wait(1.5) -- Needs to match tween duration
local result = workspace:Spherecast(origin.Position, 4, target.Position) -- Same as above
if result then
local humanoid = result.Instance.Parent and result.Instance.Parent:FindFirstChildWhichIsA"Humanoid"
if humanoid then humanoid:TakeDamage(20) end
end
end)