Creating Classes

I am a recent starter to Corona and Lua, coming from a web development background.

I’ve been reading about making classes and having a bit of trouble understand how best to access methods and properties. I’ll use my cose as it is for this example. Basically, I would like to create a new ‘player’, display it on the screen and add it as a physics object.

In main.lua:
[lua]require(“physics”)
physics.start()
physics.setDrawMode(“hybrid”)
physics.setScale(60)
physics.setGravity(0,0)

Player = require(“player”)
local player = Player:init()[/lua]

In player.lua
[lua]local Player = {}
local Player_mt = {__index = Player}
— Create a new instance of a Player object.
function Player:init()
local self = {}
setmetatable( self, Camera_mt )
self._view = display.newGroup()

local playerCollisionFilter = {categoryBits = 2, maskBits = 5}
local playerBodyElement = {filter = playerCollisionFilter}

local imageSheet = graphics.newImageSheet(“spriteCharacter.png”, {
width = 80,
height = 56,
numFrames = 6,
sheetContentWidth = 240,
sheetContentHeight = 112
})

local character = display.newSprite(imageSheet, {
name=“walking”,
start=1,
count=4,
time=420,
loopCount = 0,
loopDirection = “forward”
})

self._view:setReferencePoint(display.CenterReferencePoint)
self.isBullet = true
self.objectType = “player”
self.isVisible = true

self.x = 100
self.y = 100

physics.addBody(self._view,“dynamic”,playerBodyElement)
self._view:insert(character)
character:play()

return self
end

return Player[/lua]

Currently, I can’t see anything on the screen, and I don’t think this is correct. I want my player.lua file to handle player methods and be able to alter properties of the player object (health, visibility, scale etc) but main.lua needs to be able to get properties such as x and y position.

Can someone see where I am going wrong with this? I would like to keep it as object oriented as I can and get into good coding habits from the start.

Thanks [import]uid: 140429 topic_id: 24526 reply_id: 324526[/import]

Do you have the terminal running? Are you getting an error printing?

If so can you let us know what it is, please? :slight_smile: [import]uid: 52491 topic_id: 24526 reply_id: 99312[/import]

I can see a few things in your code. On line 8 its using Camera_mt when I assume you mean Player_mt.

I’d also change the name of your local self variable. self is already defined by Player and good practice wouldn’t redefine it here. I’d usually go with lowercase player as my naming convention instead of self.
[import]uid: 118390 topic_id: 24526 reply_id: 99321[/import]

@Peach - no errors in the console at the moment - but I think the problem is more that I can’t control or manipulate my player object.

@gravityapple, I have been using this camera.lua file as a reference, hence accidentally leaving in the _mt reference: http://cl.ly/1j441C2B111Y1j0X2129

I have a touch even in my main.lua and I want to be able to control the position of the player. If I add self._view to the physics, I don’t see anything on screen. If I add my character (just a sprite) then I see it on screen, and physics hybrid mode displays the correct bounding box. The problem then is that I’ve only added an image, not not my actual player object.

So to ask more direct questions:

  1. How can I add properties (x, y, health, name, isVisible etc) to my player

  2. How can I manipulate the position of the object on screen. I have tried in main.lua to more player.x and it does nothing (no errors either)

  3. Does this look better code now:

[lua]local Player = {}
local Player_mt = {__index = Player}
— Create a new instance of a Player object.
function Player:init()
local character = {}
setmetatable(character, Player_mt)
character._view = display.newGroup()

local playerCollisionFilter = {categoryBits = 2, maskBits = 5}
local playerBodyElement = {filter = playerCollisionFilter}

local imageSheet = graphics.newImageSheet(“spriteCharacter.png”, {
width = 80,
height = 56,
numFrames = 6,
sheetContentWidth = 240,
sheetContentHeight = 112
})

local sprite = display.newSprite(imageSheet, {
name=“walking”,
start=1,
count=4,
time=420,
loopCount = 0,
loopDirection = “forward”
})

character._view:setReferencePoint(display.CenterReferencePoint)
character.isBullet = true
character.objectType = “player”
character.isVisible = true

character.x = 100
character.y = 100
character.width = 80
character.height = 56

physics.addBody(character._view,“dynamic”,playerBodyElement)
character._view:insert(sprite)
sprite:play()

return character

end

return Player[/lua] [import]uid: 140429 topic_id: 24526 reply_id: 99323[/import]

Ah I see. Yes, where you have character.x, character.y this isn’t doing anything with the display group. You’d be better off doing something like character._view.x.

However I’d scrap the meta table approach and do something like this:

local Player = {}  
   
--- Create a new instance of a Player object.  
function Player:init()  
 local character = display.newGroup()  
   
 local playerCollisionFilter = {categoryBits = 2, maskBits = 5}  
 local playerBodyElement = {filter = playerCollisionFilter}  
  
 local imageSheet = graphics.newImageSheet("spriteCharacter.png", {  
 width = 80,  
 height = 56,  
 numFrames = 6,  
 sheetContentWidth = 240,  
 sheetContentHeight = 112  
 })  
   
 local sprite = display.newSprite(imageSheet, {  
 name="walking",  
 start=1,  
 count=4,  
 time=420,  
 loopCount = 0,  
 loopDirection = "forward"   
 })  
   
 character:setReferencePoint(display.CenterReferencePoint)  
 character.isBullet = true  
 character.objectType = "player"  
 character.isVisible = true  
  
 character.x = 100  
 character.y = 100  
 character.width = 80  
 character.height = 56  
   
 physics.addBody(character,"dynamic",playerBodyElement)  
 character:insert(sprite)  
 sprite:play()  
  
 return character  
  
end  
   
return Player  

This will make your player object “inherit” from display object. And so player.x, player.y will work as expected. I’d try staying clear of setmetatable when working with display objects. [import]uid: 118390 topic_id: 24526 reply_id: 99324[/import]

Hey gravity apple, I need to ask why your dropping the metable method? Luckly im already doing as you mentioned, yet for a while I was under the impression metatables give quicker performance due to some sort of overlouding from reading the Lua sites docs.

In the end I didnt bother changing to meta tables as my game was running ok but would be great to know your reasoning?
Also you say use “object” instead of self. Are they not both same thing

local Obstacle = {}  
  
function Obstacle.new(param)  
  
  
 local obstacle = display.newImageRect(param.image, param.width, param.height)  
   
 physics.addBody(obstacle, param.physic, {isSensor=param.isSensor, density=param.density, friction=param.friction, bounce=param.bounce,filter=param.filter,radius = param.radius, shape = param.shape})  
  
  
  
 obstacle.id = param.id  
 obstacle:setReferencePoint( display.CenterReferencePoint )  
 local obstacleY  
 function obstacle:fire(percent,height)  
 self.x = percent   
 obstacleY = height  
 self.y = obstacleY  
 end  
  
  
  
 function obstacle:destroy()  
 self:removeEventListener("collision", obstacle)  
 obstacle:removeSelf()  
 obstacle = nil  
 end  
  
 return obstacle  
  
end  
  
return Obstacle  

Ive mixed and matched both ways in the functions and still works as expected. PLease enlighten me if im missing something. Cheers. Stu. [import]uid: 118379 topic_id: 24526 reply_id: 99328[/import]

@Beloudest

I believe metatables might offer some performance benefits - however I think it’s at the cost of increased memory. The main reason I don’t use them (in this example) is because they don’t work directly with corona display objects. There’s an interesting article about it here: http://tekarak.blogspot.co.uk/2010/08/displayobject-inheritance-in-corona.html

The camera example you gave stores the display object in the _view field. This means anytime you want to modify it, you need to do camera._view.x or camera._view:insert(). Conceptually, I’d prefer camera.x, camera.width and have camera act like an actual display object.

Also you say use “object” instead of self. Are they not both same thing

I’m not sure what you mean here. My comment about your previous code snippet used local self = {}. “self” the variable is automatically defined in Player:init() through the use of the “:” operator. Here’s an example:

--main.lua  
local PlayerClass = require("Player")  
local playerObject = PlayerClass:init()  
  
playerObject:printName()  
--Player.lua  
local Player = {}  
  
function Player:init()  
 --self here would be the PlayerClass object  
 local player = {name = "Bob"}  
  
 function player.printName()  
 --self doesn't exist here, because the "." operator was used  
 --print(self.name) would fail  
 end  
  
 function player:printName()  
 print(self.name) -- works!  
 end  
  
 -- the function above is just another way of doing this:  
 function player.printName(self)  
 print(self.name)  
 end  
  
 return player  
end  
  
return Player  

[import]uid: 118390 topic_id: 24526 reply_id: 99333[/import]

Thanks @gravityapple (I think you have mixed myself and Beloudest up?)

I’ve used your ‘player’ example - I still don’t get any errors, but I see the same problem as before. I don’t see my sprite on screen, and in main.lua the property player.x is NaN.

When I added just my sprite to the physics (or used the old self._view) I saw the sprite on screen and I can see the physics hybrid box around it. Currently, physics has drawn a box over my entire screen and the sprite is nowhere to be seen.

Perhaps I am missing something obvious, but do I need to add ‘character’ into my player object somehow to make player.x work? [import]uid: 140429 topic_id: 24526 reply_id: 99348[/import]

@Peach - any ideas here. I’m pretty stuck?

If it helps, I was playing around some more. If I use this following code:

[lua]–player:setReferencePoint(display.CenterReferencePoint)
–physics.addBody (player,“dynamic”,playerBodyElement)
player.isBullet = true
player.objectType = “player”
player.isVisible = true
player:insert(sprite,true)
sprite:play()

return player[/lua]

I see my sprite appear, and my onTouch event allows me to alter the x and y properties of the displayGroup (of which the sprite is a child object) based on event.x and event.y

If I enable the setReferencePoint, the sprite appears but then disappears as soon as I try and set the player.x = event.x - but the player.x and player.x values are set correctly

If I enable physics.addBody, then I don’t see the sprite at all and also player.x and player.y are both reported as ‘nan’

I think I am pretty close, but missing a few fundamental things - can anyone help me get this simple example working? [import]uid: 140429 topic_id: 24526 reply_id: 99390[/import]

In the past ive had issues where I drop a dynmaic object ontop of another and one the objects will move off of the other. Sounds similar to your sprite issue. When you make the sprite have physics does it interact with another dynamic object thus causing it to move off screen? Just a thought?

Sorry for hijacking your post earlier :slight_smile: good luck. [import]uid: 118379 topic_id: 24526 reply_id: 99472[/import]

Thanks @gravityapple (I think you have mixed myself and Beloudest up?)

Oops, sorry, I was replying from my phone and didn’t notice.

Back at home now, I’ll be able to respond much faster!

Leave the setReferencePoint and addBody commented out and add:

print(player.width, player.height)

What values do you get?

Also try setting the reference point to top left, does it continue to disappear?

You have a lot of things going on at the moment - sprites, physics, reference points etc. You want to keep cutting the features out so you can isolate the problem.

If possible also try removing the imageSheet and use a static image with physics.addBody and setReferencePoint re-enabled. [import]uid: 118390 topic_id: 24526 reply_id: 99490[/import]

Ah and also, looking at the api: http://developer.anscamobile.com/reference/index/objectsetreferencepoint

Someone has commented to make sure to set the reference point AFTER you’ve populated the display group.

So move it down to right before return player.

Also note this remark:

When a Display Object is converted into a physics object, the Physics engine assumes the reference point of the object is the center of the object. Calling object:setReferencePoint() may change the reference point from Corona’s Display Object point of view but not for the Physics engine.

You might not need to set the reference point anyway [import]uid: 118390 topic_id: 24526 reply_id: 99491[/import]

@Beloudest you didn’t hijack as we were all talking classes! The more input the better. I’ve gone ahead and dropped my class metatables as well which has helped simplify development. Thanks

@gravityapple thanks again, I’ll have a go with the static images and see what results I get. Does it look like I am writing things in an awkward way? You mentioned that it is doing quite a lot (sprites, physics) is there a more efficient way? [import]uid: 140429 topic_id: 24526 reply_id: 99733[/import]

Nope, I think what you’re doing is fine. It’s just difficult to see what’s causing the problem. My advice would be to take out the elements, one by one, to see what differences they make.

Also if you want to post/ send me the source code i’d be happy to take a look.

Goodluck, hope you get it all sorted either way. [import]uid: 118390 topic_id: 24526 reply_id: 99824[/import]

@gravityapple

Spot an with your answer - I had to move the reference point after I had added my sprite into the display group. The same was also true of the physics addBody method. Everything looks good now.

You were also correct in saying that the reference point is set to center by default, so I can take that line of code out now.

Thank you kindly for helping me through this issue. I hope it helps others out.

Alex [import]uid: 140429 topic_id: 24526 reply_id: 99846[/import]

glad to help

Aaron [import]uid: 118390 topic_id: 24526 reply_id: 99860[/import]