-
-
Save howmanysmall/d3681cdf315245927b6b4d3da852d077 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
____________________________________________________________________________________________________________________________________________________________________________ | |
Created by Swordphin123 - 2019. If you have any questions, feel free to message me on DevForum. Credits not neccessary but is appreciated. | |
[ How To Use - Quick Start Guide ] | |
1. Insert Attachments to places where you want your "hitbox" to be. For swords, I like to have attachments 1 stud apart and strung along the blade. | |
2. Name those Attachments "DmgPoint" (so the script knows). You can configure what name the script will look for in the variables below. | |
3. Open up a script. As an example, maybe we have a sword welded to the character or as a tool. Require this, and initialize: | |
* Example Code | |
local Damage = 10 | |
RaycastHitbox:Initialize(Character, {Character}) | |
RaycastHitbox:HitStart(Character, Damage) | |
wait(2) | |
RaycastHitbox:HitStop(Character) | |
4. Profit. Refer to the API below for more information. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
[ RaycastHitBox API ] | |
* local RaycastHitbox = require(RaycastHitbox) ---Duh | |
--- To use, insert this at the top of your scripts or wherever. | |
* RaycastHitbox:Initialize(Instance model, table ignoreList) | |
Description | |
--- Preps the model and recursively finds attachments in it so it knows where to shoot rays out of later. | |
Arguments | |
--- Instance model: Model instance (Like your character, a sword model, etc). May support Parts later. | |
--- table ignoreList: Raycast takes in ignorelists. Heavily recommended to add in a character so it doesn't hurt itself in its confusion. | |
* RaycastHitbox:HitStart(Instance model, Number damage) | |
Description | |
--- Starts drawing the rays. Will only damage the target once. Call HitStop to reset the target pool so you can damage the same targets again. | |
Arguments | |
--- Instance model: Same model that you initialized with earlier, will return an error if said model was not init first. | |
* RaycastHitbox:HitStop(Instance model) | |
Description | |
--- Stops drawing the rays and resets the target pool. Will do nothing if no rays are being drawn from the initialized model. | |
Arguments | |
--- Instance model: Same model that you initialized with earlier, will return an error if said model was not init first. | |
* RaycastHitbox:Deinitialize(Instance model) | |
Description | |
--- Removes references to the attachments and garbage collects values from the instance. Great if you are deleting the hitbox soon. | |
--- The script will attempt to run this function automatically if the model ancestry was changed. | |
Arguments | |
--- Instance model: Same model that you initialized with earlier. Will do nothing if model was not initialized. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
[ Troubleshooting ] | |
Q1 - Rays are not coming out / DebugRays not showing any rays | |
--- Make sure you initialized a "Model" instance (not a part, not a mesh, etc) and that it contains parts. These parts should contain attachments | |
named after the AttachmentName variable below. | |
Q2 - Sometimes my rays "lag" or they do not come out soon enough | |
--- This is a known issue and I've been actively trying to investigate the cause of it. Though it doesn't really happen often enough to be a concern. | |
Q3 - How do I set it to damage specific NPCs/Teams? | |
--- For now, you can look below and edit the Hitboxing:RaysStart() function to suit your needs. If requested enough, I can support it natively later. | |
Or you can use the ignoreLists during initialization to separate the targets. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
I do not recommend editing the mayhem below unless you know what you're doing. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
--]] | |
local AttachmentName = "DmgPoint" --- The attachment names the script will look for. This is where the rays will shoot out of. | |
local DontCheckForTransparency = false --- Normally the script won't fire rays if the parent part the attachment is in is transparent (== 1). You can set this to true to override it. | |
local DebugRays = false --- Highly recommended to test your rays so turn this to true to see where your rays are shooting from. | |
------------------------------------------------- | |
local RaycastHitModule = { | |
Version = "1.0 Beta" | |
} | |
------------------------------------------------- | |
local function assert(condition, m) -- how about just don't make a new function | |
if not condition == true then -- WHY | |
warn(m) | |
end | |
end | |
local function CheckForTransparency(Point) | |
-- Could just do return DontCheckForTransparency or Point.Attachment.Parent.Transparency < 1 | |
-- or better yet don't use a function call | |
if DontCheckForTransparency or Point.Attachment.Parent.Transparency < 1 then | |
return true | |
end | |
return false | |
end | |
local function FindDamageAttachments(model) | |
local Points = {} | |
local Model = model:GetChildren() | |
for i = 1, #Model do -- ipairs faster | |
local Part = Model[i] | |
if not Part:IsA("BasePart") then | |
local MorePoints = FindDamageAttachments(Part) | |
for v = 1, #MorePoints do -- ipairs faster. | |
table.insert(Points, MorePoints[v]) -- Use a length variable and insert it with that. | |
end | |
else | |
if Part:FindFirstChild(AttachmentName) then | |
local DmgPoints = Part:GetChildren() | |
for v = 1, #DmgPoints do -- Same from above | |
local DmgPoint = DmgPoints[v] | |
if DmgPoint.Name == AttachmentName then | |
table.insert(Points, DmgPoint) -- Same from above as well | |
end | |
end | |
end | |
end | |
end | |
return Points | |
end | |
------------------------------------------------- | |
local RunService = game:GetService("RunService") | |
local Debris = game:GetService("Debris") | |
local Instances = {} | |
local Hitboxing = {} | |
Hitboxing.__index = Hitboxing | |
function Hitboxing.__tostring(self) | |
return self.Object.Name | |
end | |
function Hitboxing:RaysStart(Damage) | |
self.Connection = RunService.Heartbeat:connect(function() | |
local Target; -- unused | |
for _,Point in next, self.Points do -- use pairs | |
if CheckForTransparency(Point) then | |
if self.Connection then | |
if not Point.LastPosition then | |
Point.LastPosition = Point.Attachment.WorldPosition | |
end | |
local ray = Ray.new(Point.LastPosition, Point.Attachment.WorldPosition - Point.LastPosition) -- don't make a new variable for this | |
local obj, pos = workspace:FindPartOnRayWithIgnoreList(ray, self.Ignore) -- pos isn't even used | |
if DebugRays then | |
local beam = Instance.new("Part") | |
beam.BrickColor = BrickColor.new("Bright red") -- Why is it not a constant | |
beam.FormFactor = "Custom" -- Redundant. | |
beam.Material = "Neon" -- Should use Enums, not strings. | |
beam.Transparency = 0 -- Redundant. | |
beam.Anchored = true | |
beam.Locked = true | |
beam.CanCollide = false | |
local Dist = (Point.Attachment.WorldPosition - Point.LastPosition).magnitude | |
beam.Size = Vector3.new(0.1, 0.1, Dist) | |
beam.CFrame = CFrame.new(Point.Attachment.WorldPosition, Point.LastPosition) * CFrame.new(0, 0, -Dist / 2) | |
beam.Parent = workspace -- Use service damn it | |
Debris:AddItem(beam, 1) | |
end | |
if obj and (not self.HitTargets[obj.Parent]) then | |
local TargetHumanoid = obj.Parent:FindFirstChildOfClass("Humanoid") | |
if TargetHumanoid then | |
self.HitTargets[obj.Parent] = true | |
TargetHumanoid:TakeDamage(Damage) | |
end | |
end | |
Point.LastPosition = Point.Attachment.WorldPosition --- Save the last position in frame | |
else | |
break | |
end | |
end | |
end | |
end) | |
end | |
function Hitboxing:RaysStop() | |
if self.Connection then | |
for _,Point in next, self.Points do | |
Point.LastPosition = nil | |
end | |
self.HitTargets = {} | |
self.Connection:Disconnect() | |
self.Connection = nil | |
end | |
end | |
function Hitboxing:Deactivate() | |
self.Object = nil | |
self.Connection = nil | |
self.Points = nil | |
self.HitTargets = nil | |
self.Ignore = nil | |
end | |
function Hitboxing:FindDamageAttachments(model) | |
local Points = FindDamageAttachments(model) | |
if #Points > 0 then | |
-- ipairs damn it | |
for i = 1, #Points do | |
local DmgPoint = Points[i] | |
self.Points[DmgPoint] = {} -- Make a local table and set the properties, then set the table like this | |
self.Points[DmgPoint].Attachment = DmgPoint | |
self.Points[DmgPoint].LastPosition = nil | |
end | |
else | |
warn("No attachments were found with the name", AttachmentName.."!", "Did you setup correctly?") | |
end | |
end | |
------------------------------------------------- | |
function RaycastHitModule:Initialize(instanceObject, ignoreList) | |
assert(typeof(instanceObject) == "Instance", "RaycastHitModule requires an Instance") | |
local Hitbox = Instances[instanceObject] | |
if Hitbox then | |
print("Hitbox for this Instance exists") | |
else | |
if instanceObject:IsA("Model") then | |
local NewHitbox = setmetatable({ | |
Object = instanceObject, | |
Connection = nil, | |
Points = {}, | |
HitTargets = {}, | |
Ignore = ignoreList and ignoreList or {} | |
}, Hitboxing) | |
Instances[instanceObject] = NewHitbox | |
Instances[instanceObject]:FindDamageAttachments(instanceObject) | |
instanceObject.AncestryChanged:Connect(function() -- I don't trust roblox to gc | |
if not workspace:IsAncestorOf(instanceObject) then | |
Instances[instanceObject]:Deactivate() | |
Instances[instanceObject] = nil | |
print("Hitbox Object was deleted") | |
end | |
end) | |
print("Created Hitbox for Object") | |
else | |
print("RaycastHitModule requires a Model instance") | |
end | |
end | |
end | |
function RaycastHitModule:Deinitialize(instanceObject) | |
assert(typeof(instanceObject) == "Instance", "RaycastHitModule requires an Instance") | |
local Hitbox = Instances[instanceObject] | |
if Hitbox then | |
Hitbox:Deactivate() | |
Hitbox = nil | |
else | |
print("Hitbox does not exist") | |
end | |
end | |
function RaycastHitModule:HitStart(instanceObject, Damage) | |
local Hitbox = Instances[instanceObject] | |
assert(Hitbox ~= nil, "Hitbox does not exist") | |
if Hitbox then | |
Hitbox:RaysStart(Damage) | |
end | |
end | |
function RaycastHitModule:HitStop(instanceObject) | |
local Hitbox = Instances[instanceObject] | |
assert(Hitbox ~= nil, "Hitbox does not exist") | |
if Hitbox then | |
Hitbox:RaysStop() | |
end | |
end | |
return RaycastHitModule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
____________________________________________________________________________________________________________________________________________________________________________ | |
Created by Swordphin123 - 2019. If you have any questions, feel free to message me on DevForum. Credits not neccessary but is appreciated. | |
[ How To Use - Quick Start Guide ] | |
1. Insert Attachments to places where you want your "hitbox" to be. For swords, I like to have attachments 1 stud apart and strung along the blade. | |
2. Name those Attachments "DmgPoint" (so the script knows). You can configure what name the script will look for in the variables below. | |
3. Open up a script. As an example, maybe we have a sword welded to the character or as a tool. Require this, and initialize: | |
* Example Code | |
local Damage = 10 | |
RaycastHitbox:Initialize(Character, {Character}) | |
RaycastHitbox:HitStart(Character, Damage) | |
wait(2) | |
RaycastHitbox:HitStop(Character) | |
4. Profit. Refer to the API below for more information. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
[ RaycastHitBox API ] | |
* local RaycastHitbox = require(RaycastHitbox) ---Duh | |
--- To use, insert this at the top of your scripts or wherever. | |
* RaycastHitbox:Initialize(Instance model, table ignoreList) | |
Description | |
--- Preps the model and recursively finds attachments in it so it knows where to shoot rays out of later. | |
Arguments | |
--- Instance model: Model instance (Like your character, a sword model, etc). May support Parts later. | |
--- table ignoreList: Raycast takes in ignorelists. Heavily recommended to add in a character so it doesn't hurt itself in its confusion. | |
* RaycastHitbox:HitStart(Instance model, Number damage) | |
Description | |
--- Starts drawing the rays. Will only damage the target once. Call HitStop to reset the target pool so you can damage the same targets again. | |
Arguments | |
--- Instance model: Same model that you initialized with earlier, will return an error if said model was not init first. | |
* RaycastHitbox:HitStop(Instance model) | |
Description | |
--- Stops drawing the rays and resets the target pool. Will do nothing if no rays are being drawn from the initialized model. | |
Arguments | |
--- Instance model: Same model that you initialized with earlier, will return an error if said model was not init first. | |
* RaycastHitbox:Deinitialize(Instance model) | |
Description | |
--- Removes references to the attachments and garbage collects values from the instance. Great if you are deleting the hitbox soon. | |
--- The script will attempt to run this function automatically if the model ancestry was changed. | |
Arguments | |
--- Instance model: Same model that you initialized with earlier. Will do nothing if model was not initialized. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
[ Troubleshooting ] | |
Q1 - Rays are not coming out / DebugRays not showing any rays | |
--- Make sure you initialized a "Model" instance (not a part, not a mesh, etc) and that it contains parts. These parts should contain attachments | |
named after the AttachmentName variable below. | |
Q2 - Sometimes my rays "lag" or they do not come out soon enough | |
--- This is a known issue and I've been actively trying to investigate the cause of it. Though it doesn't really happen often enough to be a concern. | |
Q3 - How do I set it to damage specific NPCs/Teams? | |
--- For now, you can look below and edit the Hitboxing:RaysStart() function to suit your needs. If requested enough, I can support it natively later. | |
Or you can use the ignoreLists during initialization to separate the targets. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
I do not recommend editing the mayhem below unless you know what you're doing. | |
____________________________________________________________________________________________________________________________________________________________________________ | |
--]] | |
local Workspace = game:GetService("Workspace") | |
local Debris = game:GetService("Debris") | |
local RunService = game:GetService("RunService") | |
local AttachmentName = "DmgPoint" --- The attachment names the script will look for. This is where the rays will shoot out of. | |
local DontCheckForTransparency = false --- Normally the script won't fire rays if the parent part the attachment is in is transparent (== 1). You can set this to true to override it. | |
local DebugRays = false --- Highly recommended to test your rays so turn this to true to see where your rays are shooting from. | |
------------------------------------------------- | |
local RaycastHitModule = { | |
Version = "1.0 Beta" | |
} | |
local pairs = pairs | |
local ipairs = ipairs | |
-- Can probably be replaced with GetDescendants. | |
local function FindDamageAttachments(model) | |
local Points = {} | |
local Length = 0 | |
for _, Part in ipairs(model:GetChildren()) do | |
if not Part:IsA("BasePart") then | |
for _, Point in ipairs(FindDamageAttachments(Part)) do | |
Length = Length + 1 | |
Points[Length] = Point | |
end | |
else | |
if Part:FindFirstChild(AttachmentName) then | |
for _, DmgPoint in ipairs(Part:GetChildren()) do | |
if DmgPoint.Name == AttachmentName then | |
Length = Length + 1 | |
Points[Length] = DmgPoint | |
end | |
end | |
end | |
end | |
end | |
return Points | |
end | |
------------------------------------------------- | |
local Instances = {} | |
local Hitboxing = {} | |
Hitboxing.__index = Hitboxing | |
function Hitboxing:__tostring() | |
return self.Object.Name | |
end | |
local BRIGHT_RED = BrickColor.new("Bright red") | |
function Hitboxing:RaysStart(Damage) | |
-- probably not smart | |
self.Connection = RunService.Heartbeat:Connect(function() | |
for _, Point in pairs(self.Points) do -- use pairs | |
local Attachment = Point.Attachment | |
local WorldPosition = Attachment.WorldPosition | |
if DontCheckForTransparency or Attachment.Parent.Transparency < 1 then | |
if self.Connection then | |
if not Point.LastPosition then | |
Point.LastPosition = WorldPosition | |
end | |
-- local ray = Ray.new(Point.LastPosition, Point.Attachment.WorldPosition - Point.LastPosition) -- don't make a new variable for this | |
local obj = Workspace:FindPartOnRayWithIgnoreList(Ray.new(Point.LastPosition, WorldPosition - Point.LastPosition), self.Ignore) -- pos isn't even used | |
if DebugRays then | |
local beam = Instance.new("Part") | |
beam.BrickColor = BRIGHT_RED -- Why is it not a constant | |
-- beam.FormFactor = "Custom" -- Redundant. | |
beam.Material = Enum.Material.Neon -- Should use Enums, not strings. | |
-- beam.Transparency = 0 -- Redundant. | |
beam.Anchored = true | |
beam.Locked = true | |
beam.CanCollide = false | |
local Dist = (WorldPosition - Point.LastPosition).Magnitude | |
beam.Size = Vector3.new(0.1, 0.1, Dist) | |
beam.CFrame = CFrame.new(WorldPosition, Point.LastPosition) * CFrame.new(0, 0, -Dist / 2) | |
beam.Parent = Workspace -- Use service damn it | |
Debris:AddItem(beam, 1) | |
end | |
if obj and (not self.HitTargets[obj.Parent]) then | |
local TargetHumanoid = obj.Parent:FindFirstChildOfClass("Humanoid") | |
if TargetHumanoid then | |
self.HitTargets[obj.Parent] = true | |
TargetHumanoid:TakeDamage(Damage) | |
end | |
end | |
Point.LastPosition = WorldPosition --- Save the last position in frame | |
else | |
break | |
end | |
end | |
end | |
end) | |
end | |
function Hitboxing:RaysStop() | |
if self.Connection then | |
for _, Point in pairs(self.Points) do | |
Point.LastPosition = nil | |
end | |
self.HitTargets = {} | |
self.Connection:Disconnect() | |
self.Connection = nil | |
end | |
end | |
function Hitboxing:Deactivate() | |
self.Object = nil | |
self.Connection = nil | |
self.Points = nil | |
self.HitTargets = nil | |
self.Ignore = nil | |
end | |
function Hitboxing:FindDamageAttachments(model) | |
-- local Points = FindDamageAttachments(model) | |
local Points = {} | |
local Length = 0 | |
for _, Part in ipairs(model:GetChildren()) do | |
if not Part:IsA("BasePart") then | |
for _, Point in ipairs(FindDamageAttachments(Part)) do | |
Length = Length + 1 | |
Points[Length] = Point | |
end | |
else | |
if Part:FindFirstChild(AttachmentName) then | |
for _, DmgPoint in ipairs(Part:GetChildren()) do | |
if DmgPoint.Name == AttachmentName then | |
Length = Length + 1 | |
Points[Length] = DmgPoint | |
end | |
end | |
end | |
end | |
end | |
if Length > 0 then | |
-- ipairs damn it | |
for _, DmgPoint in ipairs(Points) do | |
self.Points[DmgPoint] = { | |
Attachment = DmgPoint; | |
LastPosition = nil; | |
} | |
end | |
else | |
warn("No attachments were found with the name", AttachmentName.."!", "Did you setup correctly?") | |
end | |
end | |
------------------------------------------------- | |
function RaycastHitModule:Initialize(instanceObject, ignoreList) | |
if typeof(instanceObject) ~= "Instance" then warn("RaycastHitModule requires an Instance") end | |
local Hitbox = Instances[instanceObject] | |
if Hitbox then | |
print("Hitbox for this Instance exists") | |
else | |
if instanceObject:IsA("Model") then | |
local NewHitbox = setmetatable({ | |
Object = instanceObject, | |
Connection = nil, | |
Points = {}, | |
HitTargets = {}, | |
Ignore = ignoreList and ignoreList or {} | |
}, Hitboxing) | |
Instances[instanceObject] = NewHitbox | |
Instances[instanceObject]:FindDamageAttachments(instanceObject) | |
local Connection | |
Connection = instanceObject.AncestryChanged:Connect(function() | |
if not Workspace:IsAncestorOf(instanceObject) then | |
Instances[instanceObject]:Deactivate() | |
Instances[instanceObject] = nil | |
Connection:Disconnect() | |
Connection = nil | |
print("Hitbox Object was deleted") | |
end | |
end) | |
print("Created Hitbox for Object") | |
else | |
print("RaycastHitModule requires a Model instance") | |
end | |
end | |
end | |
function RaycastHitModule:Deinitialize(instanceObject) | |
if typeof(instanceObject) ~= "Instance" then warn("RaycastHitModule requires an Instance") end | |
local Hitbox = Instances[instanceObject] | |
if Hitbox then | |
Hitbox:Deactivate() | |
Instances[instanceObject] = nil | |
Hitbox = nil -- doesn't work if the above is their intention | |
else | |
print("Hitbox does not exist") | |
end | |
end | |
function RaycastHitModule:HitStart(instanceObject, Damage) | |
local Hitbox = Instances[instanceObject] | |
if not (Hitbox ~= nil) then warn("Hitbox does not exist") end | |
if Hitbox then | |
Hitbox:RaysStart(Damage) | |
end | |
end | |
function RaycastHitModule:HitStop(instanceObject) | |
local Hitbox = Instances[instanceObject] | |
if not (Hitbox ~= nil) then warn("Hitbox does not exist") end | |
if Hitbox then | |
Hitbox:RaysStop() | |
end | |
end | |
return RaycastHitModule | |
--[[ | |
Length benchmark: | |
local Functions = {} | |
local Workspace = game:GetService("Workspace") | |
local table_insert = table.insert | |
local function GlobalTableInsert(Parent, ClassName) | |
local Descendants = {} | |
for _, Descendant in ipairs(Parent:GetDescendants()) do | |
if Descendant:IsA(ClassName) then | |
table.insert(Descendants, Descendant) | |
end | |
end | |
return Descendants | |
end | |
local function LocalTableInsert(Parent, ClassName) | |
local Descendants = {} | |
for _, Descendant in ipairs(Parent:GetDescendants()) do | |
if Descendant:IsA(ClassName) then | |
table_insert(Descendants, Descendant) | |
end | |
end | |
return Descendants | |
end | |
local function RecalculateLength(Parent, ClassName) | |
local Descendants = {} | |
for _, Descendant in ipairs(Parent:GetDescendants()) do | |
if Descendant:IsA(ClassName) then | |
Descendants[#Descendants + 1] = Descendant | |
end | |
end | |
return Descendants | |
end | |
local function LengthVariable(Parent, ClassName) | |
local Descendants = {} | |
local Length = 0 | |
for _, Descendant in ipairs(Parent:GetDescendants()) do | |
if Descendant:IsA(ClassName) then | |
Length = Length + 1 | |
Descendants[Length] = Descendant | |
end | |
end | |
return Descendants | |
end | |
Functions["GlobalTableInsert"] = function() | |
local Table = GlobalTableInsert(Workspace, "ModuleScript") | |
Table = nil | |
end | |
Functions["LocalTableInsert"] = function() | |
local Table = LocalTableInsert(Workspace, "ModuleScript") | |
Table = nil | |
end | |
Functions["RecalculateLength"] = function() | |
local Table = RecalculateLength(Workspace, "ModuleScript") | |
Table = nil | |
end | |
Functions["LengthVariable"] = function() | |
local Table = LengthVariable(Workspace, "ModuleScript") | |
Table = nil | |
end | |
require(4185109675).new(1, "BenchmarkName", Functions) | |
--]] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment