Bullets lagging the game badly

Hello, I am making a ‘Conscript’ enemy ai for my game, and its shooting script completely destroys the game and makes it unplayable with a large amount of them spawned in:


Before anyone mentions it, I have tried the FastCast module and after 2 hours of debugging and messing around with it, I have come to no conclusion, as I kept getting this error in it:

ReplicatedStorage.FastCast Redux.ActiveCast:121: attempt to index number with 'X'

Here is my current code (for the shooting and instancing bullets)

function AI.Gun(self)
	if self.AttackDB == false then
		self.AttackDB = true
		self.IdleAnim:Stop()
		self.WalkAnim:Stop()
		self.NPC:SetPrimaryPartCFrame(CFrame.new(self.NPC.HumanoidRootPart.Position,Vector3.new(self.target.HumanoidRootPart.Position.X,self.NPC.HumanoidRootPart.Position.Y,self.target.HumanoidRootPart.Position.Z)))
		local AttackAnim = self.hum:LoadAnimation(self.NPC.attackAnim)
		local ReloadAnim = self.hum:LoadAnimation(self.NPC.reloadAnim)
		local Damage = self.NPC.Gun.Damage.Value
		local fireRate = self.NPC.Gun.fireRate.Value
		local MagSize = self.NPC.Gun.MagSize
		local FullMagSize = self.NPC.Gun.FullMagSize.Value
		local firePoint = self.NPC.Gun.FirePoint
		local attackSound = self.NPC.Gun.Handle.attackSound
		local reloadSound = self.NPC.Gun.Handle.reloadSound

		local direction = self.target.HumanoidRootPart.Position - firePoint.Position
		local bulletSpread = direction + Vector3.new(math.random(-3,3),math.random(-3,3))
		local raycastParams = RaycastParams.new()
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
		raycastParams.FilterDescendantsInstances = {self.NPC,workspace.Projectiles,workspace.OreSpawningArea}
		local result = workspace:Raycast(firePoint.Position,direction,raycastParams)

		if result.Instance:FindFirstAncestor(self.target.Name) then
			if MagSize.Value > 0 then
				MagSize.Value -= 1
				AttackAnim:Play()
				attackSound:Play()
				AttackAnim:AdjustSpeed(2)
				local bullet = Instance.new('Part')
				local bulletVelocity = Instance.new('BodyVelocity')
				bullet.Position = firePoint.Position
				bullet.Size = Vector3.new(0.2,0.2,12)
				bullet.Material = Enum.Material.Neon
				bullet.Color = Color3.fromRGB(255, 225, 0)
				bullet.CFrame = CFrame.new(bullet.Position,self.target.HumanoidRootPart.Position)
				bullet.CanCollide = false
				bullet.Parent = workspace.Projectiles
				bullet:SetNetworkOwner(nil)
				
				Debris:AddItem(bullet,5)
				bulletVelocity.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
				bulletVelocity.Parent = bullet
				bulletVelocity.Velocity = bulletSpread.Unit * 5
				bullet.Touched:Connect(function(hit)
					if hit.Parent and hit.Parent:FindFirstChild('Humanoid') then
						if not hit:FindFirstAncestor(self.Team) then
							hit.Parent.Humanoid:TakeDamage(Damage)
						end
					end
					if not hit:FindFirstAncestor(self.Team) and hit.Transparency == 0 then
						bullet:Destroy()
					end
				end)
			else
				if self.ReloadDB == false then
					self.ReloadDB = true
					reloadSound:Play()
					ReloadAnim:Play()
					coroutine.wrap(function()
						ReloadAnim.Stopped:Wait()
						self.IdleAnim:Play()
					end)()
					reloadSound.Ended:Wait()
					MagSize.Value = FullMagSize
					self.ReloadDB = false
				end
			end
		else
			self.pathfindAgro()
		end

		coroutine.wrap(function()
			self.IdleAnim:Play()
			task.wait(fireRate)
			self.AttackDB = false
		end)()
		end
end

Please help, I am very willing to go back to fastcast but its impossible for me to do that right now because of that error.

EDIT: the slow speed of the projectiles in the video isn’t due to lag, I just configured it to be slow for debugging purposes and forgot to change it back

You might wanna check out PartCache, you can use it with FastCast for way better performance, this module does not use Instance.new() which can be quite expensive when creating a lot of parts like that.

1 Like

I know I tried that already but I didn’t see any tutorial of it that I could find and I don’t really know how to use it.

Here’s a tutorial by B Ricey. Of course if you prefer can also read the documentation which is pretty simple as there are only a few functions.

1 Like

Thanks, i’ll try to use that but, (sorry if I sound like a begger) i’ve made like 3 different fastcast scripts all following a tutorial or guide and none of them worked because of the error:

ReplicatedStorage.FastCast Redux.ActiveCast:121: attempt to index number with 'X'

and I don’t really know how to implement fastcast into my enemy because of this, and since I am making my enemies with a module script using oop, I don’t know how I can migrate my existing code into that, can you give me a basic guide on how I can do this?

here is my full script if necessary:

local Players = game:GetService('Players')
local RunService = game:GetService('RunService')
local PathfindingService = game:GetService('PathfindingService')
local ReplicatedStorage = game:GetService('ReplicatedStorage')
local CollectionService = game:GetService('CollectionService')
local RaycastHitbox = require(ReplicatedStorage.RaycastHitboxV4)
local maxDistance = math.huge
local Anims = script.Parent.R6Anims
local FastCast = require(ReplicatedStorage.FastCastRedux)
local PartCache = require(ReplicatedStorage.PartCache)
local Debris = game:GetService('Debris')

local AI = {}
AI.__index = AI

function AI.new(NPC,WalkAnim,AttackType)
	CollectionService:AddTag(NPC,NPC.Name)
	CollectionService:GetInstanceAddedSignal(NPC.Name):Connect(function(NPC)
		task.wait(5)
		local Team = NPC.Parent.Name
		local NPCProperties = setmetatable({},AI)
		local hum = NPC:WaitForChild('Humanoid')
		local humrp = NPC.HumanoidRootPart
		local WalkAnim = hum:LoadAnimation(WalkAnim)
		local RunOnce = false
		local AttackDB = false
		local ReloadDB = false
		local NPCHitbox = nil
		local idleAnim
		
		-- makes sure that the loadanimation has an animation object
		if NPC:FindFirstChild('idleAnim') then
			idleAnim = hum:LoadAnimation(NPC.idleAnim)
			NPCProperties.IdleAnim = idleAnim
			idleAnim:Play()
		end
		
		NPC.PrimaryPart:SetNetworkOwner(nil)
		
		NPCProperties.NPC = NPC
		NPCProperties.Humanoid = hum
		NPCProperties.humrp = humrp
		NPCProperties.hum = hum
		NPCProperties.NPCHitbox = NPCHitbox
		NPCProperties.AttackDB = AttackDB
		NPCProperties.RunOnce = RunOnce
		NPCProperties.Team = Team
		NPCProperties.WalkAnim = WalkAnim
		NPCProperties.ReloadDB = ReloadDB
		
		hum.Died:Connect(function()
			NPC:BreakJoints()
			Debris:AddItem(NPC,5)
		end)
		
		--Pathfinding
		local CoreOfFactory = workspace.CentreOfFactory

		local function findTarget()
			local team
			local MaxDistance
			if Team == 'Allies' then
				team = workspace.Enemies:GetChildren()
				maxDistance = 200
			else
				team = workspace.Allies:GetChildren()
				maxDistance = math.huge
			end
			local nearestTarget

			for _,enemy in pairs(team) do
				if enemy then
					if enemy.Humanoid.Health > 0 then
						local target = enemy
						local distance = (humrp.Position - target.HumanoidRootPart.Position).Magnitude

						if distance < maxDistance then
							nearestTarget = target
							MaxDistance = distance
						end
					end
				end
			end
			return nearestTarget
		end

		local function agro()
			WalkAnim:Play()
			repeat task.wait(0.05)
				local target = findTarget()
				
				if target and target.HumanoidRootPart then
					local pos1 = humrp.Position - Vector3.new(0,humrp.Position.Y,0)
					local pos2 = target.HumanoidRootPart.Position - Vector3.new(0,target.HumanoidRootPart.Position.Y,0)
					local distance = (pos1 - pos2).Magnitude
					local whenToStop = 4
					
					if AttackType == 'Gun' then
						whenToStop = 100
					end

					if distance <= whenToStop then
						WalkAnim:Stop()
							NPCProperties.target = target
							AI[AttackType](NPCProperties)
					else
						local targetVel = target.PrimaryPart.Velocity
						local leadBy = 7
						local lead
						
						
						if targetVel.Magnitude < 0.1 then
							lead = Vector3.new(0, 0, 0)
						else
							lead = targetVel.Unit * leadBy
						end
						hum:MoveTo(target.PrimaryPart.Position + lead)
					end
				end
			until target == nil or target.Humanoid.Health == 0 or (humrp.Position - target.HumanoidRootPart.Position).Magnitude > maxDistance
			WalkAnim:Stop()
		end
		
		local function pathfindAgro()
			
			local path = PathfindingService:CreatePath()
			
			local target = findTarget()

			local success, errorMessage = pcall(function()
				path:ComputeAsync(humrp.Position, target.HumanoidRootPart.Position)
			end)
			
			if idleAnim then
				idleAnim:Play()
			end
			
			WalkAnim:Play()
			if success and path.Status == Enum.PathStatus.Success then
				local waypoints = path:GetWaypoints()
				for _,waypoint in pairs(waypoints) do

					if waypoint.Action == Enum.PathWaypointAction.Jump then
						hum.Jump = true
					end

					if target and (humrp.Position - target.HumanoidRootPart.Position).Magnitude < math.huge then
						AI[AttackType](NPCProperties)
					end

					hum:MoveTo(waypoint.Position)
				end
			end
		end
		
		NPCProperties.pathfindAgro = pathfindAgro

		local function walkToCore()
			if Team == 'Enemies' then
				local path = PathfindingService:CreatePath()

				local success, errorMessage = pcall(function()
					path:ComputeAsync(humrp.Position, CoreOfFactory.Position)
				end)
				WalkAnim:Play()
				if success and path.Status == Enum.PathStatus.Success then
					local waypoints = path:GetWaypoints()
					for _,waypoint in pairs(waypoints) do

						if waypoint.Action == Enum.PathWaypointAction.Jump then
							hum:ChangeState(Enum.HumanoidStateType.Jumping)
						end

						if findTarget() and (humrp.Position - findTarget().HumanoidRootPart.Position).Magnitude < maxDistance then
							WalkAnim:Play()
							agro()
						end

						hum:MoveTo(waypoint.Position)
						hum.MoveToFinished:Wait()
					end
				end
				WalkAnim:Stop()
			else
				if findTarget() and (humrp.Position - findTarget().HumanoidRootPart.Position).Magnitude < maxDistance then
					WalkAnim:Play()
					agro()
				else
					WalkAnim:Stop()
				end
			end
		end

		walkToCore()
	end)
end

function AI.Punch(self)
	if self.AttackDB == false then
		self.AttackDB = true
		self.WalkAnim:Stop()
		self.NPC:SetPrimaryPartCFrame(CFrame.new(self.NPC.HumanoidRootPart.Position,Vector3.new(self.target.HumanoidRootPart.Position.X,self.NPC.HumanoidRootPart.Position.Y,self.target.HumanoidRootPart.Position.Z)))
		local AttackAnim = self.hum:LoadAnimation(Anims.Attack)
		local Damage = 15
		local delay = 1
		
		if self.RunOnce == false then
			self.RunOnce = true
			self.NPCHitbox = RaycastHitbox.new(self.NPC['Right Arm'])
			self.NPCHitbox:SetPoints(self.NPC['Right Arm'],{Vector3.new(0,0,1),Vector3.new(-1,1,3),Vector3.new(-3.5,1,3)})
		end

		AttackAnim:Play()
		self.NPCHitbox:HitStart()

		self.NPCHitbox.OnHit:Connect(function(hit,humanoid)
			if humanoid.Parent.Name ~= self.NPC.Name and not humanoid:FindFirstAncestor(self.Team) then
				humanoid:TakeDamage(Damage)
			end
		end)
		coroutine.wrap(function()
			AttackAnim.Stopped:Wait()
			self.NPCHitbox:HitStop()
			task.wait(delay)
			self.AttackDB = false
			self.WalkAnim:Play()
		end)()
	end
end

function AI.Sword(self)
	if self.AttackDB == false then
		self.AttackDB = true
		self.IdleAnim:Stop()
		self.WalkAnim:Stop()
		self.NPC:SetPrimaryPartCFrame(CFrame.new(self.NPC.HumanoidRootPart.Position,Vector3.new(self.target.HumanoidRootPart.Position.X,self.NPC.HumanoidRootPart.Position.Y,self.target.HumanoidRootPart.Position.Z)))
		local AttackAnim = self.hum:LoadAnimation(Anims.Sword)
		local Damage = 15
		local delay = 1

		if self.RunOnce == false then
			self.RunOnce = true
			self.NPCHitbox = RaycastHitbox.new(self.NPC['Right Arm'])
			self.NPCHitbox:SetPoints(self.NPC['Right Arm'],{Vector3.new(0,-1,-1),Vector3.new(0,-2,-2),Vector3.new(0,-1,-3),Vector3.new(0,-2,-4),
				Vector3.new(0,-1,1),Vector3.new(0,-2,0),Vector3.new(0,-1,1)})
		end

		AttackAnim:Play()
		self.NPCHitbox:HitStart()

		self.NPCHitbox.OnHit:Connect(function(hit,humanoid)
			if humanoid.Parent.Name ~= self.NPC.Name and not humanoid:FindFirstAncestor(self.Team) then
				humanoid:TakeDamage(Damage)
			end
		end)
		coroutine.wrap(function()
			AttackAnim.Stopped:Wait()
			self.IdleAnim:Play()
			self.WalkAnim:Play()
			self.NPCHitbox:HitStop()
			task.wait(delay)
			self.AttackDB = false
		end)()
	end
end

function AI.Gun(self)
	if self.AttackDB == false then
		self.AttackDB = true
		self.IdleAnim:Stop()
		self.WalkAnim:Stop()
		self.NPC:SetPrimaryPartCFrame(CFrame.new(self.NPC.HumanoidRootPart.Position,Vector3.new(self.target.HumanoidRootPart.Position.X,self.NPC.HumanoidRootPart.Position.Y,self.target.HumanoidRootPart.Position.Z)))
		local AttackAnim = self.hum:LoadAnimation(self.NPC.attackAnim)
		local ReloadAnim = self.hum:LoadAnimation(self.NPC.reloadAnim)
		local Damage = self.NPC.Gun.Damage.Value
		local fireRate = self.NPC.Gun.fireRate.Value
		local MagSize = self.NPC.Gun.MagSize
		local FullMagSize = self.NPC.Gun.FullMagSize.Value
		local firePoint = self.NPC.Gun.FirePoint
		local attackSound = self.NPC.Gun.Handle.attackSound
		local reloadSound = self.NPC.Gun.Handle.reloadSound

		local direction = self.target.HumanoidRootPart.Position - firePoint.Position
		local bulletSpread = direction + Vector3.new(math.random(-3,3),math.random(-3,3))
		local raycastParams = RaycastParams.new()
		raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
		raycastParams.FilterDescendantsInstances = {self.NPC,workspace.Projectiles,workspace.OreSpawningArea}
		local result = workspace:Raycast(firePoint.Position,direction,raycastParams)

		if result.Instance:FindFirstAncestor(self.target.Name) then
			if MagSize.Value > 0 then
				MagSize.Value -= 1
				AttackAnim:Play()
				attackSound:Play()
				AttackAnim:AdjustSpeed(2)
				local bullet = Instance.new('Part')
				local bulletVelocity = Instance.new('BodyVelocity')
				bullet.Position = firePoint.Position
				bullet.Size = Vector3.new(0.2,0.2,12)
				bullet.Material = Enum.Material.Neon
				bullet.Color = Color3.fromRGB(255, 225, 0)
				bullet.CFrame = CFrame.new(bullet.Position,self.target.HumanoidRootPart.Position)
				bullet.CanCollide = false
				bullet.Parent = workspace.Projectiles
				bullet:SetNetworkOwner(nil)
				
				Debris:AddItem(bullet,5)
				bulletVelocity.MaxForce = Vector3.new(math.huge,math.huge,math.huge)
				bulletVelocity.Parent = bullet
				bulletVelocity.Velocity = bulletSpread.Unit * 5
				bullet.Touched:Connect(function(hit)
					if hit.Parent and hit.Parent:FindFirstChild('Humanoid') then
						if not hit:FindFirstAncestor(self.Team) then
							hit.Parent.Humanoid:TakeDamage(Damage)
						end
					end
					if not hit:FindFirstAncestor(self.Team) and hit.Transparency == 0 then
						bullet:Destroy()
					end
				end)
			else
				if self.ReloadDB == false then
					self.ReloadDB = true
					reloadSound:Play()
					ReloadAnim:Play()
					coroutine.wrap(function()
						ReloadAnim.Stopped:Wait()
						self.IdleAnim:Play()
					end)()
					reloadSound.Ended:Wait()
					MagSize.Value = FullMagSize
					self.ReloadDB = false
				end
			end
		else
			self.pathfindAgro()
		end

		coroutine.wrap(function()
			self.IdleAnim:Play()
			task.wait(fireRate)
			self.AttackDB = false
		end)()
		end
end


return AI

edit: shooting script is on the very bottom

Unfortunately i am simply recommending theses modules because i know what they do, but i have never personally used them, you will have to learn how to use FastCast and PartCache as i won’t explain better than a dedicated tutorial. (also both are pretty amazing modules)

I’ll mess around in studio and see what i can do though, give me a few minutes.

1 Like

Line 121 is:

WalkAnim:Stop()
1 Like

Yeah I have a bunch of random WalkAnims as a crude attempt to get everything working, thanks for pointing that one out

should I give a place download? I think it would be easier

Thanks man! I messed around with partcache and fastcast some more and it actually worked!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.