Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Beginner Question - Classes/OOP in LUA — Gideros Forum

Beginner Question - Classes/OOP in LUA

gyrospgyrosp Member
edited November 2012 in General questions
Hi :).

I'm new to Lua and I have some difficulties in understanding Classes/OOP in Lua.

I'm just trying to create a simple "player"-class. This is what I have so far:

--[[
filename : Player.lua
--]]

Player = Core.class()

function Player:init()
self.health = 100
self.speed = 3
end

function Player:startRunning()
end

function Player:stopRunning()
end

I create the instance and call a method like this:

require ("Class/Player")

local player = Player.new()
player.startRunning()


Is this the right approach? How do I declare local variables like health and access them from outside the class?
I've read several articles about this issue but after that I'm more confused than before ;). There are many different oppinions out there.

Another thing. What's the point with the . and : syntax? I have seen some methods/functions like this

function object.print(self)
print("Hello from ", self)
end

What's the difference between function object.print(self) and function object:print(self)?

Comments

  • plamenplamen Member
    edited November 2012 Accepted Answer
    you don't need "require ("Class/Player")" and "player.startRunning()" should be "player:startRunning()" if you want to access health its done like this: player.health=100 or player.speed=200 :-)
    question about . and : here it is: object:method() - calls object method and object.property - access to objects property.
  • techdojotechdojo Guru
    Accepted Answer
    When you use the single dot ( . ) method to call the function in the table, it does just that, it calls the function with the parameter list as specified.

    When you use the double dot ( : ) method, the lua interpreter actually puts "self" as a hidden first parameter so
            object.print(self)
    Is the same as
            object:print()
    Likewise when defining functions like
        function myClass:printSelfAndA(a)
            print("Self = ",self,a)
        end
    Will work because "self" is actually the first "hidden" parameter.

    You can call the above function in two ways
         object = myClass.new()
     
         object:printSelfAndA(a)
         object.printSelfAndA(object,a)
    In Lua "self" is a reference to the table that contains the function being called - like "this" in C++.

    If the above has completely confused you then just remember it like this.

    For accessing variables stored in a table use ( . )
    For defining and calling functions stored in a table use ( : )

    You can then always use "self" within a function to refer to the current "instance" of the class for accessing variables and related functions.

    Likes: gyrosp

    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
    +1 -1 (+1 / -0 )Share on Facebook
  • Thanks for your answers, it's getting clearer now :).

    But I have two more questions left ;)...

    1. Why don't I need
    "require ("Class/Player")"
    ? Is this some sort of 'Gideros-Magic'?
    2. So I don't need to declare my class variables in Lua? I just write self.myVar in my class, this is all?
  • MellsMells Guru
    edited November 2012 Accepted Answer
    Hi @gyrosp

    1. Yes, unless you specifically 'Exclude from execution" your file (right click on your file in the project hierarchy)
    2. The following works :
    self.myVar = 1
    self.myVar = "test"
    local x, y, z = 2, 4, 6
    self.myVar = {1, "apple", {x, y, z}}
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • This seems to be a great forum :D. Thank you all very much for your fast and competent answers :)!
  • @gyrosp Lua gives unbelievable freedom and with freedom comes responsibility :)

    Likes: aimoi

    +1 -1 (+1 / -0 )Share on Facebook
  • @gyrosp Lua gives unbelievable freedom and with freedom comes responsibility :)
    You are so right :D
  • techdojotechdojo Guru
    edited November 2012 Accepted Answer
    1. You don't need "require" because the Gideros IDE/Engine will run all of the scripts in the project in the order specified (see the execution order tab), so effectively they will have already been "loaded" by the time your code runs.

    If you "exclude" a file from execution, then it won't be run and Lua will not know anything about your file ( and will then need "requiring" )

    2. When Lua run's "self.foo = 10" it''s actually setting the variable "foo" in the "current" table to equal "10", if that variable doesn't exist then it's added to the table, so effectively by running that statement you are both "declaring" and initialising the variable in a single step.

    Because your working on "self" ie the "instance" of the table that's created when you call Class.new() then each "instance" of the table will be initialised to contain the same variables.

    You mentioned "class" variables, in this instance I'm talking about "instance" variables - one's where each object "instance" has their own separate copy. I would normally use the term "class" variable to mean a (in C++ terms) static variable, one that is shared across all instances of a class. You can create a static class variable by simply declaring a variable as being "local" at the top of the file, this will also mean it can only be accessed from within the file (which effectively makes it "private" )

    see the following...
    -- File - MyPlayerClass.lua
     
    MyPlayer = Core.class(Sprite)      -- I usually derive all onscreen objects from a "Sprite"
     
    -- these variables are accessible by ANY instance of a MyPlayer object, 
    -- changing the value in one instance will mean ALL instances see the new value
    -- also because they are "local" to this file, no other object can see or access them!
     
    local privateSharedVarA = 1      
    local privateSharedVarB = 2
     
    -- again a "static" function, accessible to any MyPlayer instance, might be more efficient
    -- as there is no table lookup to perform, however this wouldn't be able to access 
    -- MyPlayer instance variables unless they are passed as parameters...
     
    local function privateFunctionA(a, b)
         return a+b
    end
     
    -- this static function CAN access MyPlayer instance variables as "self" is passed as a 
    -- parameter - as it's NOT a table function there is no . or : malarky to worry about
    -- but YOU do have to pass "self" (ideally) as the first parameter
     
    local function privateFunctionB(self,a)
         print(self.health,a)
    end
     
    -- This is the "constructor" function, any parameters passed to the "new" function will
    -- need to be declared, as Sprite.new() doesn't take a parameter it easy to extend 
    -- the class and still retain the Sprite functionality, it gets a little more complex if you
    -- try and extend say a Bitmap or other class that takes parameters as I'm not 100% 
    -- sure what happens to the parent (super) class or if the parent (super) class 
    -- constructor is automatically called.  So to make like easy for yourself, just 
    -- extend Sprite if need be.
     
    function MyPlayer:init(startingHealth)
        -- declares and initialises the current instance variables
        self.health = startingHealth 
    end
     
    -- This is a normal function, the "self" is implied as a "hidden" first parameter as it's 
    -- declared using : 
     
    function MyPlayer:update(xyz)
        local val = privateFunction(self.health, privateSharedVarA)
        self.health = (self.health + xyz) - val
     
        privateFunctionB(self,privateSharedVarB)
     
    end
    Ideally each "class" should be in a separate file, which if it's added to the Gideros project won't need requiring

    So to use this in main you could write.
        -- starting health - note the . here as new() actually returns a table that is
       --  "filled in"  automatically by the :init() function.
       local p1 = MyPlayer.new(100)    
     
        -- these two function do EXACTLY the same thing!
        p1:update(4)         -- note the : here
        p1.update(p1,4)    -- note the . here
    Hope this helps! :)

    Likes: gyrosp, plamen

    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
    +1 -1 (+2 / -0 )Share on Facebook
  • techdojotechdojo Guru
    Accepted Answer
    Also remember - because of Lua's "untyped / don't declare variables before you use them" open nature, if you ever misspell a variable name it'll just assume your talking about a new one and promptly (silently) create it for you.

    This is usually the number 1 source of "bugs" in Lua apps! - remember what @plamen said.
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
  • gyrospgyrosp Member
    edited November 2012
    Ok, I think I have to get used to not declaring my vars ;). I am a friend of explicit declaration to avoid such bugs as techdojo mentioned.

    I stumbled about another thing:

    I have read a post about class inheritance with gideros (http://www.giderosmobile.com/documentation/classes_in_gideros.html) but have some issues I didn't get.

    I have following inheritance:
    --[[
     filename : ZombieBase.lua
    --]]
     
    ZombieBase = Core.class()
     
    function ZombieBase:init()
        self.health = 100
        self.speed = 3
    end
     
    function ZombieBase:startWalking()
    end
     
    function ZombieBase:stopWalking()
    end
    --[[
     filename : SlowZombie.lua
    --]]
     
    SlowZombie = Core.class(ZombieBase)
     
    function SlowZombie:init()
    end
    local slowZombie = SlowZombie.new()
    slowZombie:startWalking()
    Executing this throws following error
    main.lua:14: attempt to call method 'startWalking' (a nil value)
    stack traceback:
    	main.lua:14: in main chunk
    Why does the class Zombie does not inherit the function startWalking() from ZombieBase?

    Edit:
    What is the tag I need to use to format my code like techdojo did ;)?
  • @gyrosp
    If you want to display your code, you can use < pre lang="lua" > Your code </ pre>

    (remember to clear blank on tags)
    local slowZombie = SlowZombie.new()
    slowZombie:startWalking()
    Coming soon
  • @vitalitymobile: Thanks ;)...

    Any idea why the inheritance is not working?
    +1 -1 (+1 / -0 )Share on Facebook
  • @gyrosp,
    when you call the
     local slowZombie = SlowZombie.new()
    you are not returning anything in your
    function ZombieBase:init()
    just add the line
    return self
    and it shall work for you ;)

    cheers,
    twitter: @ozapps | http://www.oz-apps.com | http://howto.oz-apps.com | http://reviewme.oz-apps.com
    Author of Learn Lua for iOS Game Development from Apress ( http://www.apress.com/9781430246626 )
    Cool Vizify Profile at https://www.vizify.com/oz-apps
  • MellsMells Guru
    edited November 2012
    Edit : my mistake :)
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • gyrospgyrosp Member
    edited November 2012
    @OZApps + @Mells: Neither is working
  • @gyrosp
    Can you create a test file? No doubt that your problem will be solved in no time.
    Looking at all the answers you got above, isn't this community amazing? :)
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • @Mells: Yes, it is :D.

    I'm sorry but I installed Gideros just today. What do you mean with test file? My project source files?
  • @gyrosp
    you create a very simplified version of your two classes + main.lua so we can see better where the problem is.
    You just have to duplicate your project and remove unnecessary files.
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • Ok, these are my classes and the main.lua file. It's basically the same code I've posted above.
    zip
    zip
    Zombie Runner.zip
    1K
  • edited November 2012
    https://docs.google.com/document/pub?id=149g_P1YlE4_6v_hHbFx3YAaYpdU0HoM70XJSLTE38ww#h.vwgiperksgpn

    Right click to SlowZombie and add Code Dependencies (Check on ZombieBase)

    And ... 'Gideros-Magic' will happen :P
    zip
    zip
    OOPTest.zip
    2K
    Coming soon
  • "Voilà" :)
    twitter@TheWindApps Artful applications : The Wind Forest. #art #japan #apps
  • :O ... thanks a lot, this is one of those annoying beginner problems you'll face only once :D
  • techdojotechdojo Guru
    edited November 2012
    Glad you got it working :)
    WhiteTree Games - Home, home on the web, where the bits and bytes they do play!
    #MakeABetterGame! "Never give up, Never NEVER give up!" - Winston Churchill
Sign In or Register to comment.