Attempt to index nil with Position (OOP)

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")

local function GetRandomMob()
	local MobsFolder = ServerStorage:WaitForChild("Mobs"):GetChildren()
	local randomItem = MobsFolder[math.random(1, #MobsFolder)]
	
	return randomItem
end

local viewDistance = 20
local attackDistance = 5

local Mobs = {}
Mobs.__index = Mobs

function Mobs:Attack()
	print(self.Mob)
end

function Mobs:Chase(mob,mobRP)
	self.Mob = mob
	self.HumRP = mobRP
	
	
	
	local possiblePlayers = game:GetService("Players"):GetPlayers()

	local target = nil
	local distance = nil
	local direction = nil

	for i, player in pairs(possiblePlayers) do
		local character = player.Character

		if character then
			local distanceVector = (character.HumanoidRootPart.Position - self.HumRP.Position)

			if not target then
				target = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit
			elseif distanceVector.Magnitude < distance then
				target = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit

			end
		end
	end
	
	if target then
		if distance <= viewDistance and distance >= attackDistance then
			mob.Humanoid:Move(direction)
		else
			mob.Humanoid:Move(Vector3.new())
		end
	end
end

function Mobs.new(spawnPart, Health, Damage, Speed, Cooldown)
	local mobTemplate = setmetatable({}, Mobs)
	local clone = GetRandomMob():Clone()
	local cloneHum = clone:WaitForChild("Humanoid")
	
	mobTemplate.Health = Health
	mobTemplate.Damage = Damage
	mobTemplate.Speed = Speed
	mobTemplate.Cooldown = Cooldown
	mobTemplate.Mob = clone
	mobTemplate.HumRP = clone:WaitForChild("HumanoidRootPart")
	
	cloneHum.Health = Health
	cloneHum.WalkSpeed = Speed
	
	clone:SetPrimaryPartCFrame(CFrame.new(spawnPart.CFrame.Position))
	clone.Parent = workspace.Main.Mobs
	
	return mobTemplate
end

return Mobs
repeat wait() until game.Players.PlayerAdded

local Mobs = require(script:WaitForChild("Mobs"))
local GeneratedRooms = workspace.Main:WaitForChild("GeneratedRooms"):GetChildren()
local RunService = game:GetService("RunService")

local spawnParts = GeneratedRooms.SpawnParts

for i, Room in pairs(GeneratedRooms) do
	for i, spawnPart in pairs(Room.SpawnParts:GetChildren()) do
		local Mob = Mobs.new(spawnPart,100,10,12,3)
		RunService.Heartbeat:Connect(function()
			if Mob then
				
				Mobs:Chase(Mob,Mob.HumanoidRootPart)
			end
			
		end)
		
	end
	
end

currently learning OOP and trying to make attacking npcs with it, running into a problem where its saying that the npcs humanoidrootpart is nil ( attempt to index nil with position <---- nil = humanoidrootpart ). the humanoidrootpart is defined i made sure of that so i dont know whats happening, it prints and everything
the system is supposed to get a random npc from a folder, clone it into an open position (a part i have laid out) and then the npc should chase after the player
image

2 Likes

The Mob (Mobs) object being returned contains a member called HumRP, not HumanoidRootPart.

Mob.HumanoidRootPart will return nil.

Since you're new to object-oriented programming...

I see that you’re calling Chase() on the base object, instead of the object returned from your new() function. Because of this, for the first parameter, the Mob object that you want to use must be passed. You should not do this.

In your class Module, you’re setting the base object’s .__index to itself, as you should. The point of doing this, however, is so that the children objects will inherit from the base object. Hence, you should call Chase() on the child objects rather than the base object so that you don’t have to provide the object (self) that you want to use in that function.

The only time that you should have to provide an object (self) to be used is when you’re calling the method of that object with a period (.) instead of a colon (:). When a function is defined with a colon, it means that function is subject to a specific object because it uses the self that it’s being called from. Because of this, automatically the first parameter is the self.

Methods called with a period that are originally supposed to be called with a colon need the self (object) to be passed as the first argument because, as I already stated, functions defined with colons use the self that’s it’s being called from, and automatically store these selfs as their first parameters.

Your class Module should look more like this:

-- Your class' & module's name should be the name of the object
-- that you are returning.
local Mob = {}
Mob.__index = Mob

function Mob.new()
    -- Return a new object, as you're already doing.
end

-- (You don't need the parameters that hold information 
-- about the self/object that the method is being called from.)
function Mob:chase() 
    -- ...
end

So you should then revise your script utilizing the Module to look more like this:

-- ...
local mob = Mob.new(--[[arguments here]])
-- Since we're calling Chase() from the object we want to use,
-- we don't need to pass itself or its HumRP as arguments;
-- the function already knows about the object.
mob:chase()
2 Likes

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