How would I make a fireball that is synced with client and server?

I am trying to make a fireball where it increases in size when the the player holds down the left mouse button. I am not sure of a good/better way to do this. I have attached what I have tried below. It does not seem to work.

ServerScript

script.Parent.OnServerEvent:Connect(function(player, Name, partyID, character, mouse)
	if Name == "Charge" then
		character.HumanoidRootPart.Anchored = true
		wait(0.1)
		fireball = Instance.new('Part')
		fireball.Anchored = true
		fireball.Parent = game.Workspace.Projectiles
		fireball.CFrame = CFrame.new(-3, 0, 0) * character.HumanoidRootPart.CFrame
		fireball.Shape = "Ball"
	elseif Name == "Fire" then
		print('fire')
		fireball.Anchored = false
		local BodyVelocity = Instance.new("BodyVelocity")
		BodyVelocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
		BodyVelocity.Velocity = mouse.LookVector * 250
		BodyVelocity.Parent = fireball
		character.HumanoidRootPart.Anchored = false
	elseif Name == "Grow" then
		wait()
		fireball.Size = fireball.Size + Vector3.new(0.2, 0.2, 0.2)
	end
end)

Local Script

local tool = script.Parent
local mouse = game.Players.LocalPlayer:GetMouse()
local debounce = false
local held = false
local UIS = game:GetService("UserInputService")

tool.Equipped:Connect(function()
	print('eq')
	UIS.InputEnded:Connect(function(Input, gameProcessedEvent)
		local keycode = Input.UserInputType
			if not gameProcessedEvent then
				if keycode == Enum.UserInputType.MouseButton1 and debounce == true then
					tool.Fireball:FireServer("Fire", game.Players.LocalPlayer.Backpack.PartyID.Value, game.Players.LocalPlayer.Character, mouse.Hit)
					debounce = false
					held = false
			end
		end
	end)
	UIS.InputBegan:Connect(function(Input, gameProcessedEvent)
		local keycode = Input.UserInputType
			if not gameProcessedEvent then
				if keycode == Enum.UserInputType.MouseButton1 then
					debounce = true
					held = true
					tool.Fireball:FireServer("Charge", game.Players.LocalPlayer.Backpack.PartyID.Value, game.Players.LocalPlayer.Character, mouse.Hit)
					while held do
						wait()
						tool.Fireball:FireServer("Grow", game.Players.LocalPlayer.Backpack.PartyID.Value, game.Players.LocalPlayer.Character, mouse.Hit)
					end
				end
			end
	end)
end)

Any help/feedback is appreciated!

I would recommend re-coding this a bit. I don’t think it’s a good idea to fire a remote event with a wait() loop because that would fire roughly 30 times a second which is a bit excessive.

So instead of doing:

while held do 
	wait() 
	--fire remote 
end 

I would recommend removing the above portion of code entirely, and instead stopping the growing portion when “Fire” is received from the server. In other words, the fireball should only grow between the time it’s created and the time it’s fired, so there isn’t much of a reason to constantly tell it to grow when there’s already two events which you can use to indicate when it should grow. Hope this helps clean up some code.

As for the script not working entirely, the only potential problem I can see at a glance is not relocating the fireball to its previous CFrame on the server-side script as it grows. When you resize a part, its position will be sensitive to collisions, so it’ll ‘teleport’ on top of anything it’s inside of. To fix this isn’t too difficult:

local cf = fireball.CFrame 
fireball.Size = fireball.Size + Vector3.new(0.2,0.2,0.2) 
fireball.CFrame = cf 

Lastly, a problem might be the fact that you’re connecting the .InputEnded and .InputBegan each time the tool is equipped. If you only want the script to be ‘active’ when the tool is equipped, you can do:

local equipped = false 

tool.Equipped:Connect(function() 
	equipped = true 
end) 

tool.Unequipped:Connect(function() 
	equipped = false 
end) 

And then use ‘equipped’ to determine whether or not the code in the .InputBegan and .InputEnded should run.

Hope this helps, and sorry if it’s a lot to read. If you’re confused, I’ll be happy to provide additional help.

2 Likes

Alright! So, I took a totally different approach. However, the fireball does not seem to shoot after I release the mouse button. Here’s the script now.

local pressing = {}
local playersFireball
local maxSize = Vector3.new(5,5,5)

function KeyPress(player, state)
	print('okay')
	if state == "Began" then
		KeyDown(player)
		print('began')
	else
		KeyUp(player)
		print('ended')
	end
end

function KeyUp(player)
	if (pressing[player]) then
		pressing[player] = false
		print('up')
		local fireball = playersFireball
		fireball.Anchored = false
		player.Character.Humanoid.WalkSpeed = 16
	end
end

function KeyDown(player)
	if (not pressing[player]) then
		pressing[player ] = true
		local hrp = player.Character.HumanoidRootPart
		local fireball = Instance.new("Part")
		fireball.Parent = workspace.Projectiles
		fireball.Shape = "Ball"
		fireball.Size = Vector3.new(0, 0, 0)
		fireball.CanCollide = false
		fireball.Anchored = true
		fireball.CFrame = hrp.CFrame*CFrame.new(0, 0, -5)
		local damage = Instance.new("NumberValue");
		damage.Name = "Damage"
		damage.Value = 0
		damage.Parent = fireball
		local bv = Instance.new("BodyVelocity")
		bv.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
		bv.Velocity = hrp.CFrame.lookVector * 250
		bv.Parent = fireball
		playersFireball = fireball
		player.Character.Humanoid.WalkSpeed = 0
		while (pressing[player]) do
			print(pressing)
			fireball.Size = fireball.Size + Vector3.new(0.1,0.1,0.1)
			damage.Value = damage.Value + 10
			if fireball.Size.X >= maxSize.X then
				KeyUp(player)
			end
			wait(0.1)
		end
	end
end

script.Parent.OnServerEvent:Connect(KeyPress)

The while loop does not stop for some reason, only when it reaches the max size. It also doesn’t work when I just simply click and don’t hold the mouse button. When I click, the part just continues to increase in size.

I believe the problem might be with your local script. I wrote my own local script and tested the server script, it seemed to work fine for me. I’d normally ask to see the local script, but since I already wrote one, I’ll just post mine here and hopefully you can compare it to yours and resolve the issue:

local remote = script.Parent:WaitForChild("RemoteEvent") 

local userinput = game:GetService("UserInputService") 

userinput.InputBegan:Connect(function(input) 
	
	if input and input.UserInputType == Enum.UserInputType.MouseButton1 then 
		
		remote:FireServer("Began") 
		
	end 
	
end) 

userinput.InputEnded:Connect(function(input) 
	
	if input and input.UserInputType == Enum.UserInputType.MouseButton1 then 
		
		remote:FireServer("Ended") 
		
	end 
	
end) 

The script I wrote isn’t the cleanest and I didn’t bother to add the GameProcessedEvent since it was just for testing purposes, so I’d recommend still using your own script and just referring to this to see why maybe yours doesn’t work.

Oh, I found the problem! Another problem popped up though…it just Anchors my character when I just click. I’m not sure why.

Could you post your local script? I don’t see anywhere in the server script which anchors your character, so unless your fireball is somehow anchoring your character, I don’t see how your character is being anchored in the server script.

Oops. I meant setting walkspeed to 0. The fireball part just stays there if I just simply click and the walkspeed stays 0.

This doesn’t happen to me, either. It might be something to do with the .Equipped in your local script since I didn’t bother adding that to mine, either.

Here are my scripts. I changed the server script a bit.
Server:

local pressing = 0
local playersFireball
local maxSize = Vector3.new(5,5,5)

function KeyPress(player, state, mouse)
	print('okay')
	if state == "Began" then
		KeyDown(player, mouse)
		print('began')
		wait()
	else
		KeyUp(player)
		print('ended')
	end
end

function KeyDown(player, mouse)
	if pressing == 0 then
		pressing = 1
		local hrp = player.Character.HumanoidRootPart
		hrp.Anchored = true
		wait(0.5)
		local fireball = Instance.new("Part")
		fireball.Parent = workspace.Projectiles
		fireball.Shape = "Ball"
		--fireball.Size = Vector3.new(0.1, 0.1, 0.1)
		fireball.CanCollide = false
		fireball.Anchored = true
		fireball.CFrame = hrp.CFrame*CFrame.new(0, 0, -5)
		local damage = Instance.new("NumberValue");
		damage.Name = "Damage"
		damage.Value = 0
		damage.Parent = fireball
		local bv = Instance.new("BodyVelocity")
		bv.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
		bv.Velocity = mouse.lookVector * 250
		bv.Parent = fireball
		playersFireball = fireball
		player.Character.Humanoid.WalkSpeed = 0
		while pressing == 1 do
			print(pressing)
			fireball.Size = fireball.Size + Vector3.new(0.1,0.1,0.1)
			damage.Value = damage.Value + 10
			fireball.CFrame = hrp.CFrame*CFrame.new(0, 0, -5)
			bv.Velocity = mouse.lookVector * 250
			if fireball.Size.X >= maxSize.X then
				KeyUp(player)
			end
			wait(0.1)
		end
	end
end

function KeyUp(player)
	if pressing == 1 then
		pressing = 0
		print('up')
		local fireball = playersFireball
		fireball.Anchored = false
		player.Character.HumanoidRootPart.Anchored = false
		player.Character.Humanoid.WalkSpeed = 16
	end
end

script.Parent.OnServerEvent:Connect(KeyPress)

Local:

local tool = script.Parent
local mouse = game.Players.LocalPlayer:GetMouse()
local debounce = true
local held = false
local UIS = game:GetService("UserInputService")
local equip = false

tool.Equipped:Connect(function(mouse)
	equip = true
end)

UIS.InputEnded:Connect(function(Input, gameProcessedEvent)
	if not gameProcessedEvent and equip == true then
		local KeyCode = Input.UserInputType
		if KeyCode == Enum.UserInputType.MouseButton1 then
			script.Parent.Fireball:FireServer("Ended", mouse.Hit)
		end
	end
end)
	
UIS.InputBegan:Connect(function(Input, gameProcessedEvent)
	if not gameProcessedEvent and equip == true then
		local KeyCode = Input.UserInputType
		if KeyCode== Enum.UserInputType.MouseButton1 and debounce == true then
			debounce = false
			script.Parent.Fireball:FireServer("Began", mouse.Hit)
			wait(3) debounce = true
		end
	end
end)
	
tool.Unequipped:Connect(function()
	equip = false
end)

What it prints when I just click:

okay(x2)
up
Players.Math_Solvers.Backpack.Fireball.Fireball.Script:59: attempt to index local 'fireball' (a nil value)
began

You could add a conditional statement to ensure fireball exists on line 59 to fix the error in the output:

if fireball then 
	--code 
end 

But it does exist. The mouse up is firing before the mouse down…

Your .InputEnded function doesn’t use debounce, so clicking twice in less than 3 seconds might could cause this error.

I added a debounce, but now the fireball just grows until it reaches the restricted size.

local tool = script.Parent
local mouse = game.Players.LocalPlayer:GetMouse()
local debounce = true
local held = false
local UIS = game:GetService("UserInputService")
local equip = false

tool.Equipped:Connect(function(mouse)
	equip = true
end)

tool.Unequipped:Connect(function()
	equip = false
end)

UIS.InputBegan:Connect(function(Input, gameProcessedEvent)
	if not gameProcessedEvent and equip == true then
		local KeyCode = Input.UserInputType
		if KeyCode== Enum.UserInputType.MouseButton1 and debounce == true then
			debounce = false
			script.Parent.Fireball:FireServer("Began", mouse.Hit)
			wait(3) debounce = true
		end
	end
end)

UIS.InputEnded:Connect(function(Input, gameProcessedEvent)
	if not gameProcessedEvent and equip == true then
		local KeyCode = Input.UserInputType
		if KeyCode == Enum.UserInputType.MouseButton1 and debounce == true then
			debounce = false
			script.Parent.Fireball:FireServer("Ended", mouse.Hit)
			wait(3) debounce = true
		end
	end
end)
	

I should probably create a new thread. Will do that right now.
Edit: I have made the new thread.