CFrame from Raycast not setting properly during possible lag

I’m experiencing a couple issues with the spawning function in my Tower Defense game. Sometimes, when placing a tower, instead of being placed at where the mouse raycast is, it goes to the center of the map. This doesn’t happen when it isn’t laggy but does when there is lag. Is there any way to ensure that the CFrame is set and not be set at the center of the map?

function tower.Spawn(player, name, cframe, previous)
	local allowedToSpawn = tower.CheckSpawn(player, name, previous)
	local map = workspace.Map:FindFirstChildOfClass("Folder")
	
	if allowedToSpawn then
		
		local newTower
		local oldMode = nil
		local oldprice = 0
		if previous then
			oldMode = previous.Config.TargetMode.Value
			local heightA = (previous.HumanoidRootPart.Size.Y/2) + previous.Humanoid.HipHeight
			local offsetA = Vector3.new(0, heightA, 0)
			
			if previous:FindFirstChild("CostMultiplier") then
				oldprice = previous.CostMultiplier.Value
			end
			
			previous:Destroy()
			
			newTower = ReplicatedStorage.Towers.Upgrades[name]:Clone()
			
			local heightB = (newTower.HumanoidRootPart.Size.Y/2) + newTower.Humanoid.HipHeight
			local offsetB = Vector3.new(0, heightB, 0)
			
			local SizeDifference = offsetB - offsetA
			
			newTower.HumanoidRootPart.CFrame = cframe + SizeDifference
		else
			newTower = ReplicatedStorage.Towers[name]:Clone()
			player.PlacedTowers.Value += 1
			player.CurrentlyPlaced[name].Value += 1
			
			newTower.HumanoidRootPart.CFrame = cframe
		end
				
		local ownerValue = Instance.new("StringValue")
		ownerValue.Name = "Owner"
		ownerValue.Value = player.Name
		ownerValue.Parent = newTower.Config
		
		local targetMode = Instance.new("StringValue")
		targetMode.Name = "TargetMode"
		targetMode.Parent = newTower.Config
		targetMode.Value = oldMode or "First"
				
		ownerValue.Value = player.Name
		ownerValue.Parent = newTower.Config
		
		local height = (newTower.PrimaryPart.Size.Y/2) + newTower.Humanoid.HipHeight
		local offset = CFrame.new(0, -height, 0)
		
		newTower.Parent = map.Towers
		newTower.HumanoidRootPart:SetNetworkOwner(nil)
		newTower.PrimaryPart.Anchored = true

		game.ReplicatedStorage.Events.UpgradeButtonEvent:FireAllClients(player)
		
		placedTowerEvent:FireAllClients()

		for i, object in ipairs(newTower:GetDescendants()) do
			if object:IsA("BasePart") then
				PhysicsService:SetPartCollisionGroup(object, "Tower")
			end
		end
		
		if oldprice ~= 0 then
			player.Gold.Value -= math.floor(newTower.Config.Price.Value * oldprice)
		else
			player.Gold.Value -= math.floor(newTower.Config.Price.Value)
		end
		
		coroutine.wrap(tower.Attack)(newTower, player)
		
		return newTower
	else
		return false
	end
end
spawnTowerFunction.OnServerInvoke = tower.Spawn


function tower.CheckSpawn(player, name, previous)
	local errortype
	local towername
	local towerExists = ReplicatedStorage.Towers:FindFirstChild(name, true)
	if towerExists then
		towername = towerExists.Config.TowerName.Value
		local towerplaced = player.CurrentlyPlaced[towername].Value
			if towerplaced >= workspace.Info.Limits[towername].Value and not previous then
				local limitvalue = workspace.Info.Limits[towername].Value
				errortype = "towerlimit"
				player.ErrorSound:FireAllClients(errortype, name, limitvalue)
				return false
			end
		if towerExists.Config.Price.Value <= player.Gold.Value then
			local maxTowers = info.MaxTowers.Value
			if previous or player.PlacedTowers.Value < maxTowers then
				return true
			else
				errortype = "limit"
				player.ErrorSound:FireAllClients(errortype)
			end
		elseif previous and previous:FindFirstChild("CostMultiplier") then
			if towerExists.Config.Price.Value * previous.CostMultiplier.Value <= player.Gold.Value then
				local maxTowers = info.MaxTowers.Value
				if previous or player.PlacedTowers.Value < maxTowers then
					return true
				else
					errortype = "limit"
					player.ErrorSound:FireAllClients(errortype)
				end
			end
		else
			errortype = "afford"
			player.ErrorSound:FireAllClients(errortype)
		end
	else
		warn("That tower does not exist")
	end
	
	return false
end
requestTowerFunction.OnServerInvoke = tower.CheckSpawn

Sorry if the code is a little messy, I cut out any unneccessary parts that don’t effect the spawning of the tower. This is all in a module script by the way.

name is the tower’s name, cframe is the cframe given by the raycast, and previous is the previous tower if you upgraded the tower.

1 Like