ClientCast - A Client-based, Idiosyncratic Hitbox System!

Just running the humanoid collided event with no internal checks, a humanoid is not always detected on the hit, even when they are clearly hit by the weapon/rays. If anyone else has experienced this, and/or has a possible solution, please let me know.

local Caster = ClientCast.new(tool.Blade, raycastParams)
	
local hitTable = {}
	
-- Connect callback to 'Collided' event	
Caster.HumanoidCollided:Connect(function(RaycastResult, HitHumanoid)
	print(HitHumanoid.Parent.Name)
	--Calcuate and deal damage based on weapon and active buffs/debuffs
	--DamageModule.DealDamageBasic(tool.Name, player, HitHumanoid, character, hitTable, toolInfo.Damage)
end)

-- Set owner of ClientCaster, who will be the one to calculate collisions.
-- You can skip this if you want the server to be the owner.
Caster:SetOwner(player)
-- Start detecting collisions
Caster:Start()
	
task.wait(toolInfo.Cooldown - 0.2)
-- Stop the caster object / collisions detections
Caster:Stop()
1 Like

I can’t really help without having a way to reproduce the issue. I recommend you try isolating the problem, and finding as consistent a repro as possible. This would help a lot in fixing the bug!

Well the issue I’m having is with me running the module bare bones, with nothing else in the back ground. I am however, accessing the module from the server, which honestly isn’t really a great idea for combat, and may be the cause of the discrepancies. I’ll try testing the system locally instead.

That’s the intended use of the module! It handles client <–> server communication for you, so you can just set up hitboxes on server, have them calculated on client, and sent back to server. Depending on how big the hitbox inaccuracy may be, perhaps it could be attributed to network lag?

Well the issue persists even in local play test servers against stationary player characters with fairly optimal performance. I’ll run some extensive tests when I have time.

Why should i use this and not this Raycast Hitbox 4.01: For all your melee needs!

You should know why if you were to read the thread.

1 Like

Not to be rude, it could simply be an issue with how I setup the system, but after moving over to the raycast hitbox, all hits were detected. If anyone else ends up having similar issues to what I was having I could maybe dig deeper and try to be a bit more helpful. One thing I will note is that I setup the hitboxes on the client using the rh4 module, over on the server as I did with yours.

Also i’ve seen others mention this; however, the detections seemed worse when I had attacking animations that caused the dmgpoints to intersect the player wielding the weapon. Hence, it may have been an issue with the module registering the attacker’s character and then jumping out of the hitdetection system, without continuing checks. (Just speculations though)

Well, as mentioned in the thread this module is meant to be very barebones in order to allow for maximum customization for the end user - this comes with a downside of being complex for those not as familiar, or to those who don’t need overly dynamic behavior.

Unsure if you’re referring to this module or rh4, but ClientCast has no built-in debounces, nor does it differentiate the weapon wielder from other targets. It shouldn’t stop raycasting when it hits a target, only when you explicitly call either:Stop or :Destroy.

game.Players.PlayerAdded:Connect(function(plr)
   plr.CharacterAdded:Connect(function(char)
      while true do
         local lastHRP = char.HumanoidRootPart.Position
         task.wait(2)
         if (char.HumanoidRootPart.Position - lastHRP).Magnitude > 20 then
            char:PivotTo(CFrame.new(lastHRP))
         end
      end
   end)
end)


anyway, I wanted smooth weapons and this is what i will use in the future, thanks

2 Likes

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.

1 Like

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?

image


Screenshots:

image

image

image


Client:

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)

Server:

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.

1 Like

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.