Tween or Heartbeat projectile?

I am trying to make the most optimized projectile and I am unsure if I should use a tween-based movement or a run service heartbeat projectile. Example codes are shown below. Does anyone know what the difference is and which one is better?

image

2 Likes

why not just give physics a try?

1 Like

That was my original projectile, but there is stutter with linear velocity unless you set network host of the projectile to the player (bad idea)

i mean you can always make 2 projectiles, one server side, and then once client side…

thats what it already is, i know how to do client server replication but the stutter on the server side makes the projectile less consistent hence I would like to not use physics. also I’m pretty sure most combat games don’t use physics since they’re so bad

Well, I guess then to answer your original question, Either works, but I personally would use TweenService

You need to raycast every single time it steps, so you’ll need to use a heartbeat regardless.

1 Like

does tweening not altogether remove the need for raycasting?

Yes, but it’s less consistent and you’re better using heartbeats because at that level it’s more efficient. Plus, you can compensate for the framerate by multiplying it by the delta time.

touched events are notoriously wonky (on the server side) and as previously mentioned, tweens really cant compare to just using deltaTime, so your best bet is overlapparams + heartbeat
also, I recommend not worrying about optimization until there are complaints, because usually you wont be focusing on optimizing unless something is majorly wrong :eyes:

before i did client replication the ping of the server would 4x :cry: also I want to optimize since I plan for this game to be successful with many players.

I advise against overlapparams since it’s way less efficient, unless your projectile is a chair or something where collisions matter.

This is what I did three years ago:

local gun = script.Parent
local fireSound = gun.Fire
local reloadSound = gun.Reload
local fireRate = 0.1

local ammo = 25
local damage = 5
local maxAmmo = ammo
local MaxRange = 500
local ReloadTime = 3
local shake = 1 --in degrees
local bulletCount = 1

local reloading = false

local params = RaycastParams.new()
params.IgnoreWater = true
params.FilterDescendantsInstances = {gun}
params.FilterType = Enum.RaycastFilterType.Blacklist

function reload()
	if not reloading then
		reloading = true
		reloadSound:Play()
		ammo = maxAmmo
		wait(ReloadTime)
		reloading = false
	end
end

function fire()
	
	if reloading then
		return
	end
	
	local function r()
		return Random.new():NextNumber(-1, 1)*(shake/180)
	end
	
	local look = gun.CFrame.YVector	
	
	local look2 = (look+Vector3.new(r(), r(), r())).Unit
	
	local up = gun.CFrame.ZVector
	local pos = gun.Position+(look*gun.Size.Y/2)
	local dir = look2*MaxRange
	
	local result = workspace:Raycast(pos, dir, params)
	local part, position = nil, pos+dir
	if result then
		part, position = result.Instance, result.Position
	end
	
	local distance = (position-pos).Magnitude
	
	local bullet = Instance.new("Part", gun)
	game:GetService("Debris"):AddItem(bullet, 1)
	bullet.Size = Vector3.new(1, 1, 1)
	bullet.Anchored = true
	bullet.CanCollide = false
	bullet.Locked = true
	bullet.BrickColor = BrickColor.new("Cool yellow")
	bullet.Transparency = 0.5
	
	bullet.Name = "Bullet"
	local mesh = Instance.new("BlockMesh", bullet)
	mesh.Scale = Vector3.new(.04, .04, distance)
	bullet.CFrame = CFrame.lookAt(pos:Lerp(position, 0.5), position)
	
	spawn(function()
		for i = 0, 1, 0.01/(distance/MaxRange) do
			bullet.CFrame = CFrame.lookAt(pos:Lerp(position, i), pos+dir)
			mesh.Scale = Vector3.new(.04, .04, 4)
			game:GetService("RunService").Heartbeat:Wait()
		end
		bullet:Destroy()
	end)
	fireSound:Play()
	
	if part and part.Parent then
		local h = part.Parent:FindFirstChildWhichIsA("Humanoid")
		if h and h.Parent.Name~=script.Parent.Parent.Parent.Name then
			h:TakeDamage(damage)
			local tag = h:FindFirstChild('creator')
			if tag then
				tag:Destroy()
			end
			tag = Instance.new("ObjectValue", h)
			tag.Name = "creator"
			tag.Value = script.Parent.Parent.Parent
			delay(2, function()tag:Destroy()end)
		end
	end
	
	ammo-=1
	if ammo<=0 then
		reload()
	end
end

script.Parent.Parent.Activated:Connect(function()
	local activated = true
	spawn(function()
		while activated do
			for i=1, bulletCount do
				fire()
			end
			wait(fireRate)
		end
	end)
	script.Parent.Parent.Deactivated:Wait()
	activated = false
	reload()
end)

I’ll leave you guys to decipher it.

If the system is designed with optimization in mind in the first place, it will be significantly better to deal with in the future. And you won’t forget about it if people don’t complain.

2 Likes

good point, but I was mostly talking about semantics and like premature optimization (the type of debates where people would argue about using game.ServerScriptService or GetService)
added that extra detail bc I would remember times when I wouldn’t even start working on something bc I was so anxious that a small part of it would be slightly unoptimized :skull: dark times

sorry for the miscommunication haha

1 Like