Oh cool alr, thx for letting me know. Il try that and if ut doest work maybe ill tey to pm the OP
yeah okay, i tried all types of CollisionFidelity, but none of them worked. Thx for trying to help though.
nvm i think i realized the reason why itās not properly working for me. The problem is that my enemy is too tall. Roblox added a feature long time ago where the humanoid root partās bottom aligns with the hip of a rig. For that reason, when the character swings the sword sometimes it doesnāt hit the HumanoidRootPart of the enemy (only their legs) and doesnāt trigger HumanoidCollided.
This is weird. HumanoidCollided should (to my limited knowledge of ClientCast internal workings) fire when any of the characterās parts were collided, not only the root part.
You should definitely PM the creator a repro file to find out if it is either a bug in ClientCast or a setup error.
I figured out my problem, the meshparts had CanQuery off. It was not showing me the option because CanCollide was on.
So Iām having an issue where if I set the owner of a cast to a player then if they have a frame drop then it is a lot less accurate and having itās owner to the client in general seems to make it a lot less accurate.
The way I solved this was by setting the owner of the cast to the server and the cast is way more accurate and consistent now though it created another problem. Now if you start running or moving quickly then use your weapon the cast is coming from where the server sees your character rather than where you see your character.
Is there a way to make it run on the client with the accuracy of the server?
Not even sure if what Iām asking is even possible or makes much sense.
Thatās not possible, no. The reason the client isnāt as accurate is because the client isnāt running at 60 FPS like the server - my best advice would be to try checking if your game has any performance issues on the client, and fix them.
Hello, Iām having a fairly big issue with the module. The caster works perfectly fine when a player first joins the game and spawns, however when the player resets or dies any future use of a new Caster will cause an error in the ClientCast module:
It seems like an issue with the reference of the old caster not being cleared, which is weird because I use the :Destroy() method on the ClientCaster object, which should completely remove the object and all its connections according to the API on the ClientCast wiki. It also doesnāt happen every single time the player dies, it only happens half the time, which just confuses me even more.
Hereās the rest of the code (for reference, this is a Module Script):
----- Services -----
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local Debris = game:GetService("Debris")
----- Modules -----
local ClientCast = require(ServerScriptService.ClientCast)
----- Variables -----
local PlayerList = {} -- PlayerName = {Caster, NextSwing, LastAttack, LastParry, LastFeint, FeintPossible, Feinting, Debounce, HitPlayers}
----- Functions -----
local function ClaymoreUnequip(Player)
if PlayerList[Player] ~= nil then
PlayerList[Player].Caster:Destroy() -- Destroys Caster to prevent memory leak
task.wait()
PlayerList[Player] = nil
end
end
local function ClaymoreEquip(Player, Tool)
if PlayerList[Player] == nil then
local Character = Player.Character
if Character == nil then return end
local Humanoid = Character:FindFirstChild("Humanoid")
if Humanoid == nil or Humanoid.Health == 0 then return end
local RayParams = RaycastParams.new()
RayParams.FilterDescendantsInstances = Character:GetDescendants()
RayParams.FilterType = Enum.RaycastFilterType.Blacklist
local ClientCaster = ClientCast.new(Tool:FindFirstChild("Blade"), RayParams)
ClientCaster:SetOwner(Player)
ClientCaster.HumanoidCollided:Connect(function(RaycastResult, HitHumanoid)
if PlayerList[Player].HitPlayers[HitHumanoid] then return end
local EnemyRootPart = HitHumanoid.Parent.PrimaryPart
local PlayerRootPart = Character.PrimaryPart
if (EnemyRootPart.CFrame.Position - PlayerRootPart.CFrame.Position).Magnitude > 11 then return end
PlayerList[Player].HitPlayers[HitHumanoid] = true
HitHumanoid:TakeDamage(22)
EnemyRootPart:ApplyImpulse((EnemyRootPart.CFrame.Position - Character.PrimaryPart.CFrame.Position).Unit * 2000)
task.wait(0.3)
PlayerList[Player].HitPlayers[HitHumanoid] = false
end)
Humanoid.Died:Connect(function() -- Removes player from table when they die to free memory
ClaymoreUnequip(Player)
end)
PlayerList[Player] = {Caster = ClientCaster, NextSwing = 1, LastAttack = 0, LastParry = 0, LastFeint = 0, FeintPossible = false, Feinting = false, Debounce = false, HitPlayers = {}}
end
end
local function ClaymoreAttack(Player, Tool)
if PlayerList[Player].Debounce == true then return end
PlayerList[Player].Debounce = true
if os.clock() - PlayerList[Player].LastAttack > 0.5 then
if os.clock() - PlayerList[Player].LastAttack < 1.5 or os.clock() - PlayerList[Player].LastFeint < 0.5 then
PlayerList[Player].Debounce = false
return
end
PlayerList[Player].NextSwing = 1
end
if PlayerList[Player].NextSwing == 1 then
PlayerList[Player].NextSwing = 2
task.wait(.25)
PlayerList[Player].Caster:Start()
task.wait(.25)
elseif PlayerList[Player].NextSwing == 2 then
PlayerList[Player].NextSwing = 3
task.wait(.16)
PlayerList[Player].Caster:Start()
task.wait(.25)
elseif PlayerList[Player].NextSwing == 3 then
PlayerList[Player].NextSwing = 2
task.wait(.25)
PlayerList[Player].Caster:Start()
task.wait(.25)
end
PlayerList[Player].Caster:Stop()
PlayerList[Player].Debounce = false
end
----- Connections -----
Players.PlayerRemoving:Connect(function(Player)
if PlayerList[Player] ~= nil then
PlayerList[Player].Caster:Destroy()
PlayerList[Player] = nil
end
end)
ClaymoreModule = {}
function ClaymoreModule:StartAction(Player, ActionType)
local Character = Player.Character
if Character then
if ActionType == "Unequipping" then
ClaymoreUnequip(Player)
else
local Claymore = Character:WaitForChild("Claymore", 1)
if Claymore then
if ActionType == "Equipping" then
ClaymoreEquip(Player, Claymore)
elseif ActionType == "Attacking" then
ClaymoreAttack(Player, Claymore)
elseif ActionType == "Parrying" then
-- ClaymoreParry(Player, Claymore)
elseif ActionType == "Feinting" then
-- ClaymoreFeint(Player, Claymore)
end
end
end
end
end
return ClaymoreModule
Any help would be appreciated and bear with me please, as Iām not an extremely experienced coder.
PS: I also noticed something peculiar when playing around with printing:
print("Destroying players caster!")
PlayerList[Player].Caster:Destroy()
print("Players caster destroyed!")
task.wait()
print(PlayerList[Player].Caster)
PlayerList[Player] = nil
The output:
Maybe Iām not well-versed enough to understand why this is happening?
The provided code is a bit large - I recommend making a more minimal reproduction script, such as creating a new baseplate and adding the minimal amount of code needed to reproduce that bug.
local Players = game:GetService("Players")
local ClientCast = require(game:GetService("ServerScriptService").ClientCast)
local RemoteEvent = game:GetService("ReplicatedStorage").RemoteEvent
local Table = {} -- Player = Caster
local function StartCasting(Player)
Table[Player].Caster:Start()
task.wait(10)
Table[Player].Caster:Stop()
end
Players.PlayerAdded:Connect(function(Player)
Player.CharacterAdded:Connect(function(Character)
local Humanoid = Character:WaitForChild("Humanoid", 1)
local ClientCaster = ClientCast.new(workspace.ExamplePart1, RaycastParams.new())
ClientCaster:SetOwner(Player)
ClientCaster:StartDebug()
ClientCaster.HumanoidCollided:Connect(function(RaycastResult, HitHumanoid)
print("Hit model:" .. HitHumanoid.Parent.Name)
end)
Table[Player] = {Caster = ClientCaster}
Humanoid.Died:Connect(function()
Table[Player].Caster:Destroy()
task.wait()
Table[Player] = nil
end)
end)
end)
RemoteEvent.OnServerEvent:Connect(StartCasting)
This is the smallest I could get while keeping the structure relatively the same (hopefully thatās enough). This still causes the same error I posted above whenever the player dies and gets a new table entry and caster. This code is also no longer in a Module Script, so itās not caused by that either. I also noticed now that the error only appears when the HumanoidCollided event is fired, not when casting starts or stops.
At what intervals is the remote fired? Could you replace it with a manual loop?
The remote was fired manually by me via UserInputService on a local script, but hereās the same script with a loop instead:
local Players = game:GetService("Players")
local ClientCast = require(game:GetService("ServerScriptService").ClientCast)
local RemoteEvent = game:GetService("ReplicatedStorage").RemoteEvent
local Table = {} -- Player = Caster
local function StartCasting(Player)
while Table[Player] ~= nil do
print("Started casting")
Table[Player].Caster:Start()
task.wait(5)
if Table[Player] ~= nil then
print("Stopped casting")
Table[Player].Caster:Stop()
task.wait(5)
end
end
end
Players.PlayerAdded:Connect(function(Player)
Player.CharacterAdded:Connect(function(Character)
local Humanoid = Character:WaitForChild("Humanoid", 1)
local ClientCaster = ClientCast.new(workspace.ExamplePart1, RaycastParams.new())
ClientCaster:SetOwner(Player)
ClientCaster:StartDebug()
ClientCaster.HumanoidCollided:Connect(function(RaycastResult, HitHumanoid)
print("Hit model:" .. HitHumanoid.Parent.Name)
end)
Table[Player] = {Caster = ClientCaster}
task.spawn(function()
StartCasting(Player)
end)
Humanoid.Died:Connect(function()
Table[Player].Caster:Destroy()
task.wait()
Table[Player] = nil
end)
end)
end)
For some reason this doesnāt cause the same error. Unfortunately thatās not exactly helpful to me, as I canāt rewrite the script without knowing what even causes the error in the first placeā¦
v1.13.0
- Remove
ClientCaster:GetPing
- Remove
ClientCaster:GetMaxPingExhaustion
- Remove
ClientCaster:SetMaxPingExhaustion
-
- Sidenote: You should use
Player:GetNetworkPing()
now instead.
- Sidenote: You should use
- Update raycast data serialization to take up less space
I read all the introductions and comments, but found that only attachment can be used instead of bone. Itās a pity. I look forward to adding bones
Hey! Adding support for bones is definitely possible; Iāll look into it in the nearby future.
God, Iām so glad that you added this so soon. I tried to use RaycastHitboxV4 before. As you said, it also has server latency problems. I will use the new version immediately and give you feedback. Thank you for updating so quickly
Hi, my friend, did you submit the wrong version? I donāt think the code supports bone. In the OnDamagePointAdded function, there is only Attachment
hi,friendļ¼I changed your module to support bones, and then tested it in the game. I think ClientCast is better than the RaycastHitboxV4 (because the Animation Backtracks of your module will not have much server delay, and the performance is very good,This should be the best melee attack system Iāve ever used). I hope you can continue to optimize