Best way to handle a seeking rocket server side?

I’m trying to recreate the popular dodgeball gamemode from team fortress 2. Here’s some footage of what it looks like(not mine btw): [TF2 Dodgeball] Unleashing Chaos on the Dodgeball Court - YouTube
As you can see there is one or more rockets that will track a nearby player until they are air blasted away or they hit an object. I know the best way to handle rockets is on the server as it allows for easy sync between all clients and less things that can be exploited. The mock up I have right now is a module script that can create a rocket that follows an instances position. I used a align position for the rocket to point towards the target and a linear velocity that then goes in the direction of the rockets look vector multiplied by the speed of the rocket. Using constraints seems to be the best option as it allows for smooth movement that is handled by the server and doesn’t lag like if I tweened or lerped it. I also make sure to set the network owner to the server so no hackers can mess with the rocket. The issue I’m coming across is trying to figure out how to get accurate airblasting. I know that in tf2 when a rocket is airblasted its direction vector is oriented to go in the direction of the mouse position. I’ve tried looking online on how exactly the airblast functions in tf2 but have found nothing useful. I want it to be when its airblasted it doesn’t feel so linear and you should be able to flick the rocket with your airblast and it should affect the rockets path to the target. If anyone knows a good way to handle this or how tf2 handles your help would be greatly appreciated.

The code for my module script:

local Rocket = {}
Rocket.__index = Rocket

--Services
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")
local TweenService = game:GetService("TweenService")

--Functions
function Rocket.new(rocketObj: BasePart | Model, parent: Instance, spawnPart: Instance, maxForce: number, responsiveness: number, rocketSpeed: number)
	local newRocket = {}
	setmetatable(newRocket, Rocket)
	
	--Clone rocket object
	newRocket.Rocket = rocketObj:Clone()
	newRocket.Rocket.Parent = parent
	newRocket.Rocket:SetNetworkOwner(nil)

	newRocket.Rocket.Position = spawnPart.Position
	newRocket.Rocket.Anchored = false
	
	--Create attachment and velocitys
	newRocket.Attachment = Instance.new("Attachment")
	newRocket.Attachment.Parent = newRocket.Rocket
	
	newRocket.LinearVelo = Instance.new("LinearVelocity")
	newRocket.LinearVelo.Parent = newRocket.Rocket
	newRocket.LinearVelo.Attachment0 = newRocket.Attachment
	newRocket.LinearVelo.MaxForce = maxForce
	newRocket.LinearVelo.RelativeTo = Enum.ActuatorRelativeTo.World
	newRocket.LinearVelo.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector
	newRocket.LinearVelo.VectorVelocity = Vector3.new(0, 0, 0)
	
	newRocket.AlignAng = Instance.new("AlignOrientation")
	newRocket.AlignAng.Parent = newRocket.Rocket
	newRocket.AlignAng.Mode = Enum.OrientationAlignmentMode.OneAttachment
	newRocket.AlignAng.Attachment0 = newRocket.Attachment
	newRocket.AlignAng.MaxAngularVelocity = maxForce
	newRocket.AlignAng.MaxTorque = maxForce
	newRocket.AlignAng.Responsiveness = responsiveness
	newRocket.AlignAng.CFrame = spawnPart.CFrame
	
	--Set speed and current target as start position
	newRocket.Target = spawnPart
	newRocket.Speed = rocketSpeed
	newRocket.DefaultResponsiveness = responsiveness

	return newRocket
end

function Rocket:Launch()
	self.UpdateConnection = RunService.Heartbeat:Connect(function(dt)
		if self.Target and self.Target.Parent ~= nil then
			local collisionTable = self.Rocket:GetTouchingParts()
			for index, part in pairs(collisionTable) do
				if part:IsA("BasePart") then
					self:Explode()
				end
			end
			
			local distance = (self.Rocket.Position - self.Target.Position).Magnitude
			if distance <= 5 then
				self.AlignAng.Responsiveness = 200
			else
				self.AlignAng.Responsiveness = self.DefaultResponsiveness
			end
			
			self.AlignAng.CFrame = CFrame.lookAt(self.Rocket.Position, self.Target.Position)
			self.LinearVelo.VectorVelocity = self.Rocket.CFrame.LookVector * self.Speed
		end
	end)
end

function Rocket:Explode()
	self.Rocket:Destroy()
	self:Destroy()
end

function Rocket:SetTarget(targetPart: Instance)
	self.Target = targetPart
end

function Rocket:AirBlast(direction: Vector3, newTarget)
	--self:SetTarget(newTarget)
end

function Rocket:AddSpeed(amount: number)
	self.Speed += amount
end

function Rocket:Destroy()
	if self.UpdateConnection then
		self.UpdateConnection:Disconnect()
	end
	
	self = nil
end

return Rocket

1 Like