Should Each npc have their own script, or be handled by one script?

Basically should’ve asked this early before i started making this, but is it better for all npcs to be handled by one script or each to have their own script?

doing the 2nd method currently and have some code for it

Code
-- Defined Stuff --
local MainFolder = game.Workspace:WaitForChild("Humanoids")
local PlayerFolder = MainFolder:WaitForChild("Players")
local EntityFolder = MainFolder:WaitForChild("Entitys")
local Extras = MainFolder:WaitForChild("Extras")

local Players = game.Players
local RunService = game:GetService("RunService")
local Pfs = game:GetService("PathfindingService")
--               --

local function GetNearestPlayer(NbotCharFrame)
	local NPlayer
	local Distance
	
	for _, Player in (Players:GetPlayers()) do
		if Player.Character and Player.Character.Humanoid and Player.Character.Humanoid.Health ~= 0 then
			local Mag = (Player.Character.HumanoidRootPart.Position - NbotCharFrame.Position).Magnitude
			
			if Distance then
				if Mag < Distance then Distance = Mag; NPlayer = Player.Character end
			else
				Distance = Mag; NPlayer = Player.Character
			end
		end
	end
	
	return NPlayer, Distance
end

local function MoveToMovement(Nbot, Distance)
	RunService.Heartbeat:Connect(function(DeltaTime)
		local NearestPlr, NPlrDistance
		
		NearestPlr, NPlrDistance = GetNearestPlayer(Nbot.Character)
		
		if NPlrDistance and NPlrDistance <= Distance then
			if NearestPlr and NearestPlr.Humanoid.Health > 0 then
				Nbot.Humanoid:MoveTo(NearestPlr.HumanoidRootPart.Position)
				print("Moving To" .. NearestPlr.Name)
			end
		else
			print("All Players out of range")
		end

	end)
end

local function PathFindingMovement()
	
end

local function NBotTouched(NBot, Thing, DmgDelay, NBotDamage, DelayTable)
	if Thing.Parent:FindFirstChild("Humanoid") then
		local Character = Thing.Parent
		if table.find(DelayTable, Character) then return end
		
		table.insert(DelayTable, Character)
		Character.Humanoid:TakeDamage(NBotDamage)
		task.wait(DmgDelay)
		table.remove(DelayTable, table.find(DelayTable, Character))
	end
end

local function NBotValueChange(NBot, NBotCharacter, Speed, JumpPower, CanBeKilled, Health, NBotPictureID)
	NBot.Humanoid.WalkSpeed = Speed
	NBot.Humanoid.JumpPower = JumpPower

	if CanBeKilled == true then
		NBot.Humanoid.MaxHealth = Health; NBot.Humanoid.Health = Health
	else
		local ff = Instance.new("ForceField", NBot)
		ff.Visible = false
		NBot.Humanoid.MaxHealth = math.huge; NBot.Humanoid.Health = math.huge
	end
	
	NBotCharacter.Back.ImageLabel.Image = "rbxassetid://" .. NBotPictureID
	NBotCharacter.Front.ImageLabel.Image = "rbxassetid://" .. NBotPictureID
end

local function NBotSetup(Child)
	print("NextBot Has Been Added: " .. Child.Name)
	local NextBot = Child
	local NextBotCharacterFrame = NextBot.Character
	local ValFol = NextBot.ChangeableValues
	
	local _Touched
	local Movement
	
	local NextBotDamage = ValFol.Damage.Value
	local NextBotSpeed = ValFol.Speed.Value
	local NextBotJumpPower = ValFol.JumpPower.Value
	local NextBotHealth = ValFol.Health.Value
	local CanBeKilled = ValFol.CanBeKilled.Value
	local Pic = ValFol.Image.Value
	local Dmgtime = ValFol.DmgCooldown.Value
	local Distance = ValFol.DetectionDistance.Value
	local SmartMovement = ValFol.SmartAi.Value
	local Touched = {}
	
	task.wait(.2)
	
	NextBot.PrimaryPart:SetNetworkOwner(nil)
	
	NBotValueChange(NextBot, NextBotCharacterFrame, NextBotSpeed, NextBotJumpPower, CanBeKilled, NextBotHealth, Pic)
	
	if SmartMovement == true then
		PathFindingMovement()
	else
		MoveToMovement(NextBot, Distance)
	end
	
	NextBotCharacterFrame.Touched:Connect(function(Thing)
		_Touched = NBotTouched(NextBot, Thing, Dmgtime, NextBotDamage, Touched)
		_Touched = nil
	end)
end

local function CharacterSetUp(Character)
	repeat task.wait() until workspace:FindFirstChild(Character.Name)
	Character.Parent = PlayerFolder
end

local function PlayerSetup(Player)
	Player.CharacterAdded:Connect(CharacterSetUp)	
end

Players.PlayerAdded:Connect(PlayerSetup)
EntityFolder.ChildAdded:Connect(NBotSetup)

I am not sure about that question but I recommend putting all those functions into a module script which the script calls so you can edit the npcs easily without having to go to each and every one of them pasting the same thing over and over again.

ex.

local zombieModule = require(path.ZombieAI) -- Requiring a module script that has all the functions in
local char = script.Parent

local plr = zombieModule.FindNearestPlayer(char) --[[Runs a function from the module with the argument of
the zombie it wants to run it to, the argument is there for the module to detect the nearest plr of the
zombie]]

zombieModule.FollowPlr(char, plr) -- runs another function to follow the player in the module with the zombie it wants to move in as well

with that you can easily edit anything in the module script and it will automatically be applied to every
zombie using the module as well

1 Like

I was thinking about something like this, althought i dont really know how to use module scripts that good to achieve this

I have a thing setup here for this
image

A bunch of changeable values in a folder which the setup gets all these values and distributes them out where they belong

local function NBotSetup(Child)
	print("NextBot Has Been Added: " .. Child.Name)
	local NextBot = Child
	local NextBotCharacterFrame = NextBot.Character
	local ValFol = NextBot.ChangeableValues
	
	local _Touched
	local Movement
	
	local NextBotDamage = ValFol.Damage.Value
	local NextBotSpeed = ValFol.Speed.Value
	local NextBotJumpPower = ValFol.JumpPower.Value
	local NextBotHealth = ValFol.Health.Value
	local CanBeKilled = ValFol.CanBeKilled.Value
	local Pic = ValFol.Image.Value
	local Dmgtime = ValFol.DmgCooldown.Value
	local Distance = ValFol.DetectionDistance.Value
	local SmartMovement = ValFol.SmartAi.Value
	local Touched = {}
	
	task.wait(.2)
	
	NextBot.PrimaryPart:SetNetworkOwner(nil)
	
	NBotValueChange(NextBot, NextBotCharacterFrame, NextBotSpeed, NextBotJumpPower, CanBeKilled, NextBotHealth, Pic)
	
	if SmartMovement == true then
		PathFindingMovement()
	else
		MoveToMovement(NextBot, Distance)
	end
	
	NextBotCharacterFrame.Touched:Connect(function(Thing)
		_Touched = NBotTouched(NextBot, Thing, Dmgtime, NextBotDamage, Touched)
		_Touched = nil
	end)
end
1 Like

Currently I would suggest just using CollectionService and running a function from a module for each tagged character

However, when Parallel Lua gets fully released, you’ll be able to run multiple scripts with multi-threading which will be significantly more optimized

Also, you can just do workspace:WaitForChild(Character.Name)

1 Like

A module is kind of like a function but can be accessed by any script using require. Module scripts are very easy to make and are very useful as well, I recommend reading up on modules as they will definitely help!

I have a thing setup here for this

What I meant by changing the script is that if you find a bug in it or just something you want to edit to make it better than you will have to do that for every zombie in the game

Creating a module function is almost like making a normal function but with a few changes at the start and end

ex module script:

local module = {} -- Creates the table that will have all the functions and stuff in it

module.myFunction = function(arg1, arg2) -- Dont rlly know why its like this but this is how to make a func in a module

local result = str(arg1) + " " + str(arg2)

return result -- This returns the result to the script calling it

end -- no ")" at the end of the end

return module -- returns the table to whoever is calling it with require

ex script:

local module = require(path.module) -- Gets the module table that has all the functions in it

local answer = module.result("Hello", 5) -- calls the function we made in the module table with 2 arguments and putting it to a variable which will store the return the module gives

print(answer) -- prints the var which stores the module functions return aka the words combined into a string

Output:
“Hello 5”

I am not good at explaining things so I really recommend to read about it on the page here

when i work with ai i personally control all of them with one script, with each having a config module script displaying the properties that would be varying from each ai.

Sorry for the late reponse, but its something like this?

local Nbot = {}

Nbot.FindNearest = function(NbotCharacter)
	local NPlayer
	local Distance

	for _, Player in (Players:GetPlayers()) do
		if Player.Character and Player.Character.Humanoid and Player.Character.Humanoid.Health ~= 0 then
			local Mag = (Player.Character.HumanoidRootPart.Position - NbotCharacter.Position).Magnitude

			if Distance then
				if Mag < Distance then Distance = Mag; NPlayer = Player.Character end
			else
				Distance = Mag; NPlayer = Player.Character
			end
		end
	end

	return NPlayer, Distance
end

return Nbot

And then something like

local NbotModule = require(game.ReplicatedStorage.NextBotHandler)

local NearestPlayer, NearestPlayersDistance = NbotModule.FindNearest(NextBotCharacterFrame)

Also another question, how do modules do in loops?

Alright, changed it into a module script

-- The NextBot Handler Module --
local Players = game.Players
local DisableAllBots = false
local Debug = false

local PathFindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")

-- Functions
local function FindNearest(NBC)
	local NearestPlayer
	local NearestDistance
	
	for _, Player in (Players:GetPlayers()) do
		if Player.Character and Player.Character.Humanoid and Player.Character.Humanoid.Health ~= 0 then
			local Mag = (Player.Character.HumanoidRootPart.Position - NBC.Position).Magnitude

			if NearestDistance then
				if Mag < NearestDistance then NearestDistance = Mag; NearestPlayer = Player.Character end
			else
				NearestDistance = Mag; NearestPlayer = Player.Character
			end
		end
	end
	
	return NearestPlayer, NearestDistance
end


-- Module
local Nbot = {}

Nbot.Setup = function(NextBot)
	NextBot.PrimaryPart:SetNetworkOwner(nil)
	local ValFol = NextBot.ChangeableValues

	local NextBotSpeed = ValFol.Speed.Value
	local NextBotJumpPower = ValFol.JumpPower.Value
	local NextBotHealth = ValFol.Health.Value
	local CanBeKilled = ValFol.CanBeKilled.Value
	local Pic = ValFol.Image.Value
	
	local NBotHumanoid = NextBot.Humanoid
	
	NBotHumanoid.WalkSpeed = NextBotSpeed
	NBotHumanoid.JumpPower = NextBotJumpPower
	
	if CanBeKilled == true then
		NBotHumanoid.MaxHealth = NextBotHealth; NBotHumanoid.Health = NextBotHealth
	else
		local ff = Instance.new("ForceField", NextBot)
		ff.Visible = false
		NBotHumanoid.MaxHealth = math.huge; NBotHumanoid.Health = math.huge
	end
	
	NextBot.Character.Front.ImageLabel.Image = "rbxassetid://" .. Pic
	NextBot.Character.Back.ImageLabel.Image = "rbxassetid://" .. Pic
	
	if not NextBot.Humanoid:GetAttribute("Active") then
		NextBot.Humanoid:SetAttribute("Active", false)
	end
	
	return "Setup Complete"
end

Nbot.Start = function(NextBot)
	local ValFol = NextBot.ChangeableValues
	NextBot.Humanoid.Active.Value = true
	
	local Stepped
	local NearestPlayer, NearestDistance
	
	Stepped = RunService.Heartbeat:Connect(function(DTime)
		print("D")
		if NextBot.Humanoid.Active.Value == true then
			NearestPlayer, NearestDistance = FindNearest(NextBot.Character)
			
			if NearestPlayer and NearestDistance then
				if NearestDistance <= ValFol.DetectionDistance.Value then
					NextBot.Humanoid:MoveTo(NearestPlayer.HumanoidRootPart.Position)
				end
			end
		else
			Stepped:Disconnect()
		end
	end)
end

Nbot.Stop = function(NextBot, Stepped)
	NextBot.Humanoid.Active.Value = false
end

Nbot.DisableAllBots = function(A)
	if A then
		print("This function doesnt require anything to be sent, disables all bots")
	end
	
	if DisableAllBots == true then
		print("Bots are already disabled")
		return "Failed, Bots Are Disabled Already"
	end
	DisableAllBots = true
	return "Bots Disabled"
end

Nbot.EnableAllBots = function(A)
	if A then
		print("This function doesnt require anything to be sent, enables all bots")
	end

	if DisableAllBots == false then
		print("Bots are already enabled")
		return "Failed, Bots Are Enabled Already"
	end
	DisableAllBots = false
	return "Bots Enabled"
end

return Nbot

and with a little testing with this script

local i = 0
local NextBotModule = require(game.ReplicatedStorage.NextBotHandler)
repeat wait() i += 20 until i == 20

local NextBot = script.Parent

local SetupStep = NextBotModule.Setup(NextBot)

print(SetupStep)
wait(2)

repeat task.wait() until SetupStep

NextBotModule.Start(NextBot)
print("Started NextBot")

repeat 
	print("NextBot Will Disable in " .. i .. " Seconds")
	task.wait(1)
	i -= 1
until i == 0

print("NextBotDisabled")
NextBotModule.Stop(NextBot)

It seems to work fine, although i do not know if ive done something that i could’ve done better

1 Like