Bullet velocity takes effect a few milliseconds after instantiation

I have this bullet Instantiation script, which is supposed to put a bullet into the workspace and move it a set velocity. The problem ^^^ is that its velocity takes effect a few milliseconds after it appears in the workspace. There is no wait statement anywhere in the instantiation, and there arent any intense calculations.

    local bolt = bullet:Clone()
	if bolt:FindFirstChild("BodyVelocity") then
		local force = bolt.BodyVelocity
		local sound = bolt.Sound
		--local effect = bolt.Fire
		sound.Parent = script
		--effect.Parent = script
		bolt.CFrame = pos
        force.Velocity = bolt.CFrame.LookVector * speed
		bolt.Parent = game.Workspace
		Debris:AddItem(bolt, 5)
		local part = Instance.new("Part")
		part.Transparency = 1
		part.Anchored = true
		part.CanCollide = false
		part.Parent = game.Workspace
		part.Position = pos.p
		sound.Parent = part
		--effect.Parent = part
		Debris:AddItem(part, 1)
		--Debris:AddItem(effect, 0.2)
		warn("No BodyVelocity for bullet; add to prefab")

Video | | |


*projectile in video was resized for better visibility


Since the code seems like it runs on the server, it is usually expected that there will be a slight delay in terms of replication. If the bullet is meant for visual effects, you’re better off running the script on the client to reduce the load on the server and use rays to verify if the bullet will hit a target on the server.

The usual setup for guns and bullets would be to show the bullets and effects on clients only, the rest of the damage and verification logic happens on the server (meaning the server should not handle the bullet special effects)

Player Shoots Gun -> Show Bullet Locally -> Fire Server -> Server Tells Other Clients to Render Bullet -> Server Authenticates Shot

1 Like

Above reply is sound, and a principle that I have applied to a lot of areas of my gun system in my game. Integrating your own replication logic is often better than leaving it to default behaviour.

In general, I would suggest you keep all bullet and particle effects, sounds, and joint manipulation for animations (excluding Roblox Animations) completely client-side. This also allows you to manipulate the quality of these effects on other clients that they’re being replicated to. In shooters, for example, viewmodels of other players don’t have to be animated to the same level of detail.

what if i wanted to add drop to my projectile… being that the trajectory would be an arc, raycasting wouldnt necessarily work there. what would i use in this scenario?

[EDIT]: I know this isnt a permenant fix, and i will absolutely have to try something else eventually, but i have solved this problem (kind of) be setting network ownership to the player who has instatiated the bullet.

I would like to know, however, if it is possible to detect the client with the best capability of handling physics and use that client to have network ownership of all projectiles, and have this constantly updating so that if a new player joins/network owner leaves, this could be set again to the client that is most capable.

Bullets tend to move around 400-900m/s. How far are you planning for the bullets to go? There’s a reason a lot of gun systems don’t really account for it and a straight flight is an appropriate assumption.

have you ever played apex? i intend for the drop to be a little like what is seen there, like with the longbow dmr

Fair enough, one way would be to ray cast a few times from different points along the trajectory. Knowing the initial angle and speed and your rate of acceleration downwards you can easily get any given point along the trajectory. You’ll have to then raycast with reasonably small lengths along the trajectory, starting close to the weapon and moving away so you stop as soon as a hit is found. The length of the ray should be equal to the gap between them.

To determine the position of each point along the trajectory, use suvat equations in 3D. The lookvector at each point to use with the ray direction is the direction of the instantaneous velocity (again from the same suvat equations).

would there be any problem with the network ownership idea i had? (besides the obvious exploiter problem, im not sure if giving network ownership of a part to a client would allow that client to make changes to that part which would affect the server)

The bullet having an arc/gravity doesn’t make raycasting any less useless.

You’d want to model your projectile path using some simple kinematics equations, and cast a ray every update between the last position and the new one.

Would look something like this.

local CurrentPosition = CalculatePosition();

local LastPosition = self.LastPosition;

local ray = Ray.new(LastPosition,CurrentPosition-LastPosition);

As for the actual math, here is everything you’d need(taken from my projectile utils module I use for my games)

function ProjectileUtils:CalculateStartVelocity(pos, goal, power, accuracy)
	local maxAngle = rad125*math.clamp(1 - accuracy, 0, 1)
    return (CFrame.new(pos, goal)*CFrame.Angles(0, 0, rng_v:NextNumber(0, 2*pi))*CFrame.Angles(math.acos(rng_v:NextNumber(math.cos(maxAngle), 1)), 0, 0)).LookVector*power

function ProjectileUtils:CalculateProjectilePosition(startpos, startvelocity, timeSince,gravity)
	return startpos + startvelocity * timeSince + 0.5 * gravity * timeSince ^ 2;

function ProjectileUtils:CalculateProjectileVelocity(startpos, startvelocity, timeSince,gravity)
	return startvelocity + gravity * timeSince

function ProjectileUtils:CalculateTime(startPos,velocity,goalPos)
    return (startPos*Vector3.new(1,0,1) - goalPos*Vector3.new(1,0,1)).magnitude/(velocity*Vector3.new(1,0,1)).magnitude

If you don’t want to go through the effort in implementing this yourself, even though it’s not much effort,

This most likely does the same thing but is already done for you


What is the rad125 in the CalculateStartVelocity?

and the rng_v

is there some library i need to import?

and what is the goal parameter in the CalculateStartVelocity?

I cant quite wrap my head around this. I have tried toying around with it, and have gotten as far as this

        local CurrentPosition = pos --Pos is the CFrame if the part in which the bullet is instantiated
		local LastPosition = CurrentPosition
		for i = 1, 100 do wait(0.1)
			CurrentPosition = CurrentPosition + CurrentPosition.LookVector * speed/4 --Speed is a value passed to this script based on the weapon's stats
			local ray = Ray.new(LastPosition.p,CurrentPosition.p-LastPosition.p);
			local beam = Instance.new("Part", workspace) -- I make these parts so i can see the path of the rays
			beam.CanCollide = false
			beam.Anchored = true
			beam.Material = "Neon"
			beam.CFrame = LastPosition
			LastPosition = CurrentPosition

Nevermind, i figured it out. Thank you @tyridge77

Sorry for the bump

1 Like