Update 1.0.8.6
- If a connected client were to have several connected casters at once, a raycast event from the client would fire all active casters on the server which are owned by that client. Oops…
Is this good for spamming projectiles that are fast aswell?
I wouldn’t recommend ClientCast for this, as it’s mainly meant for melee hitboxes.
How would i activate raycasts for separate parts? For example, have the left fist activate it’s raycasts only, then on the second time the player clicks the right fist activates.
You can create separate Caster objects for each fist, and put damage attachments in each fist respectively.
Hey there!
So I’ve been using your module to make a sword system. Works pretty nice, however I noticed one thing.
Whenever I try to set the owner of the sword and do Caster:GetOwner()
, it prints out nil? And using Caster:GetPing()
prints out 0.
Did I mess something up?
local RS = game:GetService("ReplicatedStorage")
local PS = game:GetService("Players")
local player = PS.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
player.CharacterAppearanceLoaded:Wait()
local hum = char:FindFirstChildWhichIsA("Humanoid")
local animator = hum:FindFirstChildWhichIsA("Animator")
local tool = script.Parent
local blade = tool:WaitForChild("Blade")
local equip = blade:WaitForChild("Equip")
local request = RS.Sword.Events.Request
local validate = RS.Sword.Events.Validate
local idleAnim = nil
local animation = {
attack = {
9503532906,
9503922539,
9504030319
},
idle = 9504150874,
}
local function LoadAnimation(id, priority)
local anim = Instance.new("Animation") do
anim.AnimationId = "rbxassetid://" .. id
end
local load = animator:LoadAnimation(anim) do
load.Priority = priority
load:Play()
end
return load
end
tool.Equipped:Connect(function()
equip:Play()
idleAnim = LoadAnimation(animation.idle, Enum.AnimationPriority.Idle)
end)
tool.Unequipped:Connect(function()
if idleAnim then
idleAnim:Stop()
idleAnim = nil
end
end)
tool.Activated:Connect(function()
request:FireServer(tool)
end)
request.OnClientEvent:Connect(function()
LoadAnimation(animation.attack[math.random(1, #animation.attack)], Enum.AnimationPriority.Action)
end)
local RS = game:GetService("ReplicatedStorage")
local caster = require(RS.Sword.ClientCast)
local request = RS.Sword.Events.Request
local validate = RS.Sword.Events.Validate
local stat = {
Sword1 = {Damage = 10}
}
local debounce = {}
local function IsHitboxValid(hitPos: Vector3, swordPos: Vector3): boolean
local mag = (hitPos - swordPos).Magnitude
if mag > 8 then
return false
else
return true
end
end
local function CalculateHitbox(player, blade)
if not player or not blade then
return
end
local sword = caster.new(blade, RaycastParams.new())
local char = player.Character or player.CharacterAdded:Wait()
local debounce = {}
sword.HumanoidCollided:Connect(function(ray, hum)
local instance = ray.Instance
if instance:IsDescendantOf(char) or instance:IsDescendantOf(blade.Parent) or IsHitboxValid(ray.Position, sword.Object.Position) == false then
return
end
local info = stat[blade.Parent.Name]
local hit = blade:FindFirstChild("Hit")
if debounce[hum] then
return
end
debounce[hum] = true
hit:Play()
hum:TakeDamage(info.Damage)
end)
task.spawn(function()
warn("old: ", sword:GetOwner())
sword:SetOwner(player)
warn("new: ", sword:GetOwner())
warn("ping: ", sword:GetPing())
sword:Start()
task.wait(1)
sword:Destroy()
end)
end
request.OnServerEvent:Connect(function(player: Player, tool: Tool)
if debounce[player] or not tool:FindFirstChild("Blade") then
return
end
local blade = tool:FindFirstChild("Blade")
debounce[player] = true
request:FireClient(player)
CalculateHitbox(player, blade)
task.wait(2)
debounce[player] = false
end)
I’ve been trying to use this for a few hours, and got a bug.
Whenever I first join the game, hitboxes are fine, but when I respawn and try to hit someone, the first hit registers, but then it just breaks and there are no errors, or it just registers a few hits.
Any ways to fix it?
Hey, sorry for the late response. What’s happening is that to make sure the :SetOwner
command is replicated to the client, :SetOwner
is actually wrapped in task.spawn
, which then yields for a maximum of 0.1 seconds. This is to make sure that by the time the SetOwner
command is sent to the client, the client already has a Caster object created.
If you want the caster’s object to be set to player
the moment it’s created, use the third argument of ClientCast.new
directly:
Sorry for the confusion!
EDIT: Due to how I structured the code, it should actually be fine to remove the yields in theory. I will update the module to remove the yields, which should fix your issue without having to do anything besides updating the module.
Please verify that this is indeed an issue with the ClientCast module, and not your code. ClientCast never indexes the Character
property in any of it’s code, meaning it’s extremely unlikely for the bug to be tied to ClientCast itself.
I was using Caster:SetOwner(Player), but once that I removed the set owner line of code, everything started working fine again.
But now the hitbox is server sided and not client sided as I’d like to.
I’m not sure what the issue might be, but I recommend creating a separate studio place, and try reproducing the bug with as few lines of code as possible. It’ll become much easier to identify the problem from there.
figured a lot out but now im running into a separate issue.
I realised that if my character stays in the same position after being hit, none of the other hits register. I troubleshooted a lot and it turns out that this is a problem with client cast itself.
ClientCast only raycasts, it has no debounces, cooldowns, and the like. I really don’t think ClientCast is the issue here, but if you’re sure that it is, please provide a file which reproduces your issue in as few lines as possible.
Small script that uses a tool to use clientcast. It works fine at first, but whenever I respawn, it stops working at all. This is a simple script and it should still be working even after respawning if im not wrong, I don’t think I used something the wrong way
local clientCast = require(game.ReplicatedStorage.ClientCast)
local hitbox = clientCast.new(script.Parent.Handle, RaycastParams.new(), script.Parent.Parent.Parent)
local alreadyHit = false
hitbox.Collided:Connect(function(params)
if alreadyHit then return end
if params.Instance.Parent:FindFirstChild("Humanoid") then
alreadyHit = true
params.Instance.Parent:FindFirstChild("Humanoid"):TakeDamage(10)
end
end)
local inCD = false
function attack()
if inCD then return end
inCD = true
task.spawn(function()
wait(1)
alreadyHit = false
inCD = false
end)
hitbox:Start()
spawn(function()
wait(.5)
hitbox:Stop()
end)
end
script.Parent.Activated:Connect(function()
attack()
end)
Could you make this a .rbxl file with the tool and code in it? Since I don’t know what the tool is.
Interesting catch! This seems to be a mix of buggy behavior between both ClientCast and Roblox itself - since the server can’t access Player.PlayerScripts
, it parents the ClientHandler LocalScript into PlayerGui to run the code, and then ClientHandler reparents itself into Player.PlayerScripts
. The deprecated StarterGui.ResetPlayerGuiOnSpawn
conflicts with this though, and purges any children that were parented into PlayerGui at any point in time, even if they were later reparented.
I’ll try finding a better workaround for this, but for now, you can fix this by running game:GetService("StarterGui").ResetPlayerGuiOnSpawn = false
in the command bar. Sorry for the inconvenience!
All good, I’m not sure how to fix it so I will wait for you to push a fix. I msut use resetplayerguionspawn because I have important guis that need to be resetted when respawned. Nice one on noticing the bug so fast haha
Hey! The issue should now be fixed on GitHub, though I’m reworking ClientCast a bit to support Rojo, along with adding a brand new documentation API so I won’t update the devforum thread for a bit.
You can get the newest update here for now:
https://github.com/PysephWasntAvailable/ClientCast
(cc. @Downrest, your issue should also be fixed now)
StarterGui.ResetPlayerGuiOnSpawn = false
. (cc. @XarkDev)