Raycast Hitbox 4.01: For all your melee needs!

Here is the code for Server Sided:
local Tool = script.Parent
local Handle = Tool.Handle
local anim = Tool.SafeBox

local Light = Instance.new(“Highlight”)
Light.Enabled = false
Light.OutlineTransparency = 1

local RayCast = require(game.ServerScriptService.RaycastHitboxV4)
local HitBox = RayCast.new(Tool.Hitbox)

local de = false
local canhit = false

config = {
Damge = 20,
AttackTime = 0.002,
Cooldown = 0.002,
}

HitBox.OnHit:Connect(function(hit, Humanoid)
if canhit == false then return end
canhit = false
if Humanoid.Parent ~= script.Parent.Parent then
Humanoid:TakeDamage(config.Damge)
local char = Humanoid.Parent
Light.Parent = char

    Light.Enabled = true
    wait(0.086)
    Light.Enabled = false

    
    local Character = Humanoid.Parent
    local knockback = Instance.new("BodyVelocity")
    knockback.P = math.huge
    knockback.Parent = Character:FindFirstChild("HumanoidRootPart")
    knockback.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
    knockback.Velocity = Character:FindFirstChild("HumanoidRootPart").CFrame.lookVector * -35
    game:GetService("Debris"):AddItem(knockback, 0.01)
    task.wait(.025)
end
canhit = true

end)

script.Parent.Hit.OnServerEvent:Connect(function(Player)
if de == false then
de = true
wait(0.03)
script:WaitForChild(“SwordSwing”):Play()
de = false

if de == false then
    de = true
    local hum = Player.Character.Humanoid
    local animlist = anim:GetChildren()
    local selected = hum:LoadAnimation(anim.Play1)
    selected:Play()
    
    HitBox:HitStart()
    wait(0.04)
    canhit = true
    wait(0.01)
    HitBox:HitStop()
    canhit = false
        de = false
    end
    
end

end)

Client-Sided inside of tool:
local Tool = script.Parent
local Mouse = game.Players.LocalPlayer:GetMouse()

local hit = script.Parent:WaitForChild(“Hit”)

function isEquipped()
if Tool.Parent:FindFirstChild(“Humanoid”) then
return true else return false
end
end

Mouse.Button1Down:Connect(function()
if not isEquipped() then return end
hit:FireServer()
end)

Remove the period inbetween ReplicatedStorage in the RaycastHitbox variable.

1 Like

hey hey hey quick question when should i use this on a serverscript and on a clientscript? thank you in advance!!

Is it possible to still get V3? I want V3 to test it out.

  1. You can use GetDescendants and type checking it if you really want.
  2. Don’t be afraid of using open source code. It’s there for a reason and the mindset of freemodels/open source code instantly = bad game is not a good mindset to have as a developer.
1 Like

bro isnt there over 1k parts in the baseplate game by default :sob:
you are looping through it every heartbeat? :sob:
there are multiple weapons which will run it multiple time? :sob:

You can do something like Tool:GetDescendants()… You don’t have to loop through your entire workspace. If it’s that serious then use CollectionService and :GetTagged()

1 Like

Hi all! Sorry for not replying to anyone for the past couple of months. Realistically, this module is considered stable so there isn’t a need to update anymore.

For the last few years I’ve been playing around with using actors (but the way Roblox implemented actors makes any performance gain from using it not that worth), so if anyone is asking about that, I will not be implementing parallelization any time soon.


In some other news, I’ll be releasing a new module probably dubbed ‘Shapecast Hitbox’ (unless that name got stolen already) sometimes in the near future. It’ll be the successor to this module but will have different features, such as the ability to use Shapecasting (Block, Sphere, or Raycast), Resolution (can adjust to make raycasts less accurate for more performance), and some typechecking capabilities. There will be some features that won’t make it back (like SetPoints, LinkAttachments, Hitbox Distinctions) mainly because I think those aren’t needed anymore.

There will also be an optional helper plugin (will most likely be paid to support development of that module further) that allows you to auto-generate attachments in a part, generate block or sphere
shaped raycast colliders which you can move or rotate with studio tools. For people who likes to script the hitboxes in, this is probably not that useful but it’ll help people visualize and edit their hitboxes on the fly.

Generally the API is more simplified and generalized (no longer just working for humanoids, etc). It also should be more performant since I found out that V4 is pretty inefficient as it needs to go through all the hitboxes, even the inactive ones.

It probably will look like this:

local hitbox = ShapecastHitbox.new(swordHandle, raycastParams)

-- The hitbox will automatically disable itself after 3 seconds
-- The duration field is optional and will last indefinitely otherwise
local duration = 3 

-- HitStart is chainable into OnUpdate. You can alternative use
-- `Hitbox:OnUpdate()` on a new line if you prefer multi-lined usage.	
hitbox:HitStart(duration):OnUpdate(function(delta)

	-- We hit something!
	if hitbox.RaycastResult then 

		-- HitStop is chainable into OnUpdate or HitStart.
		-- For this example, we'll destroy the hitbox right
		-- after hitting something.
		hitbox:HitStop():Destroy()
	end
end)

-- Alternative, we can manually destroy the hitbox at some other time.
task.delay(10, hitbox.Destroy, hitbox)

or can really simplify it and chain it like so:

local hitbox = ShapecastHitbox.new(swordHandle, raycastParams)
     :HitStart(duration)
     :OnHit(function(raycastResults, segmentHit)
          local damage = 20

          -- Do more damage if the sword traveled further!
          if segmentHit.Distance >= 10 then
               damage *= 2
          end

          raycastResults.Instance.Parent.Humanoid:TakeDamage(damage)
     end)
     :OnUpdate(function(delta)
          --- Can do stuff per frame here
     end)
     :OnStopped(function()
          --- What happens when the hitbox stops?
          hitbox:Destroy()
     end)

If anyone still reads this thread, do you think you are interested in trying it out when it releases?

11 Likes

Here is the preview of ShapecastHitbox and the new plugin:

The plugin allows you create and modify shapecasts.

So rather spamming a few hundred attachments to get the same shapes, you can just modify the shapes of your hitbox however you like.

14 Likes

absolutely! i love this plugin and will always be my go when it comes to melee weapons. your work is deeply appreciated!

Is there a estimated release date?

2 Likes

I was just about to ask the same thing!

1 Like

Yes, there are no good shapecast utility modules that I know of, a sequel to Raycast Hitbox 4.0 for shapecasts would undoubtedly be amazing

1 Like

@Meat_Make @ramdoys

Shouldn’t take longer than two weeks! Probably @ me here again if I miss the deadline, haha! Currently, the module is complete but I am testing it in two full scale production games to ensure that nothing is wrong before I commit to a public release.

And also helps me to give me a window for feedback from testers to see if the API needs to be improved.

EDIT:
For those who wants to try it out earlier with us:

Here’s the Github link to the module or Wally if you are using Rojo. Be aware that this is very early versions that is currently undergoing tests with our teams.

There are no documentations just yet, but you can take a look at ShapecastHitbox/Types.luau to get a general feel of the API. For those who are new to Github, you can just download the file and copy paste the code into your codebase.

9 Likes

I’m having an issue where when I start a hitbox, the first set of lines (at least how it appears in the visualiser) doesn’t seem to register a hit, but the other lines work fine.

In the “Attachments” module script in the solvers folder on line 15, shouldn’t

local origin: Vector3 = point.Instances[1].WorldPosition 

actually be

local origin: Vector3 = point.LastPosition

?

Update 1:

I’m trying to use RaycastHitboxV4 in ServerScriptService but every example I’ve seen has been a script in the workspace.

Server Code
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local signals = ReplicatedStorage.Signals
local remotes = signals.Remotes

local raycastHitbox = require(script.RaycastHitboxV4)

local playerCooldowns = {}
local humanoidsHit = {}

local cooldown = 0.75--will be changed because each tool has different cooldowns
local damage = 25

local Attack = {}

Attack.Init = function()
	remotes.ToServer.OnServerEvent:Connect(function(player,signal,toolName)
		local char = player.Character
		local hum = char.Humanoid
		if signal == "Trigger" then
			
			local tool = char:FindFirstChild(toolName)
			if tool then
				if (playerCooldowns[player] and tick() - playerCooldowns[player] >= cooldown) or not playerCooldowns[player]  then
					playerCooldowns[player] = tick()
				else
					return
				end
				print("Successfully created")
				
				local hitbox = raycastHitbox.new(tool.Hitbox)
				local hitC = hitbox.OnHit:Connect(function(hit,humanoid)
					print(hit,humanoid)
					if humanoid == hum then return end
					humanoid:TakeDamage(damage)
				end)
				print("Started")
				hitbox:HitStart()
				task.wait(cooldown)
				hitbox:HitStop()
			end
		end
	end)
end

return Attack

When I click and attempt to fire this event to the server nothing happens but started and successfully created both print so I think its something wrong with how I’m using RaycastHitbox.

Can you only put this module and any script requiring it inside of a tool?

Update 2:

New Code
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local signals = ReplicatedStorage.Signals
local remotes = signals.Remotes

local raycastHitbox = require(script.RaycastHitboxV4)

local playerHitboxes = {}
local playerConnections = {}

local playerCooldowns = {}
local humanoidsHit = {}

local cooldown = 0.75--will be changed because each tool has different cooldowns
local damage = 25

local Attack = {}

Attack.Init = function()
	Players.PlayerAdded:Connect(function(plr)
		local hitbox

		plr.CharacterAdded:Connect(function(char)
			local hum = char.Humanoid
			char.ChildAdded:Connect(function(tool)
				
				if tool:IsA("Tool") and tool:FindFirstChild("Hitbox") then
					if not hitbox then hitbox = raycastHitbox.new(tool.Hitbox) end
					local c1 = tool.Activated:Connect(function()
						hitbox:HitStart()
						task.wait(cooldown)
						if hitbox then hitbox:HitStop() end
					end)
					local c2 = hitbox.OnHit:Connect(function(hit,humanoid)
						if humanoid == hum then return end
						humanoid:TakeDamage(damage)
					end)
					table.insert(playerConnections,c1)
					table.insert(playerConnections,c2)
				end
			end)
			char.ChildRemoved:Connect(function(tool)
				if tool:IsA("Tool") and tool:FindFirstChild("Hitbox") then
					for i,comp in ipairs(playerConnections) do
						comp:Disconnect()
						comp = nil
					end
					if hitbox then hitbox:Destroy() hitbox = nil end
				end
			end)
		end)
	end)
end

return Attack

So basically I removed firing to the server and made detection on the server.

There are multiple issues but my main one is that I can’t disconnect the OnHit event and I remember Swordphin saying he created something for the intended use of not disconnecting something so I’m not sure if I’m going in the right direction or not.

Update 3:

Ok all the fixes above I fixed but now I am back at bottom.

My current problem is that whenever I try to create a RaycastHitbox, the debug lines don’t show, the hitbox can’t be recognized in the code, and the hit detection doesn’t work.

I get random errors as well such as:

ServerStorage.Server.Attack.Melee:32: attempt to call missing method 'HitStop' of table

for _: number, point: Point in ipairs(ActiveHitboxes[i].HitboxRaycastPoints) do -- In the hitboxcaster

It mostly says HitStop and HitStart are missing methods.

It worked a while ago but I don’t know what I did to make it not work.

I checked through my past history but even in those past sessions when it did work, its not working anymore.

I don’t know what magical thing happened for this to change.

Code
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local signals = ReplicatedStorage.Signals
local unreliables = signals.Unreliables

local raycastHitbox = require(script.RaycastHitboxV4)
local infoSignaler = require(script.Parent.InfoSignaler)

local playerHitboxes = {}

local function reset(connections)
	for _,comp in ipairs(connections) do
		comp:Disconnect()
	end
	table.clear(connections)
	return connections
end


local Melee = {}

Melee.Start = function(plr,tool,info,playerParams,plrConnections)
	local char = plr.Character or plr.CharacterAdded:Wait()
	local root = char.HumanoidRootPart
	local c1,c2
	playerHitboxes[plr] = raycastHitbox.new(tool.Hitbox)
	playerHitboxes[plr].RaycastParams = playerParams
	
	c1 = tool.Activated:Connect(function()
			playerHitboxes[plr]:HitStart()
		task.wait(info.Cooldown)
			playerHitboxes[plr]:HitStop()
	end)
	
	c2 = playerHitboxes[plr].OnHit:Connect(function(hit,humanoid)
		local targetChar = hit.Parent
		local targetRoot = targetChar.HumanoidRootPart

		for _,player in Players:GetPlayers() do
			local playerChar = player.Character or player.CharacterAdded:Wait()
			local playerRoot = playerChar.HumanoidRootPart
			if (playerRoot.Position - tool.Hitbox.Position).Magnitude < 5e2 then
				local returnedInfo = infoSignaler[tool.Name](tool)
				unreliables.ToClient:FireClient(player,tool.Name.."FX",returnedInfo)
			end
		end

		targetRoot.AssemblyLinearVelocity = ((targetRoot.Position - root.Position) * 25) + Vector3.new(0,50,0)
		humanoid:TakeDamage(info.Damage)
	end)
	
	table.insert(plrConnections,c1)
	table.insert(plrConnections,c2)
	return plrConnections
end

Melee.End = function(plr,plrConnections)
	if playerHitboxes[plr] then playerHitboxes[plr]:Destroy() table.clear(playerHitboxes[plr]) playerHitboxes[plr] = nil end
	local resetConnections = reset(plrConnections)
	return resetConnections
end

return Melee

I have another main module called attack above this one but it just starts and ends these when equipping and unequipping tools.

I also tried moving the playerHitboxes table to the parent attack module but it did the same thing.

Found an interesting bug(?).
This module only works on server-sided Scripts with the Legacy RunContext. The Server RunContext returns nil when performing RaycastHitbox.new().

I haven’t tested it with client-sided scripts, so I can’t report on that.


Edit: I realised you can’t create new hitboxes if the part isn’t parented to workspace. Since “Server” RunContext scripts run regardless of the container, that’s why I was getting my error.

Legends says you’re still doing awesome work on it :muscle:

those legends would be very right