Self returning nil values

This is my first time trying object orientated programming and… it’s not going too great so far. I have came across this error which I cannot seem to solve because every value in self returns nil. When I print self it prints all of my functions and my metamethod. If I print self.Car or self.Speed in any of my functions they return nil. Also if you have any suggestions on how I can improve my oop please don’t hesitate to reply!

script:

local SERVER_STORAGE = game:GetService("ServerStorage")
local REPLICATED_STORAGE = game:GetService("ReplicatedStorage")
local SERVER_SCRIPT_SERVICE = game:GetService("ServerScriptService")
local WORKSPACE = game:GetService("Workspace")
local RUNSERVICE = game:GetService("RunService")
local PLAYERS = game:GetService("Players")

local function calculateInfo(Speed, Distance)
	local Time = Distance / Speed

	return {Time, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}
end

local Cars = SERVER_STORAGE.Cars
local ReplicatedModules = REPLICATED_STORAGE.ReplicatedModules
local Animations = SERVER_SCRIPT_SERVICE.NonPlayer.Generation.Animations

local LOCALISED_TWEEN = require(ReplicatedModules.LocalTween)
local NPC_GENERATION = require(SERVER_SCRIPT_SERVICE.NonPlayer.Generation)
local USEFUL = require(REPLICATED_STORAGE.ReplicatedModules.Useful)
local MANAGER = require(SERVER_SCRIPT_SERVICE.PlayerData.Manager)
local TEMPLATE = require(REPLICATED_STORAGE.PlayerData.Template)


local function FadeVehicle(car, transparency)
	for _, object in pairs(car:GetDescendants()) do
		if (object:IsA("BasePart") or object:IsA("MeshPart")) and object.Name ~= "Root" then
			object.Transparency = 1 - transparency

			if object.Name ~= "Window" and object.Name ~= "Sign" then
				LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = transparency})
			else
				if transparency == 1 then
					LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = 1})
				else
					if object.Name == "Window" then
						LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = 0.7})
					elseif object.Name == "Sign" then
						LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = 1})
					end
				end
			end
		end
	end
end

local car = {}
car.__index = car

function car:UpdateNode(Node)
	print(self.Border, self.Car)
	local CarInfo = calculateInfo(self.Speed, (self.Car.PrimaryPart.Position - Node.Position).Magnitude)
	
	LOCALISED_TWEEN.Tween(self.Car.PrimaryPart, CarInfo, {CFrame = Node.CFrame})
	
	self.Car.CurrentNode.Value = Node

	if Node.Name == tostring(1) and Node.Parent.Name == "Nodes" then
		task.wait(CarInfo[1])
		self.Border.Gate.CarPresent.Value = true
	end
end

function car:Discard(FinalNode)
	LOCALISED_TWEEN.Tween(self.Car.PrimaryPart, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {CFrame = FinalNode.CFrame - Vector3.new(0, 0, 20)})
	
	FadeVehicle(self.Car, 1)
	
	task.wait(2)
	
	self.Car:Destroy()
end

function car:Dismiss(decision, playerData: TEMPLATE.PlayerData)
	local CurrentNode = self.Car.CurrentNode
	local NodesToTravel = USEFUL.GetPathFromDecision(self.Border, decision)
	local Player = PLAYERS[self.Border.Owner.Value]

	task.spawn(function()
		while task.wait(1.9) do -- this needs improving
			local NextNode = NodesToTravel:FindFirstChild(tostring(tonumber(CurrentNode.Value.Name) + 1))

			if NextNode ~= nil then

				if NextNode.Name == tostring(3) then
					self.Border.Gate.CarPresent.Value = false
					self.Border.Gate.GateControl.ClickPart.Doing.Value = false
				end

				car:UpdateNode(NextNode)
			else
				car:Discard(CurrentNode.Value)
				
				break
			end
		end
	end)

	MANAGER.AdjustCash(Player, playerData.Cash + 10)
end

function car.Spawn(player)
	local Border = USEFUL.GetBorderpostFromPlayer(player)
	local CarData = {}
	setmetatable(CarData, car)

	if Border then
		local NewCar = Cars.PlaceholderCar:Clone()
		
		CarData.Car = NewCar
		CarData.Speed = 25
		CarData.Border = Border
		CarData.EntryNode = CarData.Border.RoadRoots.Queue["1"]
		CarData.Border.CurrentCarIndex.Value += 1

		NewCar.Parent = CarData.Border.Cars
		NewCar.PrimaryPart.CFrame = CarData.EntryNode.CFrame + Vector3.new(0, 0, 20)
		NewCar.Name = tostring(CarData.Border.CurrentCarIndex.Value)

		wait()
		LOCALISED_TWEEN.Tween(NewCar.PrimaryPart, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {CFrame = CarData.EntryNode.CFrame})
		FadeVehicle(NewCar, 0)
		NPC_GENERATION.CreateDriver(NewCar)

		task.spawn(function()
			NewCar:WaitForChild("Driver").ClickPart.Border.Value = CarData.Border
		end)

		task.wait(3)
		NewCar.CurrentNode.Value = CarData.EntryNode
	end
	
	return CarData
end 

return car

The cardata is not nil in the .Spawn function but it’s nil in every other function

1 Like

you have to set metatable as local and return it like this:

local SERVER_STORAGE = game:GetService("ServerStorage")
local REPLICATED_STORAGE = game:GetService("ReplicatedStorage")
local SERVER_SCRIPT_SERVICE = game:GetService("ServerScriptService")
local WORKSPACE = game:GetService("Workspace")
local RUNSERVICE = game:GetService("RunService")
local PLAYERS = game:GetService("Players")

local function calculateInfo(Speed, Distance)
	local Time = Distance / Speed

	return {Time, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}
end

local Cars = SERVER_STORAGE.Cars
local ReplicatedModules = REPLICATED_STORAGE.ReplicatedModules
local Animations = SERVER_SCRIPT_SERVICE.NonPlayer.Generation.Animations

local LOCALISED_TWEEN = require(ReplicatedModules.LocalTween)
local NPC_GENERATION = require(SERVER_SCRIPT_SERVICE.NonPlayer.Generation)
local USEFUL = require(REPLICATED_STORAGE.ReplicatedModules.Useful)
local MANAGER = require(SERVER_SCRIPT_SERVICE.PlayerData.Manager)
local TEMPLATE = require(REPLICATED_STORAGE.PlayerData.Template)


local function FadeVehicle(car, transparency)
	for _, object in pairs(car:GetDescendants()) do
		if (object:IsA("BasePart") or object:IsA("MeshPart")) and object.Name ~= "Root" then
			object.Transparency = 1 - transparency

			if object.Name ~= "Window" and object.Name ~= "Sign" then
				LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = transparency})
			else
				if transparency == 1 then
					LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = 1})
				else
					if object.Name == "Window" then
						LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = 0.7})
					elseif object.Name == "Sign" then
						LOCALISED_TWEEN.Tween(object, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {Transparency = 1})
					end
				end
			end
		end
	end
end

local car = {}
car.__index = car

function car:UpdateNode(Node)
	print(self.Border, self.Car)
	local CarInfo = calculateInfo(self.Speed, (self.Car.PrimaryPart.Position - Node.Position).Magnitude)
	
	LOCALISED_TWEEN.Tween(self.Car.PrimaryPart, CarInfo, {CFrame = Node.CFrame})
	
	self.Car.CurrentNode.Value = Node

	if Node.Name == tostring(1) and Node.Parent.Name == "Nodes" then
		task.wait(CarInfo[1])
		self.Border.Gate.CarPresent.Value = true
	end
end

function car:Discard(FinalNode)
	LOCALISED_TWEEN.Tween(self.Car.PrimaryPart, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {CFrame = FinalNode.CFrame - Vector3.new(0, 0, 20)})
	
	FadeVehicle(self.Car, 1)
	
	task.wait(2)
	
	self.Car:Destroy()
end

function car:Dismiss(decision, playerData: TEMPLATE.PlayerData)
	local CurrentNode = self.Car.CurrentNode
	local NodesToTravel = USEFUL.GetPathFromDecision(self.Border, decision)
	local Player = PLAYERS[self.Border.Owner.Value]

	task.spawn(function()
		while task.wait(1.9) do -- this needs improving
			local NextNode = NodesToTravel:FindFirstChild(tostring(tonumber(CurrentNode.Value.Name) + 1))

			if NextNode ~= nil then

				if NextNode.Name == tostring(3) then
					self.Border.Gate.CarPresent.Value = false
					self.Border.Gate.GateControl.ClickPart.Doing.Value = false
				end

				car:UpdateNode(NextNode)
			else
				car:Discard(CurrentNode.Value)
				
				break
			end
		end
	end)

	MANAGER.AdjustCash(Player, playerData.Cash + 10)
end

function car.Spawn(player)
	local Border = USEFUL.GetBorderpostFromPlayer(player)
	local MetaCarData = setmetatable({}, car)
	if Border then
		local NewCar = Cars.PlaceholderCar:Clone()
		MetaCarData.Car = NewCar
		MetaCarData.Speed = 25
		MetaCarData.Border = Border
		MetaCarData.EntryNode = CarData.Border.RoadRoots.Queue["1"]
		MetaCarData.Border.CurrentCarIndex.Value += 1

		NewCar.Parent = MetaCarData.Border.Cars
		NewCar.PrimaryPart.CFrame = MetaCarData.EntryNode.CFrame + Vector3.new(0, 0, 20)
		NewCar.Name = tostring(MetaCarData.Border.CurrentCarIndex.Value)

		wait()
		LOCALISED_TWEEN.Tween(NewCar.PrimaryPart, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {CFrame = CarData.EntryNode.CFrame})
		FadeVehicle(NewCar, 0)
		NPC_GENERATION.CreateDriver(NewCar)

		task.spawn(function()
			NewCar:WaitForChild("Driver").ClickPart.Border.Value = MetaCarData.Border
		end)

		task.wait(3)
		NewCar.CurrentNode.Value = MetaCarData.EntryNode
	end
	
	return MetaCarData
end 

return car

It does the same thing: returns nil

function car.Spawn(player)
	local Border = USEFUL.GetBorderpostFromPlayer(player)
	local CarData = setmetatable({}, car)

	if Border then
		local NewCar = Cars.PlaceholderCar:Clone()
		
		CarData.Car = NewCar
		CarData.Speed = 25
		CarData.Border = Border
		CarData.EntryNode = CarData.Border.RoadRoots.Queue["1"]
		CarData.Border.CurrentCarIndex.Value += 1

		NewCar.Parent = CarData.Border.Cars
		NewCar.PrimaryPart.CFrame = CarData.EntryNode.CFrame + Vector3.new(0, 0, 20)
		NewCar.Name = tostring(CarData.Border.CurrentCarIndex.Value)

		wait()
		LOCALISED_TWEEN.Tween(NewCar.PrimaryPart, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {CFrame = CarData.EntryNode.CFrame})
		FadeVehicle(NewCar, 0)
		NPC_GENERATION.CreateDriver(NewCar)

		task.spawn(function()
			NewCar:WaitForChild("Driver").ClickPart.Border.Value = CarData.Border
		end)

		task.wait(3)
		NewCar.CurrentNode.Value = CarData.EntryNode
	end
	
	return CarData
end 

I forgot you have to name local variable “self”

function car.Spawn(player)
	local Border = USEFUL.GetBorderpostFromPlayer(player)
	local self = setmetatable({}, car)
	if Border then
		local NewCar = Cars.PlaceholderCar:Clone()
		
		self.Car = NewCar
		self.Speed = 25
		self.Border = Border
		self.EntryNode = self.Border.RoadRoots.Queue["1"]
		self.Border.CurrentCarIndex.Value += 1

		NewCar.Parent = self.Border.Cars
		NewCar.PrimaryPart.CFrame = self.EntryNode.CFrame + Vector3.new(0, 0, 20)
		NewCar.Name = tostring(self.Border.CurrentCarIndex.Value)

		wait()
		LOCALISED_TWEEN.Tween(NewCar.PrimaryPart, {2, Enum.EasingStyle.Linear , Enum.EasingDirection.InOut, 0, false, 0}, {CFrame = self.EntryNode.CFrame})
		FadeVehicle(NewCar, 0)
		NPC_GENERATION.CreateDriver(NewCar)

		task.spawn(function()
			NewCar:WaitForChild("Driver").ClickPart.Border.Value = self.Border
		end)

		task.wait(3)
		NewCar.CurrentNode.Value = self.EntryNode
	end
	
	return self
end

No you don’t self is just a normal word thats highlighted. The issue was that I wasn’t calling the functions using my constructed object but Im unsure of how to use .Spawn without constructing a new object

2 Likes

just try, this is not just highlighted word, im using this method currently.

I have fixxed it by creating a table for the player with the carData

car[player.Name] = CarData
VehicleControl[player.Name]:UpdateNode(Border.RoadRoots.Nodes["1"])
1 Like

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