Great thank you! I tried setting up the Linked Attachments and in the end, the whole thing is starting to feel a little but Rube Goldberg for this use case.
The module is incredible as it is, but if I could add anything, it would be the ability to register a hit while the HitPoints are not in motion. Imagine a sword or lance stabbed through the target, doing damage continually as long as it is there.
I know it may seem obvious, but have you tried moving the part around? If rays are still not being drawn, make sure HitStop isn’t being written anywhere.
Uh, for some odd reason, my hitbox.onhit function is unable to detect boolean values that are set outside and above that function.
function module.Swing(AnimTable, Tool, Damage, UserHumanoid, Hitbox, Special)
local Handle = Tool:FindFirstChild("Handle")
if Hitbox then
local ChosenAnim = #AnimTable > 1 and AnimTable[math.random(1, #AnimTable)] or AnimTable[1]
local TouchConnection
ChosenAnim:Play()
print(Special)
ChosenAnim:GetMarkerReachedSignal("EnableHit"):Connect(function()
Hitbox:HitStart()
TouchConnection = Hitbox.OnHit:Connect(function(hit, Hum)
print(Special)
if Special == true then
print("YEE")
WeaponSpecials[Tool.Name](Hum)
end
Hum:TakeDamage(Damage)
end)
end)
ChosenAnim.Stopped:Connect(function()
Hitbox:HitStop()
end)
end
end
I’m currently learning how to script, and I’ve been trying to learn how to make effective hitboxes. I mostly used raycasting, and the way my hitboxes worked is basically by casting 1 ray every time the melee weapon is activated/used. I usually cast the ray from the player’s humanoidrootpart, and although it works, it’s sometimes inconsistent, and it’s hard to calculate how far I want the ray to be.
Basically, the way your raycast hitbox works is by placing attachments in a melee weapon, and then raycasting all of those attachments at once whenever the tool is activated? I’m really curious about this
Assuming you are talking about the ‘Special’ boolean, seems to be working fine for me. I am using the following setup to test:
Details
A server script inside the tool
local RaycastModule = require(script.Parent:WaitForChild("RaycastHitboxV3"))
local Schwing = require(script.Parent.Schwing)
local newHitbox, userHumanoid
script.Parent.Equipped:Connect(function()
userHumanoid = script.Parent.Parent:WaitForChild("Humanoid")
newHitbox = RaycastModule:Initialize(script.Parent, {script.Parent.Parent})
end)
script.Parent.Activated:Connect(function()
local Animation = Instance.new("Animation")
Animation.AnimationId = "rbxassetid://6017466008" --- Example
Schwing.Swing({userHumanoid:LoadAnimation(Animation)}, script.Parent, 5, userHumanoid, newHitbox, true)
end)
Same module as you are using
local module = {}
function module.Swing(AnimTable, Tool, Damage, UserHumanoid, Hitbox, Special)
local Handle = Tool:FindFirstChild("Handle")
if Hitbox then
local ChosenAnim = #AnimTable > 1 and AnimTable[math.random(1, #AnimTable)] or AnimTable[1]
local TouchConnection
ChosenAnim:Play()
print(Special)
ChosenAnim:GetMarkerReachedSignal("EnableHit"):Connect(function()
Hitbox:HitStart()
TouchConnection = Hitbox.OnHit:Connect(function(hit, Hum)
print(Special)
if Special == true then
print("YEE")
end
Hum:TakeDamage(Damage)
end)
end)
ChosenAnim.Stopped:Connect(function()
Hitbox:HitStop()
end)
end
end
return module
Ensure you are not fetching a hitbox that is created from a client or vice versa (ie. trying to fetch an initialized hitbox done by the client on the server).
Yes, each heartbeat frame will run an entire iteration on all cached attachments. There is a bit of misconception being that people think that this module has a predetermined length the rays start off with when in reality all it does is check positions between the last frame and the current frame and draws a ray in between. Thus, the length is determined entirely by the speed of your hitbox movement. If your hitbox is unmoving, the length will be exactly zero.
Hey, im curious as to how this works, and am seriously considering this for my game, however to my current understanding, when player swings, i have to call hitstart, and when the hit/animation ends, call hitstop. is this correct?
So should I create a connection and then disconnect it once getmarkerreached runs once? Because the first time it happens the hitbox.hit thing hasn’t run yet, but when it does the boolean is nil. (the first time it runs it’s true, times after that it’s nil). Keep in mind this happens in just one swing.
Correct, that is the basis for how to use it. Basically you would call HitStart/Stop whenever you want to turn on the hitbox (to detect enemies) and respectively, turn off the hitbox (to prevent hit detection). Now, people ask me what’s the best way to use it but honestly it depends on your own game. So at the end of the animation is ideal, also at the end of a swing as well, so its what you think feels right.
Sorry if I don’t understand, but could you clarify this point? It may be just late and I’m tired lol.
You can possibly flip the positions of HitStart and OnHit event (which is what I do and encourage to do as it basically guarantees the event to be created by the time the hit detection starts).
--- This order instead
Hitbox.OnHit:Connect()
-- blah
end)
Hitbox:HitStart()
Yeah, you can disconnect it once EnableHit is called, though might be best to disconnect it when the animation has stopped instead (the same place you put HitStop()) in case your animation has multiple EnableHits you want to utilize.
Hmm, not sure. I just tested it with the revised ordering (as indicated from my last post) and the boolean is detected, regardless of its actually nil or an actual value.
If you are still stuck and don’t mind sharing a little bit of your system with me, you can send a simple repo place with the problem and I’ll be happy to diagnose it for you.
Alright, so here’s what it’s outputting. First, it runs once and prints it as true (keep in mind I just swung it in the air) then it’s on the same line but somehow runs three more times even though the swing function has only run once, and it prints nil those last three times.
function module.Swing(AnimTable, Tool, Damage, UserHumanoid, Hitbox, Special)
local Handle = Tool:FindFirstChild("Handle")
if Hitbox then
local ChosenAnim = #AnimTable > 1 and AnimTable[math.random(1, #AnimTable)] or AnimTable[1]
local TouchConnection
ChosenAnim:Play()
print(Special)
ChosenAnim:GetMarkerReachedSignal("EnableHit"):Connect(function()
print(Special)
TouchConnection = Hitbox.OnHit:Connect(function(hit, Hum)
print(Special)
if Special == true then
print("YEE")
if WeaponSpecials[Tool.Name] then
WeaponSpecials[Tool.Name](Hum)
end
end
Hum:TakeDamage(Damage)
HitSound(Hum.Parent)
end)
Hitbox:HitStart()
end)
ChosenAnim.Stopped:Connect(function()
Hitbox:HitStop()
end)
end
end
Currently the output I’m speaking of is the line below ChosenAnim:GetMarkerReachedSignal
Sorry, was busy. I would recommend trying to replicate this in a separate, empty place with just the swing module and a simple tool and see if the problem persists (the problem does not show itself on my end at least).
Without video footage it’s a bit harder to visualize but I assume the Special gets printed once, then an additional 3 times (making it x4 in total). Again, I recommend disconnecting the GetMarkerReachedSignal once the animation stops (though admittedly I have not used that function a lot so it is maybe different from KeyframeReached).
There a few solutions that you can try.
Double check if there are multiple EnableHit markers on your animation by accident
Does the problem persist when you remove the OnHit event and leave it to print merely by reaching the EnableHit marker?
This problem seems to be out of the scope of this module so if there is still some persistent problems, I’ll be happy to help over in DMs instead.
@FerbZides Refer to the end of this post on how this module works, there is not really a determined length that you can easily change.
Though I guess if you want to scale up the distance (ie. overshoot the rays a little), you can go into the MainHandler script and go to around line 56:
local raycastResult = workspace:Raycast(rayStart, rayDir, Object.raycastParams)
You can probably do something like rayDir * LENGTH or something.