In my game project i have an extensive need for object oriented capabilities and all the existing libraries that i found for Lua were either too complicated to fiddle with them, or lacked some features that i needed, so in the end i crafted my own.
I shared it on github as an open-source project called Luoop, so just thought i would mention it in here just in case it can be of use to anyone
Iām still expanding the possibilities slowly and could definitely value some external feedback
THANKS for the share! it is nice to see some OOP news. I wish Corona had implemented something like this. In any event, how would you use this library to deal with say visual objects (like images) and more importantly event listeners? Say I have a HUD in the game which shows #lives, points and so on. What about event like object collisions? I am using Storyboard and not sure really how to use this library for those basic game elements (display objects or event listeners)?Ā
The example you show are good but I think people will probably need a more beefy example to apply. Of course just a suggestion:)
In any event, thank you again for sharing. I think we need a simple a Corona OOP infrastructure like Gideros and Moai SDKās
Thanks for your interest, itās great indeed to hear some feedback
A few things have been added to the library since i originally posted it, itās highly iterative work since iām basically adding to it when i encounter a case for which i need an additional development in my game project.
Your question is very very accurate actually, and in my personal case i have also recoded a whole director / event / listener implementation, as well as encapsulated the ānativeā corona display objects into my own ones.
Main reasoning behind all this is that i wanted my events to work not only with physics / display objects, but basically anything and thatās pretty much a requirement in the game iām working on.
Same thing with display objects, i found out that it was probably cleaner to encapsulate native display objects so that i am
a ) secured against api changes (such as in the graphics 2.0 update) because i only need a few changes in my āfacadeā display layer
b ) able to craft easily reusable components. Basically i just wanted to be able to call almost new Clock() for example and create a clock object easily.
I think however that once iām done with the current project iāll share all the event side of the implementation since it can definitely be useful to other projects.
In your specific case, a display object that can handle native corona events try this:
Ā
----------------------------------------------------------------------------------------- -- -- Control.lua -- ----------------------------------------------------------------------------------------- require (gsPathLuoop.."luoop") -- sClass system --- Constructor --- local function new(self, nX, nY, nWidth, nHeight, oReferencePoint) -- public members self.\_nX = nX self.\_nY = nY self.\_nWidth = nWidth self.\_nHeight = nHeight self.\_oReferencePoint = oReferencePoint -- private members self.\_sFamily = sFamily self.\_sId = sId self.\_oDisplayObject = nil end -- A class Control = class(new) --- Destructor --- function Control:destroy() self.\_nX = nil self.\_nY = nil self.\_nWidth = nil self.\_nHeight = nil self.\_oReferencePoint = nil self.\_oDisplayObject = nil end function Control:type() return 'Control' end -- Getters / setters function Control:\_getX() return self.\_nX end function Control:getX() return self:\_getX() end function Control:setX(nX) self.\_nX = nX -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.x = nX end end function Control:\_getY() return self.\_nY end function Control:getY() return self:\_getY() end function Control:setY(nY) self.\_nY = nY -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.y = nY end end function Control:\_getWidth() return self.\_nWidth end function Control:getWidth() return self:\_getWidth() end function Control:setWidth(nWidth) self.\_nWidth = nWidth -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.width = nWidth end end function Control:\_getHeight() return self.\_nHeight end function Control:getHeight() return self:\_getHeight() end function Control:setHeight(nHeight) self.\_nHeight = nHeight -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.height = nHeight end end function Control:getContentWidth() local nContentWidth = 0 local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then nContentWidth = oDisplayObject.contentWidth end return nContentWidth end function Control:getContentHeight() local nContentHeight = 0 local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then nContentHeight = oDisplayObject.contentHeight end return nContentHeight end function Control:\_getReferencePoint() return self.\_oReferencePoint end function Control:getReferencePoint() return self:\_getReferencePoint() end function Control:\_setReferencePoint(oReferencePoint) self.\_oReferencePoint = oReferencePoint end function Control:setReferencePoint(oReferencePoint) self:\_setReferencePoint(oReferencePoint) -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject:setReferencePoint(oReferencePoint) end end function Control:\_getDisplayObject() return self.\_oDisplayObject end function Control:\_setDisplayObject(oDisplayObject) self.\_oDisplayObject = oDisplayObject end --- Methods --- function Control:refresh() -- Some controls require a specific routine to call when content is updated end function Control:render() -- Called when actually rendering by creating the display object and showing on screen end function Control:addOnTouchEvent(fctOnTouch) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:addEventListener( "touch", fctOnTouch) end function Control:removeOnTouchEvent(fctOnTouch) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:removeEventListener( "touch", fctOnTouch) end function Control:addOnTapEvent(fctOnTap) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:addEventListener( "tap", fctOnTap) end function Control:removeOnTapEvent(fctOnTap) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:removeEventListener( "tap", fctOnTap) end
Thatās like a āgenericā control object from which you can create your inherited specific controls.
Once itās created you just have to call yourControl:addOnTapEvent(yourHandler) to add an event listener on the object.
You could also call yourControl:addOnTapEvent(yourControl.fctInternalHandler) to keep the handler internal to the object
The _methods are considered private methods, other public.
Feel free to contact me by pm / e-mail if you need more detail
Iāve been relearning mobile programming in my (little) free time and had been debating if I wanted to use Corona because of its lack of a simple Object Oriented implementation. This looks like it does exactly what I was looking for! Iām excited to start using this
Thanks Teddy for the additional example and the extra info. I appreciate it. I have to admit that because I am new to OOP, it is going to take me sometime to absorb the info but I am very grateful that you took the time the answer and that you are willing to help. I will have a lot of questions!
@Tyler,
Haha i had the same āissueā, itās really hard to go back to another paradigm once you are used to it
@Mo,
I just updated the control example and also added a ālabelā class displaying a simple text so that you have a concrete example
Note that itās a basic example you will have to build upon it But that should give you an idea of how inheritance works with luoop, and how you can call a parent method (the label:render() automatically calls the control:render() method using _parentMethod()).
THANKS for the share! it is nice to see some OOP news. I wish Corona had implemented something like this. In any event, how would you use this library to deal with say visual objects (like images) and more importantly event listeners? Say I have a HUD in the game which shows #lives, points and so on. What about event like object collisions? I am using Storyboard and not sure really how to use this library for those basic game elements (display objects or event listeners)?Ā
The example you show are good but I think people will probably need a more beefy example to apply. Of course just a suggestion:)
In any event, thank you again for sharing. I think we need a simple a Corona OOP infrastructure like Gideros and Moai SDKās
Thanks for your interest, itās great indeed to hear some feedback
A few things have been added to the library since i originally posted it, itās highly iterative work since iām basically adding to it when i encounter a case for which i need an additional development in my game project.
Your question is very very accurate actually, and in my personal case i have also recoded a whole director / event / listener implementation, as well as encapsulated the ānativeā corona display objects into my own ones.
Main reasoning behind all this is that i wanted my events to work not only with physics / display objects, but basically anything and thatās pretty much a requirement in the game iām working on.
Same thing with display objects, i found out that it was probably cleaner to encapsulate native display objects so that i am
a ) secured against api changes (such as in the graphics 2.0 update) because i only need a few changes in my āfacadeā display layer
b ) able to craft easily reusable components. Basically i just wanted to be able to call almost new Clock() for example and create a clock object easily.
I think however that once iām done with the current project iāll share all the event side of the implementation since it can definitely be useful to other projects.
In your specific case, a display object that can handle native corona events try this:
Ā
----------------------------------------------------------------------------------------- -- -- Control.lua -- ----------------------------------------------------------------------------------------- require (gsPathLuoop.."luoop") -- sClass system --- Constructor --- local function new(self, nX, nY, nWidth, nHeight, oReferencePoint) -- public members self.\_nX = nX self.\_nY = nY self.\_nWidth = nWidth self.\_nHeight = nHeight self.\_oReferencePoint = oReferencePoint -- private members self.\_sFamily = sFamily self.\_sId = sId self.\_oDisplayObject = nil end -- A class Control = class(new) --- Destructor --- function Control:destroy() self.\_nX = nil self.\_nY = nil self.\_nWidth = nil self.\_nHeight = nil self.\_oReferencePoint = nil self.\_oDisplayObject = nil end function Control:type() return 'Control' end -- Getters / setters function Control:\_getX() return self.\_nX end function Control:getX() return self:\_getX() end function Control:setX(nX) self.\_nX = nX -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.x = nX end end function Control:\_getY() return self.\_nY end function Control:getY() return self:\_getY() end function Control:setY(nY) self.\_nY = nY -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.y = nY end end function Control:\_getWidth() return self.\_nWidth end function Control:getWidth() return self:\_getWidth() end function Control:setWidth(nWidth) self.\_nWidth = nWidth -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.width = nWidth end end function Control:\_getHeight() return self.\_nHeight end function Control:getHeight() return self:\_getHeight() end function Control:setHeight(nHeight) self.\_nHeight = nHeight -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject.height = nHeight end end function Control:getContentWidth() local nContentWidth = 0 local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then nContentWidth = oDisplayObject.contentWidth end return nContentWidth end function Control:getContentHeight() local nContentHeight = 0 local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then nContentHeight = oDisplayObject.contentHeight end return nContentHeight end function Control:\_getReferencePoint() return self.\_oReferencePoint end function Control:getReferencePoint() return self:\_getReferencePoint() end function Control:\_setReferencePoint(oReferencePoint) self.\_oReferencePoint = oReferencePoint end function Control:setReferencePoint(oReferencePoint) self:\_setReferencePoint(oReferencePoint) -- Updating display object if needed local oDisplayObject = self:\_getDisplayObject() if oDisplayObject ~= nil then oDisplayObject:setReferencePoint(oReferencePoint) end end function Control:\_getDisplayObject() return self.\_oDisplayObject end function Control:\_setDisplayObject(oDisplayObject) self.\_oDisplayObject = oDisplayObject end --- Methods --- function Control:refresh() -- Some controls require a specific routine to call when content is updated end function Control:render() -- Called when actually rendering by creating the display object and showing on screen end function Control:addOnTouchEvent(fctOnTouch) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:addEventListener( "touch", fctOnTouch) end function Control:removeOnTouchEvent(fctOnTouch) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:removeEventListener( "touch", fctOnTouch) end function Control:addOnTapEvent(fctOnTap) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:addEventListener( "tap", fctOnTap) end function Control:removeOnTapEvent(fctOnTap) local oDisplayObject = self:\_getDisplayObject() return oDisplayObject:removeEventListener( "tap", fctOnTap) end
Thatās like a āgenericā control object from which you can create your inherited specific controls.
Once itās created you just have to call yourControl:addOnTapEvent(yourHandler) to add an event listener on the object.
You could also call yourControl:addOnTapEvent(yourControl.fctInternalHandler) to keep the handler internal to the object
The _methods are considered private methods, other public.
Feel free to contact me by pm / e-mail if you need more detail
Iāve been relearning mobile programming in my (little) free time and had been debating if I wanted to use Corona because of its lack of a simple Object Oriented implementation. This looks like it does exactly what I was looking for! Iām excited to start using this
Thanks Teddy for the additional example and the extra info. I appreciate it. I have to admit that because I am new to OOP, it is going to take me sometime to absorb the info but I am very grateful that you took the time the answer and that you are willing to help. I will have a lot of questions!
@Tyler,
Haha i had the same āissueā, itās really hard to go back to another paradigm once you are used to it
@Mo,
I just updated the control example and also added a ālabelā class displaying a simple text so that you have a concrete example
Note that itās a basic example you will have to build upon it But that should give you an idea of how inheritance works with luoop, and how you can call a parent method (the label:render() automatically calls the control:render() method using _parentMethod()).
Thatās the main limitation of objects you create that way.
The corona display group system will definitely ānotā call your object destroy function to make sure memory is freed, and you cannot āinsertā them as such since they arenāt corona display objects.
In this case, itās better to consider the luoop view object as a wrapper around a corona display group.
You could hijack the removeSelf function to make it call the destructor automatically but you would still have the issue when there is a hierarchy of corona display groups / objects - parent groups seem to call an internal display cleanup function different to removeSelf()
Conclusion is: Either use a whole luoop stack and wrap your groups inside them, or use luoop for the model / controller objects only if it is an issue
Iāve my own entirely different system which has destructors (non automatic obviously) and I think the latter is the solution. Objects are used for game scenes, some abstract display objects, resources etc. but the actual Corona flat implementation of objects displays as is.Ā
Objects that need their displays manipulated (e.g. game scenes so the game manager can do state changes fade in/out etc.) have a getDisplayObject() method.
Banjaxing around with Corona SDK is generally not a good idea - I donāt even recommend basic decoration because Corona could add methods to the API.Ā
I think it introduces unnecessary complexity anyway.Ā
In my system if you have an object that (say) represents a score display the actual implementation is hidden in the object, built up by the constructor and torn down by the destructor. It allows some automation - the object can be added to the scene in such a way that when the scene is destroyed its child objects are too, but the actual Corona tear-down is still done the same old fashioned way.