Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Classes in Gideros — Gideros Forum

Classes in Gideros

keoghkeogh Member
edited March 2012 in Game & application design
Hi,

Having the following code to create a new class:

Player = Core.class(Sprite) -- create your own player class

function Player:init()
-- do the initialization of Player instance
self.health = 100
self.speed = 3
end

function Player:walk()
-- walk logic
end

function Player:jump()
-- jump logic
end

stage:addChild(Player.new())

My questions are, how can I access to the health and speed instance variables from walk and jump methods? also, how can I invoke walk method from jump method. I'm looking for a "this" variable that allows me to access the methods o properties of its own instance.

Also, I would like to know how to call the parent construct. I wanted to inherit Bitmap and I didn't know how to pass to it the texture.

Regards,

-----
Isaac Zepeda
Tagged:
«1

Comments

  • CarolineCaroline Guru
    edited March 2012
    Instead of adding a new Player to the stage, first place the new Player into a variable so that you can access it. This variable will be an instance of the Player class, and of course you can have multiple instances of the Player class.

    Have a look at this code, whic creates an instance called "newPlayer". You can also have "newPlayer2", "newPlayer3", etc, each of which will be separate instances and you can allocate health and speed to each of these instances.

    In the init function, you can do function Player:init(speed), so when you do newPlayer.new(50), you allocate 50 as the speed for that instance.
    Player = Core.class(Sprite) -- create your own player class
     
    function Player:init()
    -- do the initialization of Player instance
    self.health = 100
    self.speed = 3
    end
     
    function Player:walk()
    -- walk logic
    	print("Player Walking - health:", self.health)
    end
     
    function Player:jump()
    -- jump logic
    end
     
    newPlayer = Player.new()
    stage:addChild(newPlayer) 
     
    print (newPlayer.health)
     
    newPlayer:walk()
    newPlayer.health = 50
    newPlayer:walk()
    In that example, the walk function will print out a line on the Gideros studio console, and that print line will print out the health value for that instance.

    The "self." is the equivalent of "this."

  • The thing to remember with Lua is that whenever you access a table function with : as opposed to . Lua automatically add's a "hidden" first parameter called "self" which as Caroline said is the equivalent of the "this" pointer in C++

    A nice way to remember is to only access methods (class functions) via the : operator and fields (class variables) using the . operator.

    "self" is (if my understanding of Lua is correct) is basically a reference to the table which is created as part of the Class.new() command - I still haven't managed to work out exactly what happens with the tables when an object instance is created (something with metatables I suspect) but it seems to work (and the Gideros approach is the cleanest OOP implementation I've seen) so for now I'm just closing my eyes and trying not to worry about it. :)

    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

  • Also, I would like to know how to call the parent construct. I wanted to inherit Bitmap and I didn't know how to pass to it the texture.
    All methods and fields from the parent are availabel in the child class. Or what was your question?

  • As you can only inherit from the Gideros base classes I think the parent "constructors" are automatically called (if that answers your question).

    If you could inherit from your own classes this might be an issue but you can't. Actually having a limited hierarchy in a lot of ways is a benefit, you can actually create complex objects using a component "plug-in" style system, where functionality comes from smaller objects being (re)used rather than trying to have an all-encompassing object tree structure.

    A quick google on "component game design" should shed some light on things.
    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
  • As you can only inherit from the Gideros base classes I think the parent "constructors" are automatically called (if that answers your question).

    If you could inherit from your own classes this might be an issue but you can't. Actually having a limited hierarchy in a lot of ways is a benefit, you can actually create complex objects using a component "plug-in" style system, where functionality comes from smaller objects being (re)used rather than trying to have an all-encompassing object tree structure.

    A quick google on "component game design" should shed some light on things.
    Actually you can inherit from your own classes. It is an undocumented feature.
  • This is a sample that someone posted a few weeks ago:
    -------------------------------------
    RoomWrapper = Core.class(EventDispatcher)
     
    function RoomWrapper:init()
    	self.props = {}
    	print("init Roomwrapper")
    end
     
    function RoomWrapper:thisIsMyTest()
    	print ("in room wrapper!")
    end
    -------------------------------------
    Room = Core.class(RoomWrapper)
     
    function Room:init()
    	   print("init room")
           self:thisIsMyTest();
    end
    -------------------------------------
    local test = Room.new()

    Likes: karnakgames

    +1 -1 (+1 / -0 )Share on Facebook
  • ar2rsawseenar2rsawseen Maintainer
    edited March 2012
    Yes MikeHart right, but still parent "constructors" are automatically called even for your own classes
  • init is called through all the layers of inheritance but be aware that other functions don't work that way. Suppose you have two classes, Base and Character, in which Base is never instantiated directly with the following functions.

    Base:myCall()
    Character:myCall()
    Character:anotherCall()

    If Base calls myCall it will call Base:myCall()
    If Character calls myCall it will call Character:myCall()
    If Base calls anotherCall it will call Character:anotherCall()
  • But isn't that correct behaviour in terms of the polymorphism as defined in the OOP paradigm?

    To be honest I wasn't aware that you *could* inherit in this way - it might be useful to know for future use, thanks for sharing.

    At the end of the day the best way to prove or dis-prove anything is via experimentation and putting some well placed print() statements in the code will always tell you which functions are being called (and when)
    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

  • Also, I would like to know how to call the parent construct. I wanted to inherit Bitmap and I didn't know how to pass to it the texture.
    If you inherit from a class that takes a constructor argument, your new class has to pass in the arguments that the base class expects as its first arguments. You can then pass in any additional arguments:
    MyBitmap = Core.class(Bitmap)
    function MyBitmap:init(texture, additional args)
        ....
    end
    Here's a very simple example:
    A = Core.class()
    function A:init(msg1)
       print("A:init("..msg1..")")
    end
     
    B = Core.class(A)
    function B:init(msg1, msg2)
       print("B:init("..msg1..","..msg2..")")
    end
     
    B.new("hello","world")
    This example produces:
    A:init(hello)
    B:init(hello,world)

    Likes: techdojo

    +1 -1 (+1 / -0 )Share on Facebook
  • @ndoss - Clear concise example, easily understandable and straight to the point.
    Well done!
    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
  • jack0088jack0088 Member
    edited March 2012
    This is a sample that someone posted a few weeks ago:
    -------------------------------------
    RoomWrapper = Core.class(EventDispatcher)
     
    function RoomWrapper:init()
    	self.props = {}
    	print("init Roomwrapper")
    end
     
    function RoomWrapper:thisIsMyTest()
    	print ("in room wrapper!")
    end
    -------------------------------------
    Room = Core.class(RoomWrapper)
     
    function Room:init()
    	   print("init room")
           self:thisIsMyTest();
    end
    -------------------------------------
    local test = Room.new()
    After trying I've noticed: inherit from own classes ONLY works when they are in one and the same file! Separate files (one for each class) doesn't work - in this case you can leave the Core.class argument but specify the parent class when calling, as you would normally do. e.g.
    Room = Core.class() -- leave this!
    function Room:init()
           RoomWrapper:thisIsMyTest() -- specify parent!
    end
    Owltwins. Smart design and creative code.
    »Gideros Illustrator« - [svg|xml] scene designer using Adobe Illustrator®™ Within one line of code!
  • But isn't that correct behaviour in terms of the polymorphism as defined in the OOP paradigm?

    To be honest I wasn't aware that you *could* inherit in this way - it might be useful to know for future use, thanks for sharing.

    At the end of the day the best way to prove or dis-prove anything is via experimentation and putting some well placed print() statements in the code will always tell you which functions are being called (and when)
    It's overriding vs overloading. Overloading would replace the parents code but the child usually has the ability to call the parent if need be. Overriding would call the function that is in the class that was instantiated much like these classes work. Overriding is not recommended for the following reason:
    Vehicle vehicle;
    vehicle = new Car()
    vehicle.start()
    If both vehicle and car have a start function and car overrides it it's not obvious which is called.
  • ndossndoss Guru
    edited March 2012

    After trying I've noticed: inherit from own classes ONLY works when they are in one and the same file! Separate files (one for each class) doesn't work - in this case you can leave the Core.class argument but specify the parent class when calling, as you would normally do.
    You can put class definitions in different files, but if it doesn't work, it's because the files are being loaded in the wrong order. You can specify file dependencies in the IDE. Select a file, right click, select "Code Dependencies".

    For example, if class A (in classA.lua) inherits from class B (in classB.lua) and you're getting a message like "attempt to index global 'B' (a nil value)", select "classA.lua" and add a dependency on classB.lua.

    --ND
  • ndossndoss Guru
    edited March 2012
    If you inherit from a class and override one of its functions, I believe you have to call the overridden function using the "BaseClassName.function(self)" syntax. I could be mistaken, but I believe this to be so.

    Here's an example:
    -- --------------------------------------
    A = Core.class()
    function A:init(msg1)
       self.text = "I'm an A"
    end
     
    function A:method()
       print(self.text .. " in A:method()")
    end
     
    -- --------------------------------------
    B = Core.class(A)
    function B:init(msg1, msg2)
       self.text = "I'm a B"
    end
     
    function B:method(arg1)
       print(self.text .. " in B:method()")
       --A:method()       <--- NOT THIS
       A.method(self)   
    end
     
    -- --------------------------------------
    b = B.new("hello","world")
    b:method()
    This will produce:
    I'm a B in B:method()
    I'm a B in A:method()
    If you try to use "A:method()" in "B:method()" you'll get an error about a nil value for the 'text' field. That's because you're using the "A" instance that was created in the "A = Core.class()" line (equivalent to "A.method(A)").

    If you try to use "self:method()", you'll be recursively calling "B:method()"
  • @andybarilla: oh, thanks, nice to know it fixes this issue!
    Owltwins. Smart design and creative code.
    »Gideros Illustrator« - [svg|xml] scene designer using Adobe Illustrator®™ Within one line of code!
  • I just figured how how to override the default new constructor so I can have all Textures failback to the Documents directory when looking for images.
    -- I got this exists function off the web somewhere
    function exists(fname)
    	local f = io.open(fname, "r")
    	if (f and f:read()) then return true end
    end
     
    Texture.oldNew = Texture.new
    function Texture.new(filename, filtering, options)
    	if exists(filename) then
    		return Texture.oldNew(filename, filtering, options)
    	elseif exists("|D|"..filename) then
    		return Texture.oldNew("|D|"..filename, filtering, options)
    	else
    		return nil
    	end
    end

    Likes: MikeHart, atilim

    +1 -1 (+2 / -0 )Share on Facebook
  • MikeHartMikeHart Guru
    edited March 2012
    Many people say LUA is bad but combined with the easiness of a BASIC dialect, LUA has so much power.
  • People say Lua is bad? That's crazy.

    I think the thing with Lua is that it can do a lot of powerful things that are a lot of work in other languages but are easy and can be hacked together in Lua because functions are 1st class values, everything is a table, and if you know how to do it to one thing you use the same steps for everything else. It even has single-threaded threading with coroutines and they are so easy to use compared to multithreaded threading. Everything is just so easy and I really appreciate simplicity.

    I started learning Lua because I thought it would be good enough for my needs. Now I'm thinking it is the best choice. Gideros is also a pretty good fit. And I have to admit that being able to develop on my iPad and run it on my iPhone with Dropbox as the intermediate step is a major feature for me and I'm not sure I would be able to do that with any other development product without jailbreaking my phone, which I do not want to do.

    I've also learned a lot or new programming concepts since I've been learning Lua. I sort of knew what "self" (or "this") was but after learning how Lua does OO I now feel like I have a much better understanding about how OO really works.
  • keoghkeogh Member
    Thanks to all, This have been really helpful! thanks!
  • Do we have a form os destructor?
    For example I have some DB operations, and want to close the DB before killing the class.
    REAL programmers type copy con filename.exe
    ---------------------------------------
  • bowerandybowerandy Guru
    edited March 2013
    @Cyberience, if you are using a recent version of Gideros (2012.9.9 or later) you can use the postInit() method to install a destructor.
    function EventDispatcher:postInit()
    	self._proxy = newproxy(true)
    	getmetatable(self._proxy).__gc = function() self:_destroy() end
    end
     
    function EventDispatcher:_destroy()
    end
    Then you just need to add a _destroy() method to any classes where you want to do something when it is garbage collected. Note that, with the code above, this will only work for objects that inherit from EventDispatcher.

    best regards

    Likes: Cyberience

    +1 -1 (+1 / -0 )Share on Facebook
  • can somebody tell me if i have a class A and then another class B inherited from A, but then i redefine the init function in B for example (or any other function), then how to call the original init function of A inside my new init function?
    i could do A.init(self) probably(?), but what if my function has some parameter(s) p?
    then would A.init(self,p) work, or how?
    thanks for the help
  • ar2rsawseenar2rsawseen Maintainer
    edited March 2013
    If I remember correctly original A init function is called inside B init automatically.
    And since init would return reference to self instance, you would not get anything from it.

    But all what you described is true A.init(self) and A.init(self,p) should work as you expect, only no point to use on init. Try wrapping needed functionality inside other A method and call it instead. ;)

    Basically don't mess with init :D
  • thanks for the interesting info, i may need to rewrite things then, so far i happily redefined init all the time. so i should use something like initMy(self,p).
  • ar2rsawseenar2rsawseen Maintainer
    hmm, my mistake, it seems that new returns reference to instance, and you probably can treat init as any other method :)
  • Adding more questions,
    local variable = value places a local var within the function, inside a class, it is still within the function,
    if I do a self:variable = value, it is available across the class, but also externally, is there another operator that would allow private variable across all functions of the class?
    REAL programmers type copy con filename.exe
    ---------------------------------------
  • another question: is there any way to define a local function in a class? i.e. that can be called from another functions of the class but not visible from outside?
  • ar2rsawseenar2rsawseen Maintainer
    edited March 2013
    @Cyberience @keszegh you can define both function and variable as a property of class, which would be available from all class methods, but there is no way to hide it from external use.
    As in there is no concept of private method and private variable in lua.
    But from a good practice stand of point, usually variable and method/functions that are meant to be used internally, are named with _ prefix:
    function Scene:init()
        self._variable = "some value"
    end
     
    function Scene:_method()
    end
Sign In or Register to comment.