Hey all! I’m working on a grenade, and I’ve run into an issue that needs to be resolved.
The grenade works and damages the player, but since I’m dealing damage using the explosions hit function it does way more damage than is intended, or doesn’t damage all players.
I added a debounce, but that made it so that only 1 person can be damaged by the grenade at a time. I added a boolean value to each player that determines whether they’ve been damaged, and if it is false they take damage from the grenade which sets the value to true.
explosion.Hit:Connect(function(hit,distance)
local hum = hit.Parent:FindFirstChildOfClass("Humanoid")
local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
if player then
print(player.Name.." Was hit by an explosion.")
if hum then
local damaged = player.isDamaged.Damaged
if damaged.Value == false then
print("damaged the player")
hum:TakeDamage((1 - distance / explosion.BlastRadius) * (maxDmg - minDmg) + minDmg)
wait(0.1)
damaged.Value = true
wait(1)
end
wait(3)
damaged.Value = false
end
end
end)
This is all wrapped inside a coroutine that creates an explosion that doesn’t destroy joints.
I need all of the players to take damage, but not be instantly killed by the explosion because the hit function triggers multiple times.
This is a server script.
Edit: I believe I got it working, all I had to do was move where I set my damaged boolean to true to above where the player first takes damage to make it more like a debounce.
I’ve recently ran into this. What you do is set BlastPressure to 0. Then with the explosion, you use the Touch event. Every player that get’s touched by the explosion you apply the damage to. The event will fire once for each player that was hit by it.
One interesting thing is that you can vary the percentage of the blast damage based on the distance the player is from the explosion. Since you have the coordinates of the explosion, the blast radius, and the player’s primary part CFrame, you can calculate the distance like this:
Hey, thanks for the quick reply. Unfortunately it seems that setting BlastForce (I’m assuming you mean BlastPressure) to 0 doesn’t cause the event to run just once per player. Because the player is touched by the explosion multiple times it runs multiple times dealing far too much damage than is input. Thanks for the math to calculate distance I’m sure it will be useful.
I went back and looked at the rocket script from Roblox’s Rocket Launcher that’s available in the Toolbox and they too are using a table to keep track of which players took damage and which didn’t. The asset ID for their tool is 47637.
why don’t you use a circle mesh similar to size of the explosion and set Transparency to 1 and clone it to the position of the grenade and inside it have a Touched event
Local Part = script.Parent
Part.Touched:Connect(function(hit)
local Player = game.Players:GetPlayerFromCharacter(hit.Parent)
if Player then
local DamageToTake = ((Player.HumanoidRootPart.Position - Part.Position) * (maxDmg - minDmg) + minDmg)
Player:FindFirstChildWhichIsA("Humanoid"):TakeDamage(DamageToTake)
end
end)
An easy fix is to change the script from checking each individual part of the character to checking to see if the hit part name is HumanoidRootPart. Since every character only has one it will only fire once for each player.
Don’t know if I did something wrong, but the player is still being hit multiple times despite it only checking for if the HRP was hit.
explosion.Hit:Connect(function(hit,distance)
local hrp = hit.Parent:WaitForChild("HumanoidRootPart")
local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
if player then
print(player.Name.." Was hit by an explosion.")
if hrp then
local hum = hrp.Parent:FindFirstChildOfClass("Humanoid")
local damaged = player.isDamaged.Damaged
if damaged.Value == false then
print("damaged the player")
hum:TakeDamage((1 - distance / explosion.BlastRadius) * (maxDmg - minDmg) + minDmg)
wait(0.1)
damaged.Value = true
wait(1)
end
wait(3)
damaged.Value = false
end
end
end)
Ignore my previous reply. Here’s how to fix it. You have to attach a script to the grenade (aka projectile) and then enable it so it will execute. When the grenade explodes, you have to reparent the script back to the tool instance (I’m assuming that you are using a grenade launcher) so it will keep executing. Then instead of placing a flag in the Player (AFAIK, that one is replicated), just use a table in the script. The table is only in that script. Here’s the code of the Rocket script from Roblox’s Rocket Launcher…
-----------------
--| Constants |--
-----------------
local BLAST_RADIUS = 8 -- Blast radius of the explosion
local BLAST_DAMAGE = 60 -- Amount of damage done to players
local BLAST_FORCE = 1000 -- Amount of force applied to parts
local IGNORE_LIST = {rocket = 1, handle = 1, effect = 1, water = 1} -- Rocket will fly through things named these
--NOTE: Keys must be lowercase, values must evaluate to true
-----------------
--| Variables |--
-----------------
local DebrisService = game:GetService('Debris')
local PlayersService = game:GetService('Players')
local Rocket = script.Parent
local CreatorTag = Rocket:WaitForChild('creator')
local SwooshSound = Rocket:WaitForChild('Swoosh')
-----------------
--| Functions |--
-----------------
-- Removes any old creator tags and applies a new one to the target
local function ApplyTags(target)
while target:FindFirstChild('creator') do
target.creator:Destroy()
end
local creatorTagClone = CreatorTag:Clone()
DebrisService:AddItem(creatorTagClone, 1.5)
creatorTagClone.Parent = target
end
-- Returns the ancestor that contains a Humanoid, if it exists
local function FindCharacterAncestor(subject)
if subject and subject ~= workspace then
local humanoid = subject:FindFirstChildOfClass('Humanoid')
if humanoid then
return subject, humanoid
else
return FindCharacterAncestor(subject.Parent)
end
end
return nil
end
local function IsInTable(Table,Value)
for _,v in pairs(Table) do
if v == Value then
return true
end
end
return false
end
-- Customized explosive effect that doesn't affect teammates and only breaks joints on dead parts
local TaggedHumanoids = {} -- **** <<<<< Damage Table
local function OnExplosionHit(hitPart, hitDistance, blastCenter)
if hitPart and hitDistance then
local character, humanoid = FindCharacterAncestor(hitPart.Parent)
if character then
local myPlayer = CreatorTag.Value
if myPlayer and not myPlayer.Neutral then -- Ignore friendlies caught in the blast
local player = PlayersService:GetPlayerFromCharacter(character)
if player and player ~= myPlayer and player.TeamColor == Rocket.BrickColor then
return
end
end
end
if humanoid and humanoid.Health > 0 then -- Humanoids are tagged and damaged
if not IsInTable(TaggedHumanoids,humanoid) then
print("Tagged")
table.insert(TaggedHumanoids,humanoid)
ApplyTags(humanoid)
humanoid:TakeDamage(BLAST_DAMAGE)
end
else -- Loose parts and dead parts are blasted
if hitPart.Name ~= 'Handle' then
hitPart:BreakJoints()
local blastForce = Instance.new('BodyForce', hitPart) --NOTE: We will multiply by mass so bigger parts get blasted more
blastForce.Force = (hitPart.Position - blastCenter).unit * BLAST_FORCE * hitPart:GetMass()
DebrisService:AddItem(blastForce, 0.1)
end
end
end
end
local function OnTouched(otherPart)
if Rocket and otherPart then
-- Fly through anything in the ignore list
if IGNORE_LIST[string.lower(otherPart.Name)] then
return
end
local myPlayer = CreatorTag.Value
if myPlayer then
-- Fly through the creator
if myPlayer.Character and myPlayer.Character:IsAncestorOf(otherPart) then
return
end
-- Fly through friendlies
if not myPlayer.Neutral then
local character = FindCharacterAncestor(otherPart.Parent)
local player = PlayersService:GetPlayerFromCharacter(character)
if player and player ~= myPlayer and player.TeamColor == Rocket.BrickColor then
return
end
end
end
-- Fly through terrain water
if otherPart == workspace.Terrain then
--NOTE: If the rocket is large, then the simplifications made here will cause it to fly through terrain in some cases
local frontOfRocket = Rocket.Position + (Rocket.CFrame.lookVector * (Rocket.Size.Z / 2))
local cellLocation = workspace.Terrain:WorldToCellPreferSolid(frontOfRocket)
local cellMaterial = workspace.Terrain:GetCell(cellLocation.X, cellLocation.Y, cellLocation.Z)
if cellMaterial == Enum.CellMaterial.Water or cellMaterial == Enum.CellMaterial.Empty then
return
end
end
-- Create the explosion
local explosion = Instance.new('Explosion')
explosion.BlastPressure = 0 -- Completely safe explosion
explosion.BlastRadius = BLAST_RADIUS
explosion.ExplosionType = Enum.ExplosionType.NoCraters
explosion.Position = Rocket.Position
explosion.Parent = workspace
-- Connect custom logic for the explosion
explosion.Hit:Connect(function(hitPart, hitDistance) OnExplosionHit(hitPart, hitDistance, explosion.Position) end)
-- Move this script and the creator tag (so our custom logic can execute), then destroy the rocket
script.Parent = explosion
CreatorTag.Parent = script
Rocket:Destroy()
end
end
--------------------
--| Script Logic |--
--------------------
SwooshSound:Play()
Rocket.Touched:Connect(OnTouched)
I don’t really understand why Roblox sanctioned weapons are using tags. Maybe I’ll ask that in a separate question.
Thanks for the reply, I ended up fixing the issue using the Damaged boolean all I had to do was move it to the top of the script to work more like a debounce. Not having it at the top was dumb on my part to begin with. I would use a table, but I can use the damaged boolean for other things aswell with minimal work.
Hey, just a heads up. Vector3 and Vector2 objects have a built in property that describes their magnitude (see links.) A quicker and tidier way to get the distance between two points in a 2D or 3D space would be to subtract the vectors by one another and then fetching the magnitude of the difference.
Example:
local Point1 = Vector3.new(1,10,4)
local Point2 = Vector3.new(12,48,1)
print((Point1-Point2).Magnitude) -- ~39.673