Tower Defense: Enemy System

It was

Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.None, false)

https://gyazo.com/00adfa0e6bf95fd5e2694de0d0b520d4

try rotating the spawn part 180 degrees so the enemeis dont have to turn around when they walk the path. It seems like they have a 50/50 chance of either turning left or right when they spawn which is whats causing them to be somewhat scattered.

They are supposed to be like that. They are path filled. Similar to TDS

I’m not sure I quite understand the issue? You want your enemies to look like this going down the path:
image

but not like this

image

I want them to be staggered rather then n a straight line.

They seem staggered to me? Could you show a simple example maybe of what you are trying to achieve?

Tower Defense Simulator. That’s the best example.

So further apart from one another and consistent?

Similar yes. To where they stagger and fill the path rather then all be right by each other.

How often do you call the move method?

every time they spawn.

local success, err = pcall(function()
	coroutine.wrap(EntityService.Move)(Enemy, EntityData)
end)

One idea I could say that might work is to do a magnitude check around each enemy and apply some external “force” on enemies that are too close to one another. Doing so will keep them spaced and staggered like you’re hoping for. You may have to do some extra checks so they dont go past the wall either.

how would i do that?

local workspace = game:GetService("Workspace")
local ServerStorage = game:GetService("ServerStorage")
local PhysicsService = game:GetService("PhysicsService")
local ServerScriptService = game:GetService("ServerScriptService")

local EntitiesFolder = ServerStorage:WaitForChild("Entities")
local EntityData = ServerScriptService:WaitForChild("Modules"):WaitForChild("EntityData")

local EntityService = {}

local MapName = nil

function EntityService.Move(Entity, EntityData)
	if not EntityData.humanoid then
		warn("Entity is missing Humanoid!")
		return
	end
	
	if MapName then
		local Map = workspace:FindFirstChild(MapName)
		if Map then
			local waypoints = Map:FindFirstChild("Waypoints")
			if waypoints then
				
				for i = 1, #waypoints:GetChildren() do
					local position = Map["Waypoints"][i].Position
					local spacing = EntityData.offsetSpacing/(i - 0.5)/1.35
					local targetPosition = position + Vector3.new(EntityData.offset.X * math.random(-spacing,spacing),0,0)
					
					EntityData.humanoid:MoveTo(targetPosition)
					EntityData.humanoid.MoveToFinished:Wait()
				end
				
				Entity:Destroy()
			else
				warn("Waypoints don't exist!")
				return "Waypoints don't exist!"
			end
		else
			warn("Map does not exist!")
			return "Map does not exist!"
		end
	end
end

function EntityService:Spawn(HordeData)
	for i, v in pairs(HordeData["Entities"]) do
		if EntitiesFolder:FindFirstChild(v) and EntityData:FindFirstChild(v) then
			local DataModule = require(EntityData[v])
			local Enemy = EntitiesFolder[v]:Clone()

			local Health = DataModule.Health
			local MaxHealth = DataModule.MaxHealth
			local isBoss = DataModule.Boss
			local walkSpeed = DataModule.Speed
			local isStealth = DataModule.Stealth

			print("Entity Name: " .. v)
			print("Health: " .. tostring(Health))
			print("Max Health: " .. tostring(MaxHealth))
			print("Is Boss: " .. tostring(isBoss))
			print("Walk Speed: " .. tostring(walkSpeed))
			print("Is Stealth: " .. tostring(isStealth))

			if MapName == nil then
				for _, child in pairs(workspace:GetChildren()) do
					if child:IsA("Folder") and child.Name ~= "Data" and child:FindFirstChild("Map") then
						print("Found a Map folder: " .. child.Name)
						MapName = child.Name
						break
					end
				end
			else
				print("Map name is not nil!")
			end

			Enemy.Humanoid.WalkSpeed = walkSpeed
			Enemy.Humanoid.MaxHealth = MaxHealth
			Enemy.Humanoid.Health = Enemy.Humanoid.MaxHealth

			Enemy:SetAttribute("Boss", isBoss)
			Enemy:SetAttribute("Stealth", isStealth)

			Enemy.Parent = game.Workspace.Data.Entities

			local spacing = 1.25 
			local maxOffset = 1.25
			local EntityData = {}
			EntityData.data = DataModule
			EntityData.humanoid = Enemy:WaitForChild("Humanoid")
			EntityData.offset = CFrame.new((math.random(-maxOffset * 100, maxOffset * 100) / 100) * spacing, 0, 0)
			EntityData.offsetSpacing = spacing
			EntityData.offsetMaxOffset = maxOffset
			
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Dead, false)
		--	Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.None, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Flying, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Landed, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Seated, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Physics, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Climbing, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Freefall, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
			Enemy.Humanoid:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
			
			Enemy.Humanoid.NameDisplayDistance = 0
			Enemy.Humanoid.HealthDisplayDistance = 0
			Enemy.Humanoid.DisplayName = " "
			Enemy.HumanoidRootPart.CFrame = game.Workspace[MapName].Start.CFrame * EntityData.offset-- game.Workspace.Map[map].Start.CFrame

			local success, err = pcall(function()
				for _, obj in pairs(Enemy:GetChildren()) do
					if obj:IsA("Part") or obj:IsA("MeshPart") then
						obj:SetNetworkOwner(nil)
						obj.CollisionGroup = "Entity"
					end
				end
			end)

			if success then
				print("Success!")
				
				local success, err = pcall(function()
					coroutine.wrap(EntityService.Move)(Enemy, EntityData)
				end)
				
				if success then
					print()
				else
					warn("Error:", err)
				end
				
			else
				warn("Nuh uh! Error mate:", err)
			end
			task.wait(HordeData["TimeBetween"])
		else
			print("Entity or Data not found for: " .. v)
		end
	end
end

return EntityService

whats the problem just to add wait before spawning another enemy

and instead of storing entityydata in a module just store it inisde the enitities itself

They just stagger pack into the straight line.

Are you still having issues with performance? If so I’d request you take a look at the microprofiler so we can get a better idea at what’s taxing everything.

As for ensuring zombies aren’t all in a straight line, you could add a horizontal offset value that gets +/-'d every time a zombie reaches a waypoint, that way instead of walking towards CFrame.new(10, 0, 0) they could be walking towards CFrame.new(10, 0, 0) * CFrame.new(horizontalOffset, 0, 0).

They’d still move roughly in the same place but with just enough subtle differences so that it looks more organic.

1 Like

How can I do that? Referring to the movement I don’t understand entity movement as I don’t often deal with that. Let me get home and I’ll send you a photo


@Kizylle