Tower Defense Placement System

I want to achieve a Tower Defense Placement System similar to the one in Tower Defense Simulator. All towers shall be placed with a certain distance between them. Some towers can only be placed on higher places (e.g.snipers) while some can be only placed on the ground.
How would I make such a system? Is it worth to take a look at Ego Moose’s Placement System? If so how to check wheter the tower is on a higher place?

Any help is welcome :slight_smile:

1 Like

One method of determining where you can place something is by setting the ground color to something different on higher places.

Please note that I have never tried this before though so it is just theoretical.

I would definitely recommend looking into the Tutorial by ego moose it’s a really great and can be applied to many systems, but in terms of checking distance between towers and if its enough to place, magnitude or even artificial hit boxes can be used. Height can be determined in many ways using rays, getting distance from a plane, or even calculating Altitude in general

TD;lr there are many ways to go about creating a system like this, it does somewhat Boil down to what you find the most sufficient

1 Like

There are many different ways to handle this, but in my years of creating similar systems for home customization etc, I’ve never been hindered in just taking the simplest approach.

I would put all “towers” inside a folder or model in workspace. Whenever a player is placing a tower, any time the tower moves calculate the distance from the tower that is being placed to every tower inside of the model. If it’s far enough away, great. If it isn’t, recolor the tower that is being placed to red or whatever and don’t allow it to be placed down until the player moves it away.

As for the height, just setting the name of the part that the tower is being placed on to “SpecialHeight” or whatever has always worked for me by using mouse.Target.

Hope this helps

2 Likes

Here’s a system I made with the replies of the thread. Sorry for bumping this. I don’t see any placement system scripts in here and I think this will be helpful for devs that are starting out

You should edit this to your liking cuz this is made for my game:

local SYSTEM = {}

--//VARIABLES//--
local towerFolder = game.ReplicatedStorage:WaitForChild("PlaceClones") -- Default is defensive
local CreateEntityEvent = game.ReplicatedStorage:WaitForChild("Remotes").Events.CreateEntity

--//SERVICES//--
local RunService = game:GetService("RunService")
local PhysService = game:GetService("PhysicsService")

function NoCollide(model)
	for k,v in pairs(model:GetChildren()) do
		if v:IsA"BasePart" or v.Name == "Collider" then
			PhysService:SetPartCollisionGroup(v,"t")
		end
	end
end

function DistanceFromOtherTowers(tower, player)
	local defensiveFolder = game.Workspace[player.Team.Name.."DefensiveTroops"]
	local hasChild = false
	local canPlace = false
	
	local collideTroop = {}
	
	for i,troop in pairs(defensiveFolder:GetChildren()) do
		hasChild = true
		if troop:IsA("Model") then
			if (tower.PrimaryPart.Position - troop.PrimaryPart.Position).magnitude < 6 then
				table.insert(collideTroop, troop)
			end
		end
	end
	if hasChild and #collideTroop == 0 then
		return true
	end
	if hasChild == false and canPlace == false then
		return true
	end
end

function SYSTEM:PlaceTower(player, tower, mouse)
	if player.dataFolder.IsPlacing.Value == true then return end
	player.dataFolder.IsPlacing.Value = true
	
	-- CLONE THE TOWER FROM REPLICATED STORAGE FIRST
	local newClone = towerFolder:FindFirstChild(tower):Clone()
	newClone.Parent = game.Workspace.PlaceFolder
	local isPlaced = false
	local canPlace = false
	local terminate = false
	
	NoCollide(newClone)
	
	assert(mouse ~= nil, "BUILDING SYSTEM: MOUSE IS INVALID")
	assert(newClone ~= nil, "BUILDING SYSTEM: CANNOT CLONE SOMETHING THAT DOESN'T EXIST!")
	
	mouse.Button1Down:Connect(function()
		if canPlace == true and isPlaced == false then
			isPlaced = true
			local placeCFrame = newClone.PrimaryPart.CFrame
			newClone:Destroy()
			player.dataFolder.IsPlacing.Value = false
			CreateEntityEvent:FireServer("Archer", 1, "Defensive", placeCFrame) -- The placing event you should edit this.
			terminate = true
		end
	end)
	
	mouse.Button2Down:Connect(function()
		newClone:Destroy()
		isPlaced = false
		canPlace = false
		player.dataFolder.IsPlacing.Value = false
		terminate = true
	end)
	
	-- GET THE PLAYER'S MOUSE POSITION EVERYTIME THE SCREEN RENDERS
	RunService.RenderStepped:Connect(function()
		if isPlaced == false and terminate == false then
			local newcframe = mouse.Hit -- GET DA POSITION
			newClone.HumanoidRootPart.CFrame = CFrame.new(newcframe.X, your height, newcframe.Z)
			newClone.HumanoidRootPart.Orientation = Vector3.new(0,0,0)
			newClone.Collider.Transparency = 0.5
			
			if mouse.Target.Name == "PlacePart" and DistanceFromOtherTowers(newClone, player) and mouse.Target.Parent.Name == player.Team.Name then
				newClone.Collider.BrickColor = BrickColor.new("Sage green")
				canPlace = true
			else
				newClone.Collider.BrickColor = BrickColor.new("Really red")
				canPlace = false
			end
		end
		wait()
	end)
end

return SYSTEM

Put this in a module script btw. Edit this:

newClone.HumanoidRootPart.CFrame = CFrame.new(newcframe.X, your height, newcframe.Z)

This should be activated from the client. Should know how to use moduleScripts before attempting to use this. Go watch Alvin Blox’s tutorial on it.

Also the newClone.Collider is just a box that acts as the indictor.

8 Likes