Hello,
I’ve made a script that creates a projectile. This project will lose all of it’s velocity, turn invisible (to simulate being destroyed), emit particles, and then destroy itself. There are a few problems though:
When it hits something, it takes a second before it actually registers it being hit, leading to the explosion happening a bit after hitting the object.
Another strange thing is that the collision also registers a bit late or early randomly. This was also happening before I added collisions when the bullet just gets destroyed it would happen earlier or later every time.
More of a minor thing, but every time I start the game, the output says “Multiple StyleLinks under RobloxGui may result in undefined behavior”
The code is:
(Ignore the disorganized naming convention for the particles, this is more of a test game.)
game.ReplicatedStorage.RemoteEvent.OnServerEvent:Connect(function(player,mouseposition)
local particleemitter = game.Workspace.ParticleEmitter:Clone()
local particleemitter2 = game.Workspace.ParticleEmitter2:Clone()
local particleemitter3 = game.Workspace.Lines:Clone()
local particleemitter5 = game.Workspace.ParticleEmitterGlow:Clone()
local four = game.Workspace.ParticleEmitterRing:Clone()
local bullet = Instance.new("Part")
local attachment = Instance.new("Attachment")
local cantouch = true
attachment.Parent = bullet
bullet.Shape = Enum.PartType.Ball
bullet.Size = Vector3.new(1,1,1)
bullet.Parent = workspace
bullet.CFrame = player.Character.HumanoidRootPart.CFrame
bullet:SetNetworkOwner(player)
local velocity = Instance.new("LinearVelocity")
velocity.Attachment0 = attachment
velocity.Parent = bullet
velocity.MaxForce = 99999999999
velocity.VectorVelocity = (mouseposition - bullet.Position).Unit * 100
bullet.CanCollide = false
bullet.Material = Enum.Material.SmoothPlastic
particleemitter.Parent = attachment
particleemitter2.Parent = attachment
particleemitter5.Parent = attachment
four.Parent = attachment
particleemitter3.Parent = attachment
bullet.Touched:Connect(function(yes)
if cantouch == true and not (yes.Parent == player.Character) and not (yes.Parent.Parent == player.Character) then
cantouch = false
print(yes)
particleemitter2:Emit(10)
velocity.VectorVelocity = Vector3.new(0,0,0)
bullet.Transparency = 1
task.wait(0.05)
particleemitter5:Emit(25)
particleemitter3:Emit(25)
particleemitter:Emit(50)
four:Emit(1)
task.wait(2)
bullet:Destroy()
end
end)
end)
Making projectiles that use velocity like this will always pose this issue: replication lag makes the projectile not have precise hit detection.
You can look into 2 alternatives:
Keep velocity based projectiles, but they are handled client side so the hit reaction is instant. You will also get the added benefit of the projectile feeling more responsive since it will appear right when the player uses the ability/item that makes a projectile.
Use raycasting. There is a community resource module that already does this called FastCast, but the foundation isn’t complex and you can set it up yourself.
What’s the point of using velocity based projectiles if the max force for the LinearVelocity is 999999999? At that point, just manually update the CFrame or tween it. Still, I would recommend you go with a community projectile module like FastCast (also mentioned by @Giftdenz) which includes features such as custom bullets and custom hit effects.
I’ve done so much research on projectiles and holy hell they’re hard to make feel good.
If I remember correctly, this is happening because of network ownership being the player. The player is actually simulating the touch, and then sends a signal to the server. However in the time it takes for that signal to reach to the server and back, your projectile has already moved past the final hit position.
The easy fix would be to set the Network owner to nil (the server.) However this causes a visible stutter. To fix this, you could create two. A fake projectile for show which is set to the player, and the real projectile Network owned by the server which is invisible, and detects hits. Then, when the real projectile detects a hit, delete both.
Because then that’s better than CFrame or tweening, if ur using max force like that, you don’t need to manually update the CFrame or Tween it, the physics already handle that.
I find .Touched for projectiles to be extremely unreliable since there seems to be a random latency to it. Instead, I like to loop GetPartsBoundsInBox using the position and size of my projectile while it is travelling on every tick for consistent and accurate collision detection. Here’s an example in practice:
task.spawn(function()
local clone = grenade:Clone()
clone.Position = position
clone.Velocity = velocity
clone.Parent = workspace
clone:SetNetworkOwner(player)
while task.wait() do
local params = OverlapParams.new()
params.FilterDescendantsInstances = player.Character:GetChildren()
params.FilterType = Enum.RaycastFilterType.Exclude
local position = clone.CFrame
local size = clone.Size
local parts = workspace:GetPartBoundsInBox(position,size,params)
if #parts > 0 then break end
end
local cloneEffect = explosionEffect:Clone()
cloneEffect.Position = clone.Position
cloneEffect.Parent = workspace
end)