why doesnt my lua class implementation work? - lua

I have implemented OOP in my lua environment but it doesnt seem to be working.
I think it has something to do with how i am handling the __index and my improper use of require and module but I'm not 100% sure.
Here is the code:
Class = function( prototype )
local derived = {}
local derivedMT = {
--When indexing into a derived class, check the base class as well
__index = prototype,
--When invoking a class, return an instance
__call = function( proto, ... )
local instance = {}
local instanceMT = {
--When indexing into an instance, check the class hierarchy as well.
__index = derived,
--Calling instances is a no-no!
__call = function()
print( "WARNING! Attempt to invoke an instance of a class!" )
print( debug.traceback() )
return instance
end,
}
setmetatable( instance, instanceMT )
if ( instance.__constructor ) then
instance:__constructor( ... )
end
return instance
end,
}
setmetatable( derived, derivedMT )
return derived
end
And here is how I use it, the nil reference is a call to a base class function, that is the error/problem im having is it seems like the base class isn't being referenced.
require "Core.Camera.FreeCamera"
local T = Core.Camera.FreeCamera.FreeCamera(0,0,-35)
c = T:getObjectType() -- nil reference
print(c .." --Type" )
and here is Camera.lua the base class
local _G = _G
module(...)
local M = _G.Class()
Camera = M
function M:__constructor(x,y,z) --This never gets called.
--self.Active = false
--self.x = x
--self.y = y
--self.z = z
--self.ID = EngineManager:getCamera()
print("InCameraInstance_-_-_-_-_-__-_--_-__-_-_-_--_-_-_-_-_--_-_-_--_--__-_---_--_---__-")
end
function M:getObjectType()
return "camera"
end
And Finally Free Camera which attempts to inherit Camera.
local require = require
local _G = _G
module(...)
require "Core.Camera.Camera"
local M = _G.Class( _G.Core.Camera.Camera ) --Gross, lame might be the culprit
FreeCamera = M
function M:__constructor(x,y,z) ---WHOOPS, this does get called... the other one doesnt
self.Active = false
self.x = x
self.y = y
self.z = z
self.ID = _G.EngineManager:getCamera()
--_G.print("got Id of:" .. self.ID)
self:setCameraPosition(x, y, z, self.ID)
_G.print("<<<Camera in lua>>>")
end
I'm running out of ideas. any help would be appreciated.

Instead of:
local M = _G.Class( _G.Core.Camera.Camera )
you need to have:
local M = _G.Class( _G.Core.Camera.Camera.Camera )
First Camera is the directory name, second is the module name, third is the class name.
Edit: You can clean it up a bit like this:
local CameraModule = require "Core.Camera.Camera"
local M = _G.Class( CameraModule.Camera )

Related

Clone object in LUA

I have the next code and I'm trying to create a copy of the object Test:
local Test = {} do
local var1, var2 = {}
function Test:Init(val1, val2, val3)
var1.val1 = val1
var1.val2 = val2
var1.val3 = val3
end
function Test:Print()
print(var1.val1)
print(var1.val2)
print(var1.val3)
end
function Test:New(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
return obj
end
end
Then I'm creating my objects like this:
local obj1 = Test:New()
obj1:Init('a','b','c')
local obj2 = Test:New()
obj2:Init('c','d','e')
obj1:Print()
obj2:Print()
The result will be this:
c, d, e
c, d, e
As you can see the method New is not cloning correctly the object and is sharing the same instance. What am I doing wrong? I haven't cloned an object before and I'm having issues to understand how to do it.
I got that code from here: https://www.lua.org/pil/16.2.html
Thanks.
The problem occurs because you are just changing local variables, not the object itself.
When you do local Test = {} do you are not doing too much about the Test object, you are just creating the table and then creating a new scope which will be executed immediately, and inside this scope you have the variable var1, which will be shared through the scope, not the object.
The correct way to implement variables for each is actually creating fields on it:
local Test = {} do
function Test:Init(val1, val2, val3)
self.val1 = val1
self.val2 = val2
self.val3 = val3
end
function Test:Print()
print(self.val1)
print(self.val2)
print(self.val3)
end
function Test:New(obj)
obj = obj or {}
setmetatable(obj, self)
self.__index = self
return obj
end
end
Anyway setmetatable it's not about cloning objects, it implement shared behaviors and then you can create as many objects you want with those behaviors.

How to fix NPCs not spawning

I coded out some functions in a ModuleScript to be executed by another script. Here is the code
local module = {}
wavepause = game.ReplicatedStorage.Values.WavePauseLength.Value
trollanoid = game.ReplicatedStorage.Trollanoid
spawnpoints = workspace.Test1.Spawns:GetChildren()
function trollanoidsummon()
local chosenspawn = math.random(#spawnpoints)
local clone = trollanoid:Clone().Parent == workspace.Zombies
clone.HumanoidRootPart.CFrame = chosenspawn.CFrame
end
module.Wave1 = function()
trollanoid()
wait(1)
trollanoid()
wait(1)
trollanoid()
wait(1)
trollanoid()
end
return module
What I expected was the NPC trollanoids to appear on the map, but instead I got this error in the output:
17:50:19.011 ServerScriptService.WaveModule:14: attempt to call a Instance
value  -  Server  -  WaveModule:14
I dont know what I did wrong, please help me fix this. Any help is appreciated
The error message is telling you what's wrong:
You're trying to call an object. The only things you can call in Lua are functions and objects with the __call metamethod.
You are calling an object. Like mentioned above, you can only call functions and objects with __call metamethod.
Try this:
local module = {}
wavepause = game.ReplicatedStorage.Values.WavePauseLength
trollanoid = game.ReplicatedStorage.Trollanoid
spawnpoints = workspace.Test1.Spawns:GetChildren()
function trollanoidsummon()
local chosenspawn = spawnpoints[math.random(#spawnpoints)]
local clone = trollanoid:Clone().Parent = workspace.Zombies
clone.HumanoidRootPart.CFrame = chosenspawn.CFrame
end
module:SpawnNPC(amount, threshold)
threshold = threshold or 1
amount = amount or 4
for i = 1, amount do
if wavepause.Value then break end;
trollanoidsummon()
wait(threshold)
end
end
return module
To use the module you would do this:
local spawner = require(modulescriptpath);
spawner:SpawnNPC(5, 1);
I made a few minor changes. Let me know if you need help with any :)

I'm getting the error "attempt to index local self (a number value)

require 'class'
Paddle=class{}
function Paddle:init(x,y,width,height)
self.x=x
self.y=y
self.width=width
self.height=height
self.dy=0
end function Paddle:update(dt)
if self.dy < 0 then
self.y = math.max(`enter code here`0, self.y + self.dy * dt)
else
self.y=math.min(VIRTUAL_HEIGHT,-self.height,self.y+self.dy*dt)
end
end
function Paddle:render()
love.graphics.rectangle('fill',self.x,self.y,self.width,self.height)
end
I am following the course CS50 lecture 0 pong update 5, and the same code is working for the teacher. I don't know why this is happening neither understand the problem because it makes no sense. If you want, here's 'class'. This problem isn't happening in the other class I made called 'ball' which does exactly the same thing. I also defined self.dy, and it does have a value "0" so I don't know why it does that error and what that error means.
local function include_helper(to, from, seen)
if from == nil then
return to
elseif type(from) ~= 'table' then
return from
elseif seen[from] then
return seen[from]
end
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if to[k] == nil then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies `other' into `class'. keys in `other' that are already
-- defined in `class' are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- returns a deep copy of `other'
local function clone(other)
return setmetatable(include({}, other), getmetatable(other))
end
local function new(class)
-- mixins
class = class or {} -- class can be nil
local inc = class.__includes or {}
if getmetatable(inc) then inc = {inc} end
for _, other in ipairs(inc) do
if type(other) == "string" then
other = _G[other]
end
include(class, other)
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
if class_commons ~= false and not common then
common = {}
function common.class(name, prototype, parent)
return new{__includes = {prototype, parent}}
end
function common.instance(class, ...)
return class(...)
end
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
So this is the part where I call the update function, which is what someone said might be the error
function love.update(dt)
if love.keyboard.isDown('w') then
player1.dy=-PADDLE_SPEED
elseif love.keyboard.isDown('s') then
player1.dy=PADDLE_SPEED
else
player1.dy=0
end
if love.keyboard.isDown('up') then
player2.dy=-PADDLE_SPEED
elseif love.keyboard.isDown('down') then
player2.dy=PADDLE_SPEED
else
player2.dy=0
end
if gameState=='play' then
ball.update(dt)
end
player1.update(dt)
player2.update(dt)
This error is pretty clear on what you're doing wrong.
You're indexing local self, a number value.
That means that somewhere you're doing something like self.dy where self is not a table but a number and using the index operator . on numbers is not allowed as it does not make any sense.
The question is why self is not a table.
function myTable:myFunction() end
is short (syntactic sugar) for
function myTable.myFunction(self) end
and the function call
myTable:myFunction() is short for myTable.myFunction(myTable)
Please refer to the Lua manual.
Function Calls
Function Definitions
Find a function in your code that is defined with : and called with . and gets a number as first argument during that call.
That way a number ends up where you expect self.
I guess the error is in the main.lua which you did not provide.
There you have several calls to Paddle:update(dt). Writing myPaddle.update(dt) would cause that error for example. But I can't tell for sure as you did not provide your code.
But that it works for the teacher, but not for you is usually because you do something different/wrong.
Edit:
As you've provided more code I can tell for sure that the observed error is caused by
ball.update(dt)
player1.update(dt)
player2.update(dt)
This will put dt a number value, where the function expects self, the table ball.
replace it by
ball.update(ball, dt) or ball:update(dt)
player1.update(player1, dt) or player1:update(dt)
player2.update(player2, dt) or player2:update(dt)

self as param, and setting scope?

Can someone please help me with this.
local function TestPrice()
local obj1 = require("myObj")
local obj2 = require("myObj")
obj1:setPrice(30)
obj2:setPrice(40)
print(obj1.price) -- this prints '40'. Setting price on obj2 changes the price in obj1
end
and
-- myObj.lua
local M = {
price = -1;}
local function _setPrice(self, newprice)
self.price = newprice
-- todo other stuff
end
M.setPrice = _setPrice
return M
I thought that by setting self as a param it set the scope. Why does calling this function on obj2 update the value of obj1?
You need a function to create new object
-- myObj.lua
local M = {}
local function _setPrice(self, newprice)
self.price = newprice
-- todo other stuff
end
M.setPrice = _setPrice
M.__index = M
local function create_new_obj()
local obj = {price = -1}
setmetatable(obj, M)
return obj
end
return create_new_obj
-- main.lua
local function TestPrice()
local obj1 = require("myObj")()
local obj2 = require("myObj")()
obj1:setPrice(30)
obj2:setPrice(40)
print(obj1.price, obj2.price)
end
TestPrice()
In your code require load once, and second require give you same object. You should implement some kind of copy method.
-- myObj.lua
local M = {
price = -1;}
local function _setPrice(self, newprice)
self.price = newprice
-- todo other stuff
end
function M:copy()
return {["price"] = self.price, ["setPrice"]=_setPrice, ["copy"] = self.copy}
end
M.setPrice = _setPrice
return M

infinite recursion in __index metamethod

As I understand lua don't call __index unless the key wasn't found in the table so I have that code and it suffers from infinite recursion in the __index part which I don't get as both values used inside the __index function already exist in the table!?
This is basically a test script for trying to save the size of the table in a memory to retreive when # is called
do
local lenKey,originalKey = {},{}
fastKey = {}
fastKey.__len = function(t) return t[lenKey] end
fastKey.__index = function (t,k)
t[lenKey] = t[lenKey] +1
return t[oroginalKey][k]
end
fastKey.__newindex = function(t,k,v) t[originalKey][k] = v end
fastKey.__pairs = function ()
return function (t, k)
return next(t[oroginalKey], k)
end
end
function fastLen(t)
local proxy = {}
local c = 0
for _ in pairs(t) do
c=c+1
end
proxy[lenKey] = c
proxy[originalKey] = t
setmetatable(proxy,fastKey)
return proxy
end
end
n = fastLen{1,2,3,x=5}
--n:insert(1) -- here the __index is called and gets stackoverflow
print(#n)
You've got two typos in there: both the __index and __pairs functions contain oroginalKey instead of originalKey.

Resources