I believed I just fixed the issue
Ok so this is great - Amazing - makes things so much easier for someone like me not so experienced with scripting but…
I followed the tutorial exactly. Everything worked, the animation, the damage, only when I equipped the weapon, it showed up like 2 feet away from the character then glitched out of the world.
I think it’s something I did wrong, but not sure exactly what. It might be something to do with the anchoring?
EDIT: Also how can I get a pre-made effect to play when I do the basic swing?
EDIT: I fixed the first issue now. I just need to know how to play my effect at the same time.
yup its fixed thanks gonna mess around with the module a bit more
the module has a trail effect in it but Arc said it was a bit glitchy so you could just use UserInputService to activate it
I’d like to give an update on the new SwordSmithV2 module that I alluded to a few times in some of the comments on this post. Currently, the module can’t do nearly as many things as the original, however, the things it can do, are performed more efficiently, cleaner, and in a more organized fashion. Below I’ve listed my current progress on the module:
- Completely new infrastructure, now having a formal settings module, and a module for both the client and the server. This will allow for much more functionality than in the original.
- There will be actual signals to connect to, unlike the original module which creates a new function for every signal.
- More advanced hit detection. I’m now detecting hits very similarly to how they’re detected in RaycastHitbox, which has yielded some very nice results. The new system raycasts on the client, once something has been hit, it fires a remote event. The hit is verified on the server before dealing damage to the target. To prevent exploitation this Clock event is fired every 2.5 seconds to verify the position of the client, if they fail to give correct data, or they give data too late, their sword will be locked until they can provide the server with accurate data 5 times. The code used to do this is below
Anti-Exploit
Clock = function(player, vector)
if Server[player] == nil then
return;
end;
local sword = Server[player];
local verification = sword._verification_controller;
local log_num = #verification._log;
if log_num == 5 then
for index in pairs(verification._log) do
verification._log[index] = nil;
end;
log_num = 1;
end;
local clock_leeway = VerificationSettings.Clock + VerificationSettings.Clock*1.5;
if vector.Y ~= 0 then
vector = Vector3.new(vector.X, 0, vector.Z);
end;
if os.time() - verification._clock <= clock_leeway then
table.insert(verification._log, vector);
if log_num > 1 and (verification._log[log_num] - vector).Magnitude >=
(sword._max_walkspeed or VerificationSettings.MaxWalkSpeed)*clock_leeway
+ VerificationSettings.StudLeeway
then
sword:ClockFailure(2);
end;
verification._clock = os.time();
verification._checks += 1;
else
sword:ClockFailure(1);
end;
verification._locked = verification._checks < 5;
- Debounces are built-in on the server and client, whereas in the previous module you would have to handle denounces yourself.
- The new module is capable of procedurally settings HitPoints along the length of the blade with far more accuracy than the original. The new code I use to do this is below.
HitPoint Position Calculating
local topAttachment: Attachment?, bottomAttachment: Attachment? = nil, nil;
if self._points == nil then
self._points = {};
local topSurface: CFrame = handle.CFrame*CFrame.new(0, handle.Size.Y/2, 0);
local bottomSurface: CFrame = handle.CFrame*CFrame.new(0, -handle.Size.Y/2, 0);
if self._compare_pairs == true then
local surface_pairs = {
{
[Enum.NormalId.Top] = topSurface,
[Enum.NormalId.Bottom] = bottomSurface,
},
{
[Enum.NormalId.Right] = handle.CFrame*CFrame.new(handle.Size.X/2, 0, 0),
[Enum.NormalId.Left] = handle.CFrame*CFrame.new(-handle.Size.Z/2, 0, 0),
},
{
[Enum.NormalId.Back] = handle.CFrame*CFrame.new(0, 0, handle.Size.Z/2),
[Enum.NormalId.Front] = handle.CFrame*CFrame.new(0, 0, -handle.Size.Z/2),
},
};
local length: number, surfaces: {[number]: Enum.NormalId | number} = -math.huge, table.create(2);
for index: number, pair: {[Enum.NormalId]: CFrame} in pairs(surface_pairs) do
local surfs: {[number]: Enum.NormalId | number} = nil;
if index == 1 then
surfs = {Enum.NormalId.Top, Enum.NormalId.Bottom};
elseif index == 2 then
surfs = {Enum.NormalId.Right, Enum.NormalId.Left};
elseif index == 3 then
surfs = {Enum.NormalId.Back, Enum.NormalId.Front};
end;
local magnitude: number = (pair[surfs[1] :: Enum.NormalId].Position -
pair[surfs[2] :: Enum.NormalId].Position).Magnitude;
if magnitude > length then
table.insert(surfs, index);
length, surfaces = magnitude, surfs;
end;
end;
local row = surface_pairs[surfaces[3] :: number];
topSurface, bottomSurface = row[surfaces[1] :: Enum.NormalId], row[surfaces[2] :: Enum.NormalId];
end;
local res: number = math.ceil((handle.Size.Y + 1)/2);
res = (res <= 1 and 5) :: number;
for i: number = 0, 1, 1/(res - 1) do
local attachment = Instance.new("Attachment");
attachment.Name = "CastPoint";
attachment.CFrame = handle.CFrame:ToObjectSpace(bottomSurface:Lerp(topSurface, i));
attachment.Visible = Settings.DebugMode;
attachment.Parent = handle;
table.insert(self._points, attachment);
end;
end;
If I listed all the differences between the new and the old Sword Smith, I would be writing here all day. However, I will try to update you all on my progress regularly. For those of you who are already using Sword Smith, I will be trying to create a conversion function between the old Sword Smith and the new version.
Feel free to suggest any new features you’d like to see and ask questions!
I plan on officially releasing the module within the next week or two if possible
I decided to try and increase the efficiency and functionality of the Raycaster
object the new module will be providing, which might take a little while longer. I said I would release the new version possibly sometime this week, however, it just won’t be done in time. Here’s a sneak-peak on the Raycaster
, it currently has a few bugs I’m trying to work out.
Raycaster
--!strict
--- [[ Module Definition ]] ---
local Raycaster = {};
local RaycasterImpl = newproxy(true);
local RaycasterInternalAPI = {};
--- [[Roblox Services]] ---
local RunService = game:GetService("RunService");
--- [[ Modules ]] ---
local Util = require(script:FindFirstAncestor("MainModule").Util);
--- [[ Localization ]] ---
local Trace: (Vector3, Vector3, boolean) -> () = Util.DrawRay or function() end;
--- [[ Types ]] ---
local TypeDefs = require(script.Parent.TypeDefs);
type Dictionary<KeyType, ValueType> = TypeDefs.Dictionary<KeyType, ValueType>;
type Debugger = TypeDefs.Debugger;
type Result = TypeDefs.Result;
type RaycastPoint = TypeDefs.RaycastPoint;
type Raycaster = TypeDefs.Raycaster;
--- [[ Variables ]] ---
local Stepper: RBXScriptSignal = RunService.Heartbeat;
local ERR_EXPECTED: string = "invalid argument #%d (%s expected, got %s)";
local ERR_ARG_MISSING: string = "Argument %d missing or nil";
local AncestorName: string = "SwordSmithV2";
--- [[ Functions ]] ---
local function OfficialError(msg: string, callback: Debugger?)
((callback or error) :: Debugger)(string.format("[%s]: %q", AncestorName, msg), 2);
end;
local function ExpectedGot(arg: number, expected: string, got: string)
if got == "nil" then
OfficialError(ERR_ARG_MISSING:format(arg));
else
OfficialError(ERR_EXPECTED:format(arg, expected, got));
end;
end;
local function __tostring(): string
return "Raycaster";
end;
local function Update(point: RaycastPoint)
point._last_position = point._real.WorldPosition;
end;
local function Solve(point: RaycastPoint): (Vector3, Vector3)
local current_pos: Vector3 = point._real.WorldPosition;
if point._last_position == nil then
point._last_position = current_pos;
local last_pos: Vector3 = point._last_position :: Vector3;
return last_pos, Vector3.new(0, 0, -last_pos.Unit.Z);
end;
local last_pos: Vector3 = point._last_position :: Vector3;
return last_pos, current_pos - last_pos;
end;
--- [[ Private API ]] ---
getmetatable(RaycasterImpl :: any).__tostring = __tostring;
function RaycasterInternalAPI.Stop(raycaster: Raycaster)
raycaster[RaycasterImpl].Stop();
end;
function RaycasterInternalAPI.Destroy(raycaster: Raycaster)
raycaster[RaycasterImpl].Destroy();
end;
function RaycasterInternalAPI.Play(raycaster: Raycaster, cast_length: number?, params: RaycastParams): Result?
return raycaster[RaycasterImpl].Play(cast_length, params);
end;
function RaycasterInternalAPI.create(tool: Tool, ignore_list: Dictionary<number, Instance>): Raycaster
local impl: Dictionary<string, any> = {
_playing = false,
_ignore_list = ignore_list,
_points = {},
};
local handle: BasePart = (tool:FindFirstChild("FakeHandle") or tool:FindFirstChild("Handle")) :: BasePart;
for _, child: Instance in pairs(handle:GetChildren()) do
if not child:IsA("Attachment") then
continue;
end;
local raycastPoint: RaycastPoint = {
_real = child :: Attachment,
_last_position = (child :: Attachment).WorldPosition,
};
table.insert(impl._points, raycastPoint);
end;
impl._ray_params = RaycastParams.new();
impl._ray_params.FilterDescendantsInstances = impl._ignore_list;
impl._ray_params.FilterType = Enum.RaycastFilterType.Blacklist;
function impl.Stop()
impl._playing = false;
end;
function impl.Destroy()
if impl._playing == true then
impl.Stop();
end;
for key: string in pairs(impl) do
impl[key] = nil;
end;
end;
function impl.Play(
cast_length: number?,
do_destroy: boolean
): Result?
local cast_length: number = cast_length or 2;
local t0: number = 0;
local collision: boolean, result: Result = false, nil;
impl._playing = true;
local points: Dictionary<number, RaycastPoint> = impl._points;
for _, point: RaycastPoint in pairs(points) do
Update(point);
end;
local kill_function: () -> () = impl[do_destroy == true and "Destroy" or "Stop"];
local params: RaycastParams = impl._ray_params;
while
collision == false and
impl._playing == true and
t0 < cast_length
do
for _, point: RaycastPoint in pairs(points) do
local origin: Vector3, direction: Vector3 = Solve(point);
local raycast_result: RaycastResult = workspace:Raycast(origin, direction, params);
if raycast_result ~= nil then
local body_part: BasePart = raycast_result.Instance;
local character: Model = body_part.Parent :: Model;
local humanoid: Humanoid = character:FindFirstChildWhichIsA("Humanoid") :: Humanoid;
if humanoid == nil then
Trace(origin, direction, false)
return;
end;
result = {
BodyPart = body_part,
Character = character,
Humanoid = humanoid,
Position = raycast_result.Position,
};
collision = true;
Trace(origin, direction, true);
else
Trace(origin, direction, false);
end;
end;
t0 += Stepper:Wait();
end;
kill_function();
return result;
end;
return {
Type = "Raycaster",
[RaycasterImpl] = impl :: any,
};
end;
--- [[ Public API ]] ---
local RaycasterPublicMeta: Dictionary<string, any> = {
__index = Raycaster,
__tostring = __tostring,
};
function Raycaster.new(tool: Tool, ignore_list: Dictionary<number, Instance>): any
return setmetatable(RaycasterInternalAPI.create(tool, ignore_list), RaycasterPublicMeta);
end;
function Raycaster:Stop()
RaycasterInternalAPI.Stop(self);
end;
function Raycaster:Destroy()
RaycasterInternalAPI.Destroy(self);
end;
function Raycaster:Play(cast_length: number?, params: RaycastParams)
return RaycasterInternalAPI.Play(self, cast_length, params);
end;
return Raycaster;
Using 2.1 version the sword dont hits a player even if it touches and the player is little bit far from hitter player. Does this is an issue in collision fedility.
how do you use the HumanoidHit function? i tried sword:HumanoidHit(function() but it just returns an error
alright it was actually sword:OnHumanoidHit(function() not just HumanoidHit
i would really appreciate it if you would make us have the ability to label the part that deals the damage, for example. i made a spear and found that every part of the tool can hurt other player when only tip should do so.
I just finished creating a usable copy of the new Sword Smith module. I plan to create a separate post detailing how to use it. Some of the key differences are:
- Higher fidelity hit detection
- More accurate hit-point position calculation
- Built-in debounce
- Signals which can be “connected” to
Model:
https://www.roblox.com/catalog/8959389592/Sword-Sm-th-V2
Can u tell how to use it? Waiting for it documentation.
I hope to have the documentation out by the weekend.
Apparently I’m late but I’m hoping for an answer, how to create a tag system on the sword?
Yep he is actually making sworsmith2 since long time but documentation is not released. It might have the feature to make ur own raycast attachment in sword
Can you add a feature that supports NPCs it will be easier to create enemies with swords
edit: Question(do you still maintain this Framework?)
This framework is no longer maintained, I currently work on:
There’s no documentation available because I decided to take more time on it.
In the version you’re talking about, using events can be done like so:
-- TargetKilled is fired every time your sword kills something
sword:OnTargetKilled(function()
print("Something has been killed")
end)
I tried it but it doesn’t print out the message
edit: other events are working expect the targetkilled
local ServerStorage = game:GetService("ServerStorage")
local SwordSmith = require(ServerStorage:WaitForChild("SwordSmith"))
local tool = script.Parent
local debounce = false
local data = {
swingAnims={7049668026},
killPlayers = false,
knockBackEnabled = true,
knockBackStrength = 100
}
local sword = SwordSmith.new(tool, data)
tool.Activated:Connect(function()
if debounce then
return
end
debounce = true
sword:Swing(1000, game.Workspace.ragdolls:GetChildren())
end);
sword:OnSwingEnded(function()
--You can of course add extra delay if you have a short swing animation
debounce = false
end)
-- TargetKilled is fired every time your sword kills something
sword:OnTargetKilled(function()
print("Something has been killed")
end)