How would I make a hitbox appear on the Right Arm on pressing R?

Alright, there are no errors, but nothing happens on ;pressing the Key. The server script is in ServerScriptService, and the localscript in startergui.
server script:

local remotes = {
	uppercut = game:GetService('ReplicatedStorage'):WaitForChild('ClickHitbox');
}


local function isAlive(player)
	local char = player.Character
	if not char or not char:IsDescendantOf(game.Workspace) or not char:FindFirstChild "Humanoid" or char.Humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	return char
end

local function getArms(character, side) -- gets all the arm parts of the side the player wants to damage from so we can check length
	-- Will only work for R15
	local hand  = character:FindFirstChild(side .. 'Arm')   
	if hand then
		return hand  
	end
end


remotes.uppercut.OnServerEvent:connect(function (player, arm)
	if player and arm and typeof(arm) == 'string' and arm == 'Left' or arm == 'Right' then
		-- some typechecking, highly recommend you also add a check to limit the amount of times a player can fire
		-- based on the cooldown of each attack etc to stop exploiting
		local character = isAlive(player)
		if character then
			local arms = getArms(character, arm)
			if arms then
local active = { } -- a table we've defined as active, which we'll store a bunch of characters within
for i, v in next, game.Players:GetPlayers() do -- get all players in the game, and loop through them
	if v and v ~= player then -- if the player we're looking at in the list of the players in the game is NOT the player who sent this remote then
		local char = isAlive(v) 
		if char then -- is this player that we're looking at alive?
			active[#active + 1] = char -- add that character to the 'active list'
		end
	end
end
		
				local length = arms.Size.y
				local bounding = character.PrimaryPart
				
				local ray = Ray.new(bounding.Position, bounding.CFrame.lookVector * (length + 1)) -- Fire a ray from our character's torso, towards its lookvector with a length of our arm size
				local hit, pos = game.Workspace:FindPartOnRayWithWhitelist(ray, active) -- find any parts we hit that's a part of a character's
				
				if hit and hit.Parent then
					local char = hit.Parent
					local hum  = hit.Parent:FindFirstChild 'Humanoid'
					if hum then
						hum:TakeDamage(99) -- Whatever we want to do to that other player's character
						print("Hit")
					end
				end
			end
		end
	end
end)

localscript:

local player     = game.Players.LocalPlayer
local character

local animationFolder = game:GetService('ReplicatedStorage'):WaitForChild('AnimationFolder'):GetChildren() --> Change 'AnimationFolder' to a folder with all of your animations in it
local anims = { }
--> This will create an easy to access dict for our anims, for this, we need an animation in there called the name of the action, in this case:
--> 'Uppercut' will need to be in the animation folder
for i, v in next, animationFolder do
	anims[v.Name] = v
end

local remotes = { 
	--> Create a table of remotes, so that you can access them at will for each command
	Uppercut = game:GetService('ReplicatedStorage'):WaitForChild('Remotes'):WaitForChild('ClickHitbox');
	--[?] To add another, you would write: otherAnimation = game:GetService('ReplicatedStorage'):WaitForChild('Remotes'):WaitForChild('otherRemote')';
}

local states = { --> This is used to keep track of what our player is currently doing
	playing = nil; --> This determines whether or not we're performing an action currently so that we can't perform another
	last    = {
		--> This is where we'll store the last performed action times
	};
}

local cooldowns = { --[!] You could put this in a module for both the server + the client to access
	Uppercut = 6; --> How long you should wait before someone can perform another Uppercut (in seconds)
}

local actions = { --[!] This could also be put in a module as above
	Uppercut = Enum.KeyCode.R; --> What input are we looking for when we want to perform this action
}

local function isAlive()
	local char = character or player.Character
	if not char or not char:IsDescendantOf(game.Workspace) or not char:FindFirstChild "Humanoid" or char.Humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	return char
end

local function checkCooldown(class)
	if cooldowns[class] then --> Do we have a cooldown value to compare to for this class of action?
		if (time() - (states.last[class] or 0)) > cooldowns[class] then	--> Returns true if the last time we performed this action has passed the length of the cooldown
			return true
		end
	end
	return false
end

local function performAction(class, args)
	local anim   = anims[class]
	local remote = remotes[class]
	if anim and remote then
		local track = character.Humanoid:LoadAnimation(anim)
		track.Priority = Enum.AnimationPriority.Action
		track:GetMarkerReachedSignal("ActionFrame"):Connect(function ()
			--> Tell the server we're at the action frame of the animation, so it needs to look to do any damage
			remote:FireServer(args)
			
			--> Update our states so we can perform other actions again + add our last time to the list
			states.last[class] = time()
			states.playing = false
		end)
		track:Play()
	end
end

game:GetService("UserInputService").InputBegan:Connect(function(Input, gameProcessed)
	character = isAlive()
	if not character or gameProcessed then
		return
	end
    if Input.UserInputType == Enum.UserInputType.Keyboard then
		if Input.KeyCode == actions.Uppercut then 
			if not states.playing and checkCooldown('Uppercut') then
				states.playing = true
				performAction('Uppercut', 'Right')  
			end
		end
	end
end)

Can you take a screenshot of ReplicatedStorage showing the folders ‘Remotes’ and ‘AnimationFolder’, incl. the animation ‘Uppercut’?

Also, did you make sure to add the marker signal ‘ActionFrame’ and republish the animation and use that new anim?

image
Yeah, I did.

Getting an error now, LoadAnimation requires an Animation object

The error is gone after moving Uppercut to AnimSaves, but nothing still happens.

You need to publish the animation ‘Uppercut’, those are keyframes. Go back into the animation editor, click the three dots and it will either say ‘Export’ or ‘Publish’ (Can’t remember which). Export/Publish it to a Roblox animation. It will provide you with an url when you publish it, copy that url and then insert an Animation object into ‘AnimationFolder’ and set the AnimationID as the url you just got given when you published it.

1 Like

So like this?
Nevermind, it works now! I’ll test it out.

Correct. I’m assuming it works now?

1 Like


Alright, all works well, but i don’t think it did damage. I’ll try to get a recording of it. Maybe it’s because of the arm raised too high?

Are you trying to damage that dummy? If so, it won’t work. It’s because I’ve made the server only check for collisions with player’s parts i.e. other players other than yourself, not NPCs.

Again, this is why details are important. Do you only want the uppercut to damage NPCs? Or both?

1 Like

I got someone to test it

I apologize for the lack of details in this entire thread.

Can you remove everything that’s not related to this and send the rbxl file? Will check and ping it back to you, it’s difficult unless you can add debug output for me and print the if statements to determine what’s failing

I’ll message you it.

(30 chars)

The issue was twofold:

  • You had two remote events called ClickHitbox, one was inside ReplicatedStorage, and the other was inside a folder called ‘Remotes’ inside ReplicatedStorage… We only needed the latter remote because that’s the only one being fired to the client. On the server code in the place you sent, it was referencing the replicated remote which was never fired by the client.

  • The other issue was my fault, I forgot that R6 has spaces between ‘Right’ and ‘Arm’ for their limbs in my haste to provide you with a solution. I have since changed this for you, and it now works.

Hope this has provided you with a comprehensive solution and you’ve learned a little.

Change the server code to this:

local remotes = {
	uppercut = game:GetService('ReplicatedStorage'):WaitForChild('Remotes'):WaitForChild('ClickHitbox');
}


local function isAlive(player)
	local char = player.Character
	if not char or not char:IsDescendantOf(game.Workspace) or not char:FindFirstChild "Humanoid" or char.Humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	return char
end

local function getArms(character, side) -- gets all the arm parts of the side the player wants to damage from so we can check length
	-- Will only work for R15
	local hand  = character:FindFirstChild(side .. ' Arm')   
	if hand then
		return hand  
	end
end


remotes.uppercut.OnServerEvent:connect(function (player, arm)
	if player and arm and typeof(arm) == 'string' and arm == 'Left' or arm == 'Right' then
		-- some typechecking, highly recommend you also add a check to limit the amount of times a player can fire
		-- based on the cooldown of each attack etc to stop exploiting
		local character = isAlive(player)
		if character then
			local arms = getArms(character, arm)
			if arms then
				local active = { } -- a table we've defined as active, which we'll store a bunch of characters within
				for i, v in next, game.Players:GetPlayers() do -- get all players in the game, and loop through them
					if v and v ~= player then -- if the player we're looking at in the list of the players in the game is NOT the player who sent this remote then
						local char = isAlive(v) 
						if char then -- is this player that we're looking at alive?
							active[#active + 1] = char -- add that character to the 'active list'
						end
					end
				end
		
				local length = arms.Size.y
				local bounding = character.PrimaryPart
				local ray = Ray.new(bounding.Position, bounding.CFrame.lookVector * (length + 1)) -- Fire a ray from our character's torso, towards its lookvector with a length of our arm size
				local hit, pos = game.Workspace:FindPartOnRayWithWhitelist(ray, active) -- find any parts we hit that's a part of a character's
				
				if hit and hit.Parent then
					local char = hit.Parent
					local hum  = hit.Parent:FindFirstChild 'Humanoid'
					if hum then
						hum:TakeDamage(99) -- Whatever we want to do to that other player's character
					end
				end
			end
		end
	end
end)
1 Like

Also, where would I edit it to make the hitbox bigger?

I did hit the person like once, but the hitbox is a bit too wonky. Possible to change it?

It’s not that it’s wonky, it just shoots a single ray from the centre of your character forwards at the length of your arms (+ 1 stud). If you want a longer hit box, change the ’ + 1’ in the code to a value to make it longer.

If you want a wider hitbox, you would either need to:

  1. Add more rays at X offsets in the width you desire OR change the raycasting entirely, and do it from points whilst the animation is playing, damaging any raycast hits along this path (debounced to previously hit objects)

OR (the better options imo)

  1. Use magnitude on active players and determine whether they’re in the direction you need and within FOV of the width of the hitbox OR do a region3 check in front of your character for a whitelist of the active players
1 Like

Thank you. I’ll try it out.

(30)

Hey, just a question. I did this with a kick animation, and it works 100% well, but it sometimes breaks randomly, and only the client can see the animation. You can reply whenever you want, I can wait for you.
Local script:

local player     = game.Players.LocalPlayer
local character

local animationFolder = game:GetService('ReplicatedStorage'):WaitForChild('AnimationFolder'):GetChildren() --> Change 'AnimationFolder' to a folder with all of your animations in it
local anims = { }
--> This will create an easy to access dict for our anims, for this, we need an animation in there called the name of the action, in this case:
--> 'Kick' will need to be in the animation folder
for i, v in next, animationFolder do
	anims[v.Name] = v
end

local remotes = {
	--> Create a table of remotes, so that you can access them at will for each command
	Kick = game:GetService('ReplicatedStorage'):WaitForChild('Remotes'):WaitForChild('Kick');
	--[?] To add another, you would write: otherAnimation = game:GetService('ReplicatedStorage'):WaitForChild('Remotes'):WaitForChild('otherRemote')';
}

local states = { --> This is used to keep track of what our player is currently doing
	playing = nil; --> This determines whether or not we're performing an action currently so that we can't perform another
	last    = {
		--> This is where we'll store the last performed action times
	};
}

local cooldowns = { --[!] You could put this in a module for both the server + the client to access
	Kick = 3; --> How long you should wait before someone can perform another Kick (in seconds)
}

local actions = { --[!] This could also be put in a module as above
	Kick = Enum.KeyCode.E; --> What input are we looking for when we want to perform this action
}

local function isAlive()
	local char = character or player.Character
	if not char or not char:IsDescendantOf(game.Workspace) or not char:FindFirstChild "Humanoid" or char.Humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	return char
end

local function checkCooldown(class)
	if cooldowns[class] then  
		if (time() - (states.last[class] or 0)) > cooldowns[class] then	 
			return true
		end
	end
	return false
end

local function performAction(class, arge)
	local anim   = anims[class]
	local remote = remotes[class]
	if anim and remote then
		local track = character.Humanoid:LoadAnimation(anim)
		track.Priority = Enum.AnimationPriority.Action
		track:GetMarkerReachedSignal("ActionFrame"):Connect(function ()
			remote:FireServer(arge)
			
			--> Update our states so we can perform other actions again + add our last time to the list
			states.last[class] = time()
			states.playing = false
		end)
	track:Play()
	end
end

game:GetService("UserInputService").InputBegan:Connect(function(Input, gameProcessed)
	character = isAlive()
	if not character or gameProcessed then
		return
	end
    if Input.UserInputType == Enum.UserInputType.Keyboard then
		if Input.KeyCode == actions.Kick then 
			if not states.playing and checkCooldown('Kick') then
				states.playing = true
				performAction('Kick', 'Right') -- Change 'Right' to whatever side of the arm the uppercut is happening on
			end
		end
	end
end)

Serverscript:

local remotes = {
	Kick = game:GetService('ReplicatedStorage'):WaitForChild('Remotes'):WaitForChild('Kick');
}


local function isAlive(player)
	local char = player.Character
	if not char or not char:IsDescendantOf(game.Workspace) or not char:FindFirstChild "Humanoid" or char.Humanoid:GetState() == Enum.HumanoidStateType.Dead then
		return
	end
	return char
end

local function getLegs(character, side) -- gets all the arm parts of the side the player wants to damage from so we can check length
	-- Will only work for R15
	local leg  = character:FindFirstChild(side .. ' Leg')   
	if leg then
		return leg  
	end
end


remotes.Kick.OnServerEvent:connect(function (player, Leg)
	if player and Leg and typeof(Leg) == 'string' and Leg == 'Left' or Leg == 'Right' then
		-- some typechecking, highly recommend you also add a check to limit the amount of times a player can fire
		-- based on the cooldown of each attack etc to stop exploiting
		local character = isAlive(player)
		if character then
			local arms = getLegs(character, Leg)
			if arms then
				local active = { } -- a table we've defined as active, which we'll store a bunch of characters within
				for i, v in next, game.Players:GetPlayers() do -- get all players in the game, and loop through them
					if v and v ~= player then -- if the player we're looking at in the list of the players in the game is NOT the player who sent this remote then
						local char = isAlive(v) 
						if char then -- is this player that we're looking at alive?
							active[#active + 1] = char -- add that character to the 'active list'
						end
					end
				end
		
				local length = arms.Size.y
				local bounding = character.PrimaryPart
				local ray = Ray.new(bounding.Position, bounding.CFrame.lookVector * (length + 3)) -- Fire a ray from our character's torso, towards its lookvector with a length of our arm size
				local hit, pos = game.Workspace:FindPartOnRayWithWhitelist(ray, active) -- find any parts we hit that's a part of a character's
				
				if hit and hit.Parent then
					local char = hit.Parent
					local hum  = hit.Parent:FindFirstChild 'Humanoid'
					if hum then
						hum:TakeDamage(20) -- Whatever we want to do to that other player's character
						game.Workspace.BOOM:Play()
						local vel = Instance.new 'BodyVelocity'
						vel.MaxForce = Vector3.new(0, 5000, 0)
						vel.Velocity = Vector3.new(0, 50, 0)
						vel.Parent = char.PrimaryPart
						delay(0.2, function()
							vel:Destroy()
						end)
					end
				end
			end
		end
	end
end)
1 Like