So i'm making a system that runs through a list of "Tutorial Steps" during a tutorial, in sequence. the tutorial steps could be anything, but to guarantee some shared structure i've made an 'abstract' class TutorialStep. each unique type of tutorial step uses this as its metatable and __index, like this PressWASDStep for example:
TutorialStep.lua
local TutorialStepAbstract = {Name = "Tutorial Step"}
--setmetatable(TutorialStepAbstract, TutorialStepAbstract);
TutorialStepAbstract.isComplete = false;
function TutorialStepAbstract:new()
local o = {};
setmetatable(o, self);
self.__index = self;
--setmetatable(self, TutorialStepAbstract);
return o
end
function TutorialStepAbstract:IsComplete()
return self.isComplete;
end
function TutorialStepAbstract:Begin()
print("beginning step: "..self.Name);
end
return TutorialStepAbstract;
PressWASDStep.lua
local TutorialStep = require(game.ReplicatedStorage.PPatrol_ReplicatedStorage.Tutorial.TutorialStep);
local UserInputService = game:GetService("UserInputService");
local HelperTChat = require(game.ReplicatedStorage.PPatrol_ReplicatedStorage.Modules.HelperTChat);
local Step = {}
--Step.__index = Step;
function Step:new()
local o = TutorialStep:new();
--setmetatable(o, self);
--self.__index = self;
o.Name = "Press WASD Step";
o.isComplete = false;
o.WPressed = false;
o.APressed = false;
o.SPressed = false;
o.DPressed = false;
return o;
end
--function Step:IsComplete()
-- return self.isComplete;
--end
function Step:Begin()
task.spawn(function ()
local messagedismisscallback = HelperTChat.ShowNewMessage("'You are currently at the patient's artery! Let’s start by going over how to move. Use the WASD keys to move.'", "nouserdimiss", "large");
while not self.isComplete do
self.WPressed = self.WPressed or UserInputService:IsKeyDown(Enum.KeyCode.W);
self.APressed = self.APressed or UserInputService:IsKeyDown(Enum.KeyCode.A);
self.SPressed = self.SPressed or UserInputService:IsKeyDown(Enum.KeyCode.S);
self.DPressed = self.DPressed or UserInputService:IsKeyDown(Enum.KeyCode.D);
if self.WPressed and self.APressed and self.SPressed and self.DPressed then
self.isComplete = true;
print("completing step "..self.Name);
messagedismisscallback();
end
task.wait();
end
end, self)
end
return Step
the issue is, when i call CurrentStep:IsComplete()
, it looks for the function in PressWASDStep, doesn't find it, and invokes the IsComplete()
in TutorialStep, as it should. But the underlying property, self.isComplete
, is always false here, because TutorialStep's self.isComplete
and and PressWASDStep's self.isComplete
are different variables.
I could get around this by referring to isComplete
directly, but i'd rather figure out how to do this seemingly basic thing with lua, and i wouldn't be able to do anything else in :IsComplete()
if i chose to later. I could also reimplement :IsComplete()
in PressWASDStep, but then i'm losing the usefulness of a superclass/abstract class.
Is there a way to have the line self.isComplete = true;
in PressWASDStep modify the property of its metatable instead of defining/modifying a property of itself? i'm not sure if that's the right way to phrase it, but essentially i want to have one shared property isComplete
that is accessible to both TutorialStep and PressWASDStep.
Thanks for any help!
What you want is proper inheritance. Right now, when creating a Step
, you actually create a TutorialStep
instead. Sure, you then fill it with values but it's metatable and therefore it's "type" is a TutorialStep
.
Instead you want it to be a Step
(the commented-out code), and pass missing method calls to it's super.
This adapted (and not very beautiful) example "extends" TutorialStepAbstract.
local Step = setmetatable({ }, {__index = TutorialStep})
function Step:new()
local o = TutorialStep:new()
setmetatable(o, {__index = Step})
...
return o
end
When you now call IsComplete
it looks inside Step
, doesn't find the method and continues in its __index
. Notice the possibility of longer inheritance chains here. For every call, self
stays the same, thus "sharing the property". You should extend on that to have explicit access to super so you can extend a method instead of just overwriting.
But please, just use a premade library and don't reinvent the wheel here :) E.g.: https://github.com/Yonaba/30log