How can I make a placement system?

Hello,

I am currently trying to make a placement system for a tower defense game, however I’ve never made some so I am quite confused on how to do this. I tried making a simple quick system to base it off of, however I don’t think it’s as efficient as I want it to be.

https://streamable.com/h6grx0
(ignore the prints i forgot to disable that)

As you can see in the video, what you’re placing follows the mouse, however the way to check if you actually can place is based on blocks. I decided to use blocks instead of regions as this is on the client, and if they’re not cheating, it will show where you can and can’t place blocks, however the server is the final decider if they can or can’t place the tower there.

My issue is the boundaries and the server. I don’t want the player to place very far from itself, which I’ve already got that coded. I also don’t want them to be able to place over a track, or on top of another tower, and this is where my issue is. I was thinking of using Region3, but I’m bad at Region3 so its gonna get confusing for me. I was also thinking of using a module called ZonePlus (ZonePlus v3.2.0 | Construct dynamic zones and effectively determine players and parts within their boundaries), however the main use of this is to use parts, and I can’t do that as mouse.Hit.Position registers those Parts.

Here is the client side of the code, let me know if you want to see the server side:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()

local placeRemote = game.ReplicatedStorage:WaitForChild("Remotes"):WaitForChild("PlaceTower")
local runService = game:GetService("RunService")
local contextAction = game:GetService("ContextActionService")
local YOffset = 1.3
local maxDist = 25

local defender = game.ReplicatedStorage.Towers.Defender:Clone()
defender.Parent = workspace

player.CharacterAppearanceLoaded:Wait()

local function pingServer(something, inputState) 
	if inputState == Enum.UserInputState.End then
		local magnitude = (player.Character.HumanoidRootPart.Position - defender.PrimaryPart.Position).magnitude

		if magnitude <= maxDist then
			local result = placeRemote:InvokeServer(defender:GetAttribute("ToPlace"), mouse.Hit.Position)

			print(result)
		end
	end
end

contextAction:BindAction("Place Tower", pingServer, true, Enum.UserInputType.MouseButton1)

local function changeColor(tower, status) 
	local color
	
	if status == true then 
		defender.Color.Value = true
		color = BrickColor.new("Really red")
	else
		defender.Color.Value = false
		color = BrickColor.new("Bright green")
	end
	
	for i,v in ipairs(defender:GetDescendants()) do 
		if v:IsA("BasePart") then 
			v.BrickColor = color 
		end
	end
end

mouse.TargetFilter = defender

runService.Heartbeat:Connect(function()
	local mousePos = mouse.Hit.Position
	local magnitude = (player.Character.HumanoidRootPart.Position - defender.PrimaryPart.Position).magnitude
	print(mouse.Target)
	
	if magnitude >= maxDist then
		if defender.Color.Value == false then 
			defender.Color.Value = true
			changeColor(defender, true)
		end
	elseif mouse.Target.Name == "Bad" then
		if defender.Color.Value == false then 
			defender.Color.Value = true
			changeColor(defender, true)
		end
	elseif mouse.Target.Name == "Good" then
		if defender.Color.Value == true then 
			defender.Color.Value = false
			changeColor(defender, false)
		end
	end
	
	defender:SetPrimaryPartCFrame(CFrame.new(mousePos.X, mousePos.Y + YOffset, mousePos.Z))
end)

Let me know of any solutions you got! Thanks :smiley:

edit: wrong video

1 Like

invisible hitbox on the track that goes upwards to the Y limit of ur placement, gettouchingparts, if colliding with another hitbox then apply colliding visuals

I originally was going to do that, however with blocks that are higher than floor level (noticeable levels), the tower levitates because it’s hitting the invisible block. I could make mouse.Target ignore, but I dont know if that will work for :GetTouchingParts(), and also with GetTouchingParts, Im not sure if it would be resource friendly if it ran in a loop

https://streamable.com/skn5pb

heartbeat gettouchingparts won’t cause a noticeable fps drop, as i do this for my game, the reason it’s floating is most likely because your origin is your camera and it’s hitting the hitbox, yea just blacklist/whitelist it and it’ll act as normal

Also i recommend raycasting over Mouse Target as i haven’t recently checked so correct me if im wrong but i believe mouse target can only have 1 instance and not a table of instances