I have a ball projectile weapon that I’m using for my new game. The only problem is I’ve had quite of few complaints about how it’s hitreg is bad. I’m talking like sometimes when you hit somebody, it hits them and bounces them back but they take absolutely no damage. I’m reviewed my scripts for problems like what parts does it check to make sure its a player then still make sure to do damage even if the projectile hits something like a hat but to no avail.
Essentially, how should I go about fixing this problem with hit detection. My script is below:
function onTouched(hit)
if not hit or not hit.Parent then return end
local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
local tag = Ball:FindFirstChild("creator")
if tag and humanoid then
if not IsTeamMate(tag.Value, game.Players:GetPlayerFromCharacter(humanoid.Parent)) then
if IsSelf(tag.Value, game.Players:GetPlayerFromCharacter(humanoid.Parent)) then
tagHumanoid(humanoid)
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 then
if not humanoid:FindFirstChild("killed") then
local killed = Instance.new("IntValue")
killed.Name = "killed"
killed.Parent = humanoid
-- Give points here
end
end
if connection then connection:Disconnect() end
end
end
end
end
FYI: I know that the function works in most cases; I just have problems sometimes.
part.Touched is very “touchy”. It likes to decide when to fire, and when not to, and is unpreditable. To fix this, I suggest using a method of raycast hit detection. Create a ray in front of the bullet, and if that ray hits something, then activate your onTouched event. From what I understand, you’ll need to create a new ray every frame.
I hope this helps. If you need a code example, I can write one later this morning after my class.
I haven’t tested it, but it would look something like this:
local run = game:GetService("RunService")
function BulletHit(character)
-- your old code should work in here
end
function shoot()
local bullet = Instance.new("Part")
-- customize bullet here
-- new ray every 1/60th of second
run.HeartBeat:Connect(function ()
--Create new ray
local ray = Ray.new(bullet.CFrame, ((bullet.CFrame * CFrame.new(0,0,-3)).p - bullet.CFrame).unit * 2)
local part, position = workspace:FindPartOnRay(ray, {bullet, localPlayer.Character}, false, true)
if part.Parent:FindFirstChild("Humanoid") then
BulletHit(part.Parent)
end
end)
end
Even after adding a print statement into the BulletHit function, it never fires. Just to let you know this is a ball projectile moving at a certain velocity with a curving path.
EDIT: I was able to get your system working. Only there is one problem. I get an error saying argument #2 needs to be a Vector3 value. After configuring it, I can’t get it to work. The error is on the Ray.new line.
local tag = Ball:FindFirstChild("creator")
print(((Ball.CFrame * CFrame.new(0, 0, -3)) - Ball.CFrame).unit * 2)
local ray = Ray.new(Ball.Position, Vector3.new((Ball.CFrame * CFrame.new(0, 0, -3)) - Ball.CFrame).unit * 2)
local part, position = workspace:FindPartOnRay(ray, {Ball, tag.Value}, false, true)
I was not able to add this into my game. I’m going to reply with a full amount of code this time. Just to let you know it either didn’t even detect it hit something at all or it didn’t get past the “0” print statement.
All of this code is in a normal script (the server) inside of the projectile block. All of this code is run when the block is created.
local Ball = script.Parent
local upgrade1 = script.upgrade1.Value
local upgrade2 = script.upgrade2.Value
local upgrade3 = script.upgrade3.Value
local damage = 50
if upgrade2 == true then
damage = 63
end
local r = game:GetService("RunService")
local debris = game:GetService("Debris")
local last_sound_time = r.Stepped:Wait()
function IsTeamMate(Player1, Player2)
return (Player1 and Player2 and not Player1.Neutral and not Player2.Neutral and Player1.TeamColor == Player2.TeamColor)
end
function IsSelf(Found, Self)
if Found ~= Self then
return true
else
return false
end
end
local function FindCharacterAncestor(subject)
if subject and subject ~= game.Workspace then
local humanoid = subject:FindFirstChild('Humanoid')
if humanoid then
return subject, humanoid
else
return FindCharacterAncestor(subject.Parent)
end
end
return nil
end
local function OnExplosionHit(hitPart, hitDistance, blastCenter, tagValue)
if hitPart and hitDistance then
local character, humanoid = FindCharacterAncestor(hitPart.Parent)
if humanoid then -- Humanoids are tagged and damaged
if game.Players:GetPlayerFromCharacter(hitPart.Parent) then
humanoid:TakeDamage(100)
if not humanoid:FindFirstChild("killed") then
local killed = Instance.new("IntValue")
killed.Name = "killed"
killed.Parent = humanoid
if upgrade1 == true then
game.ServerScriptService.Server.GivePoints:Fire(tagValue, 2)
else
game.ServerScriptService.Server.GivePoints:Fire(tagValue, 1)
end
end
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 * 500 * hitPart:GetMass()
debris:AddItem(blastForce, 0.1)
end
end
end
end
local run = game:GetService("RunService")
function BulletHit(character)
print("-1")
if not character or not character.Parent then return end
local now = r.Stepped:Wait()
if (now - last_sound_time > .1) then
Ball.Boing:Play()
last_sound_time = now
else
return
end
print("0")
local humanoid = character:FindFirstChildOfClass("Humanoid")
local tag = Ball:FindFirstChild("creator")
print("1")
if tag and humanoid then
print("a")
if not IsTeamMate(tag.Value,game.Players:GetPlayerFromCharacter(humanoid.Parent)) then
print("b")
if IsSelf(tag.Value,game.Players:GetPlayerFromCharacter(humanoid.Parent)) then
print("c")
if upgrade3 == true then
local explosion = Instance.new('Explosion')
explosion.BlastPressure = 0 -- Completely safe explosion
explosion.BlastRadius = 1
explosion.ExplosionType = Enum.ExplosionType.NoCraters
explosion.Position = character:FindFirstChild("HumanoidRootPart").Position
explosion.Parent = game.Workspace
explosion.Hit:connect(function(hitPart, hitDistance) OnExplosionHit(humanoid.Parent:FindFirstChild("HumanoidRootPart"), hitDistance, explosion.Position, tag.Value) end)
script.Parent = explosion
tag.Parent = script
Ball:Destroy()
elseif upgrade3 == false then
print("yay")
tagHumanoid(humanoid)
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 and upgrade3 == false then
if not humanoid:FindFirstChild("killed") then
local killed = Instance.new("IntValue")
killed.Name = "killed"
killed.Parent = humanoid
if upgrade1 == true then
game.ServerScriptService.Server.GivePoints:Fire(tag.Value, 2)
else
game.ServerScriptService.Server.GivePoints:Fire(tag.Value, 1)
end
end
end
end
--if connection then connection:Disconnect() end
end
end
end
end
run.Heartbeat:Connect(function ()
local tag = Ball:FindFirstChild("creator")
local ray = Ray.new(Ball.CFrame.p, ((Ball.CFrame * CFrame.new(0,0,-3)).p - Ball.CFrame.p).unit * 3)
local part, position = workspace:FindPartOnRayWithIgnoreList(ray, {tag.Value, Ball}, false, true)
if part and part.Parent:FindFirstChild("Humanoid") then
print("Hit: " .. part.Parent.Name)
BulletHit(part.Parent)
end
end)
function tagHumanoid(humanoid)
local tag = Ball:FindFirstChild("creator")
if tag then
while(humanoid:FindFirstChild("creator")) do
humanoid:FindFirstChild("creator").Parent:Destroy()
end
local new_tag = tag:Clone()
new_tag.Parent = humanoid
debris:AddItem(new_tag, 1)
end
end
EDIT: Looking at it even more, I’ve noticed it only works when the player is hit in the head. I can’t seem to figure out why it doesn’t work with other body parts/hat hitboxes.
I’ve edited the code enough until it is at a state of working order. There is only one problem left to address. Sometimes when it hits a target the function that does damage can fire more than once killing them in one hit when they are not supposed to. I’m also getting this weird bug where the humanoid deletes itself when the NPC dies. Here is my code for the damage function:
function BulletHit(character)
if not character or not character.Parent then return end
local now = r.Stepped:Wait()
if (now - last_sound_time > .1) then
Ball.Boing:Play()
last_sound_time = now
end
local humanoid = character:FindFirstChildOfClass("Humanoid")
local tag = Ball:FindFirstChild("creator")
if tag and humanoid then
if not IsTeamMate(tag.Value,game.Players:GetPlayerFromCharacter(humanoid.Parent)) then
if IsSelf(tag.Value,game.Players:GetPlayerFromCharacter(humanoid.Parent)) then
if upgrade3 == true then
local explosion = Instance.new("Explosion")
explosion.BlastPressure = 0
explosion.BlastRadius = 1
explosion.ExplosionType = Enum.ExplosionType.NoCraters
explosion.Position = character:FindFirstChild("HumanoidRootPart").Position
explosion.Parent = game.Workspace
explosion.Hit:connect(function(hitPart, hitDistance) OnExplosionHit(humanoid.Parent:FindFirstChild("HumanoidRootPart"), hitDistance, explosion.Position, tag.Value) end)
script.Parent = explosion
tag.Parent = script
Ball:Destroy()
elseif upgrade3 == false then
print("yay: " .. character.Name)
tagHumanoid(humanoid)
humanoid:TakeDamage(damage)
if humanoid.Health <= 0 and upgrade3 == false then
if not humanoid:FindFirstChild("killed") then
local killed = Instance.new("IntValue")
killed.Name = "killed"
killed.Parent = humanoid
if upgrade1 == true then
game.ServerScriptService.Server.GivePoints:Fire(tag.Value, 2)
else
print("point: " .. humanoid.Name)
game.ServerScriptService.Server.GivePoints:Fire(tag.Value, 1)
end
end
end
end
end
end
end
end
Have you considered setting bullet’s NetworkOwnership to the client, this way you can smoothly move the part and detect it on clients end. Although it can be exploited.
The tests of the raycasts are on the server. But It just hits the player twice. Seeing as it creates a new ball when you throw the projectile, would it be smart to just say you can’t do anymore damage with that specific projectile after its already hit the player?