How to make aim assist for this

Hello guys.

i recently started doing a cube that throws when your click, the cube start position depends of the mouse position. When the cube hits the ground, it spawns a cage at the impact of the cube.

But it’s quite complicated to aim to players so i tried doing aim assist, i tried curving the cube to the nearest player, spawning the cage near the player but nothing work. Here is the Server script that only throws the cube to the mouse position :

local replicatedStorage = game:GetService("ReplicatedStorage")
local cooldowns = {} -- Table to manage player cooldowns

-- Check or create the RemoteEvent
local remoteEvent = replicatedStorage:FindFirstChild("LaunchObjectEvent")
if not remoteEvent then
	remoteEvent = Instance.new("RemoteEvent")
	remoteEvent.Name = "LaunchObjectEvent"
	remoteEvent.Parent = replicatedStorage
end

local function getClosestPlayer(position, excludePlayer)
	local closestPlayer = nil
	local closestDistance = math.huge

	-- Find the closest player, excluding the player who launched the object
	for _, player in pairs(game.Players:GetPlayers()) do
		if player ~= excludePlayer and player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
			local playerPosition = player.Character.HumanoidRootPart.Position
			local distance = (playerPosition - position).magnitude
			if distance < closestDistance then
				closestDistance = distance
				closestPlayer = player
			end
		end
	end

	return closestPlayer
end

remoteEvent.OnServerEvent:Connect(function(player, position, direction)
	local character = player.Character
	if not character then return end

	local tool = character:FindFirstChildOfClass("Tool")
	if not tool then
		print("No tool found for player:", player.Name)
		return
	end

	local handle = tool:FindFirstChild("Handle")
	if not handle or not handle:IsA("BasePart") then
		print("No 'Handle' detected in the tool.")
		return
	end

	-- Check if the player is in cooldown
	if cooldowns[player] and tick() - cooldowns[player] < 1.5 then
		print("Cooldown active for", player.Name)
		return
	end
	cooldowns[player] = tick() -- Start cooldown

	-- Clone the Handle ONLY ONCE
	local thrownHandle = handle:Clone()
	thrownHandle.Parent = game.Workspace
	thrownHandle.Position = position + Vector3.new(0, 2, 0) -- Slightly above the player
	thrownHandle.CanCollide = true
	thrownHandle.Anchored = false

	-- Remove previous attachments (prevents bugs)
	for _, child in pairs(thrownHandle:GetChildren()) do
		if child:IsA("Weld") or child:IsA("Motor6D") then
			child:Destroy()
		end
	end

	-- Prevent uncontrolled rotation
	local gyro = Instance.new("BodyGyro")
	gyro.P = 5000
	gyro.MaxTorque = Vector3.new(math.huge, math.huge, math.huge)
	gyro.CFrame = thrownHandle.CFrame
	gyro.Parent = thrownHandle

	-- Add force to launch the object
	local velocity = Instance.new("BodyVelocity")
	velocity.Velocity = direction * 60
	velocity.MaxForce = Vector3.new(math.huge, math.huge, math.huge)
	velocity.Parent = thrownHandle

	-- Remove the force after a while to let physics take over
	game:GetService("Debris"):AddItem(velocity, 1.5)
	game:GetService("Debris"):AddItem(gyro, 1.5)

	-- Check for the closest player, excluding the one who launched the object
	local closestPlayer = getClosestPlayer(thrownHandle.Position, player)
	if closestPlayer then
		print("Trajectory adjusted towards", closestPlayer.Name)

		local targetPosition = closestPlayer.Character.HumanoidRootPart.Position
		local directionToTarget = (targetPosition - thrownHandle.Position).unit
		-- Slightly adjust the trajectory to curve towards the target
		velocity.Velocity = velocity.Velocity + directionToTarget * 20 -- Adds a small curve to the direction
	end

	print("Object cloned and launched by", player.Name)

	-- Debounce to prevent multiple cages
	local debounce = false

	thrownHandle.Touched:Connect(function(hit)
		if not debounce and hit and hit:IsA("BasePart") and hit.Position.Y < position.Y then
			debounce = true -- Block other triggers
			print("Impact detected on the ground!")

			-- Destroy the thrown object
			thrownHandle:Destroy()

			-- Check if the Cage exists in ReplicatedStorage
			local cageTemplate = replicatedStorage:FindFirstChild("Cage")
			if cageTemplate and cageTemplate:IsA("Model") then
				local newCage = cageTemplate:Clone()
				newCage.Parent = game.Workspace

				-- Find the height of the Cage to place it properly
				local cagePrimaryPart = newCage.PrimaryPart or newCage:FindFirstChildWhichIsA("BasePart")
				if cagePrimaryPart then
					newCage:SetPrimaryPartCFrame(CFrame.new(thrownHandle.Position + Vector3.new(0, 3, 0))) -- Spawn at the impact point
					newCage.PrimaryPart.Anchored = false --  The cage falls naturally
					print("Cage spawned and falling!")

					-- Once the cage is below 3, anchor it after 6 seconds
					wait(2)
					newCage.PrimaryPart.Anchored = true
					print("Cage properly placed on the ground!")

					-- Destroy the cage after 5 seconds
					wait(5)
					newCage:Destroy()
					print("Cage successfully destroyed!")
				else
					print("No PrimaryPart defined for the Cage.")
				end
			else
				print("No 'Cage' object found in ReplicatedStorage")
			end
		end
	end)
end)

Thanks to anyone who are gonna help me !

You should check out this tutorial!

It might make the aiming easier if it goes exactly towards the point where the player clicks.

If you use projectile motion like above, to add aim assist you’d just need to:

  • Get the click position (which is the target position)
  • Get any nearby players (e.g. the closest within 10 studs)
  • Move the target position towards the nearest character’s feet (possibility also considering their movement direction and the predicted flight time of the projectile, for example futureCharPosition = charPosition + flightTime * charVelocity).