ShapecastHitbox: For all your melee needs!

Note: This module is still relatively new, and has yet to be tested for bugs. If you are uncertain about the stability of this module, I recommend using RaycastHitbox or ClientCast, both of which have a proven track record in thousands of games.

This module is the successor to my previous team’s RaycastHitbox: For All Your Melee Needs. Each library has unique features, so I recommend reading both to determine which one best suits your needs. In summary, ShapecastHitbox offers the following advantages over RaycastHitbox:

  • Shapecasting (Blockcast, Spherecast, Raycast)
  • Performance improvements when running many hitboxes
  • Simplified API
  • Decoupled hitboxes from instances/humanoids, allowing you to reuse hitboxes or use the same instances for different hitboxes
  • Typechecking (still new to this so might get some wrong)
  • Resolution (adjust accuracy for better performance)
  • Chaining operations

Limitations:

  • Casts do not detect hits when the rays start intersecting a part
  • Only works with Attachments (for Bones, use RigidConstraint & Parts)
  • Stationary hitboxes (like traps) will not work
  • Wide objects requires a sizable amount of attachments to be working correctly
  • Like RaycastHitbox, still requires a few redundant attachments, even if using Sphere/Blockcast as Roblox’s casting methods are still not entirely accurate

New to Raycasting Hitboxes? Original Post:

A few years ago, I became intrigued by the design of MORDHAU and Chivalry. I was particularly interested in how they achieved accurate hitboxing across a variety of weapon shapes and sizes while keeping the system simple. Here’s a timestamp showing how MORDHAU handles hitboxes for swords—it might be helpful.

Warning: Does include realistic depiction of blood so please be aware before opening

Examples

Similar to Mordhau, we will be using raycast to simulate our hitboxes.

Unlike RaycastHitbox, ShapecastHitbox does not limit you to just raycasting; you can also use Blockcasts and Spherecasts to create your hitboxes.

New Helper Plugin Now Available:

This optional plugin allows you to freely edit your hitboxes created with ShapecastHitbox, RaycastHitbox, and ClientCast. You can move groups of attachments, position or rotate Blockcast regions, or upscale Spherecast hitboxes. It is still experimental and lacks many features, but it will be periodically updated. Please refer to the main plugin post for updates.

Documentation

As this is still a relatively new module, there are no official documentation available yet. To start off, you can look at ShapecastHitbox/Types.luau to see the full API and more examples. Here are some simple scripts to get your toes wet.

Basic Hitbox Example

For any of these scripts to work, you must have added an Attachment into the part you want to become the hitbox. Rename or add the tag “DmgPoint” to this attachment.

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 alternatively use    
-- `Hitbox:OnUpdate()` on a new line if you prefer multi-lined usage.
hitbox:HitStart(duration):OnUpdate(function()

    -- 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)
local damage = 0
local duration = 3

hitbox:BeforeStart(function()
    damage = 5
end):HitStart(duration):OnHit(function(raycastResult, segmentHit)
    raycastResult.Instance.Parent.Humanoid:TakeDamage(damage)
end):OnUpdate(function(deltaTime)
    damage += (0.1 * deltaTime)
end):OnStopped(function(cleanCallbacks)
    cleanCallbacks() --- This will clean up this chain
    hitbox:Destroy()
end)

To turn off the red trails (these are used for debugging and test), you can directly disable it in the settings table:

ShapecastHitbox.Settings.Debug_Visible = false

If you are more familiar with the RaycastHitbox’s API, you can do similar things here:

local hitbox = ShapecastHitbox.new(tool, raycastParams)

local function onToolHit(raycastResult)
     local partHit = raycastResult.Instance
     partHit.Parent.Humanoid:TakeDamage(20)
end

hitbox:OnHit(onToolHit)

tool.Activated:Connect(function()
     hitbox:HitStart()
     task.wait(3)
     hitbox:HitStop()
end)

tool.Destroying:Connect(function()
     hitbox:Destroy()
end)
Main API

ShapecastHitbox constructor @returns Hitbox

@class Hitbox
Create easy hitboxes using Raycasts and Shapecasts.

To start using ShapecastHitbox, first initialize your hitbox via
ShapecastHitbox.new(instance). After you have configured it, you
can activate the hitbox by calling Hitbox:HitStart().

To stop the Hitbox, call Hitbox:HitStop(). If you are done using
the hitbox, it is recommended to call Hitbox:Destroy() to clean
up any connections left behind.

@function new
@param instance Instance
@param raycastParams? RaycastParams
@within Hitbox
Creates a new hitbox.

Hitbox methods

@function GetAllSegments
@return { [Instance]: Types.Segment }
@within Hitbox
Gets all segments of a hitbox. Segments refers to each individual
point that are being raycasted out of.

@function GetSegment
@param instance Instance
@return Types.Segment
@within Hitbox
Gets one segment from the hitbox using the original instance
as the reference.

@function SetResolution
@param resolution number
@return Types.Hitbox
@within Hitbox
Sets the resolution of the hitbox’s raycasting. This resolution is capped to
the user’s current frame rate (lower frames = less accurate). Lower accuracy
can result in better ShapecastHitbox performance so adjust according to the
context of your project.

@function SetCastData
@param castData Types.CastData
@return Types.Hitbox
@within Hitbox
Sets the current CastData of this hitbox. This is where you can set if the Hitbox should use
Raycast, Blockcast, or Spherecast. There is also additional funtionality (read the devforum post)
on only adjusting specific segments to use those raycast types.

@function BeforeStart
@param startCallback Types.StartCallback
@return Types.Hitbox
@within Hitbox
This callback runs before HitStart activates the hitbox.
Use OnStopped to clean up these callbacks or alternatively use
Hitbox:Destroy().

@function OnUpdate
@param updateCallback Types.UpdateCallback
@return Types.Hitbox
@within Hitbox
This callback runs per frame while the hitbox exists. Do note that
it will still run even if the hitbox is stopped. You can use
Hitbox.Active to determine if a hitbox is active or not. Use OnStopped
to clean up these callbacks or alternatively Hitbox:Destroy().

@function OnHit
@param hitCallback Types.HitCallback
@return Types.Hitbox
@within Hitbox
This callback is activated upon the hitbox returning a RaycastResult.
Use OnStopped to clean up these callbacks or alternatively Hitbox:Destroy().

@function OnStopped
@param stopCallback Types.StopCallback
@return Types.Hitbox
@within Hitbox
This callback runs after HitStop has activated. Do note that
Hitbox:Destroy() does not automatically run this function. This callback
has a parameter which helps you auto-cleanup every callback used so far.
If you don’t clean up, you may end up having duplicate callback calls
when reusing the hitbox.

@function Reconcile
@return Types.Hitbox
@within Hitbox
Runs automatically the first time a hitbox is initialized. Can be re-ran
again to make the hitbox re-search the instance for any new changes in the
hitbox. Do not run frequently as it is not performant.

@function HitStart
@param timer? number
@param overrideParams? RaycastParams
@return Types.Hitbox
@within Hitbox
Activates the hitbox. Can be given an optional timer parameter to make
the hitbox automatically stop after a certain amount of seconds. OverrideParams
can be used to switch RaycastParams on the fly (which the hitbox will default to).
Will activate all BeforeStart callbacks before activating the hitbox.

@function HitStop
@return Types.Hitbox
@within Hitbox
Deactivates the hitbox. Will call all OnStopped callbacks after deactivation.

@function Destroy
@within Hitbox
Disconnects the scheduler. When no references to the hitbox remain, it will be
automatically garbage collected.

Tips and Tricks and FAQ

Q1. Why does my hitbox instant-kill even though I set it’s damage to like, one??
A1. Unlike RaycastHitbox, ShapecastHitbox will fire OnHit for every raycast that hits a part. This can have an unfortunate side effect of triggering certain functions repeatedly. This is by design and for performance reasons. You must take this into account when designing your hitboxes. Here is an example code you can use that will filter out targets already hit by former raycasts:

local hitbox = ShapecastHitbox.new(part)
local targetsHit = {}

hitbox:OnHit(function(raycastResult)
     local instance = raycastResult.Instance

     if not targetsHit[instance.Parent] then
          targetsHit[instance.Parent] = true
     else
          return
     end

     local humanoid = instance.Parent:FindFirstChild("Humanoid")

     if humanoid then
          humanoid:TakeDamage(25)
     end
end):HitStart():OnStopped(function(cleanup)
     cleanup()
     table.clear(targetsHit)
end)

Q2. I made one big blockcast cover my entire weapon, but sometimes it’s still not hitting!
A2. Unfortunately, this is where we need to put redundancies. There are many reasons why your hitbox may still not be hitting, even though your sword or whatever is literally inside the enemy. This is due to how fundamentally Raycast, Blockcast, and Spherecasting work. They only detect things upon intersection and at their edge point. If a part has already passed a cast’s intersection part (like its inside a blockcast), then the cast will not detect any hits. I recommend splitting up your singular blockcast into multiple, smaller blockcasts to increase the chances of a hit.

You may find the library and their accessories with these links:

ShapecastHitbox
Plugin
Wally
Github

12 Likes

Very nice to see some high quality resources (unlike recently). I wished this module existed 3 years ago for my game. I still have PTSD from having to edit hundreds of DmgPoint attachments with RaycastHitbox :sob: . Will definitely check this out!

2 Likes

Bless you :pray:

3 Likes

Awesome!
I’ve been waiting for this since the first time you mentioned about it in a RaycastHitbox post

1 Like