Dealing with shadows and layers / groups

Hello everybody,

I am trying to figure out what is the best technique to handle moving objects with shadows.

My problem is this:

I have several (potentially moving) objects. Each has its own image and a shadow image, in a display group.

The scene then adds these objects one after the other to its own view but since the last object added is on top of the previous objects (layering-wise), I end up with a situation where no matter what is the order I add things, I always end up with the shadow of one object being on top of the other object.

Here is an image to demonstrate:

On the right is the desired state. The other two are the two states I get - either ball shadow hides the object, or object shadow hides the ball.

layers.png

I know I can first add all shadows, then all objects, but then I will have to add another piece of code to have the shadow follow the object, instead of just attaching the shadow to the same group as the object itself.

I was looking for a way to tell Corona that I want to assign specific images to a  global layer - if this is possible, I believe it is the best option for my situation (so I will assign all shadows to layer 1, all objects to layer 2 etc).

Any tips on how to approach this would be appreciated.

Hey Danny, 

Display objects will appear back to front reading down your code. Using 

object:toFront()

or

object:toBack()

will allow you to bring objects to the front or back of a display group. That doesn’t seem to be what you want, but it’s worth noting. 

There is no other built in method to reorder display objects, but you could try this:

local group = display.newGroup() local Rect1 = display.newRect(group,100,100,100,100) Rect1:setFillColor(1,0,0) local Rect2 = display.newRect(group,150,150,100,100) Rect2:setFillColor(0,1,0) local Rect3 = display.newRect(group, 200,200,100,100) Rect3:setFillColor(0,0,1) local Rect4 = display.newRect(group, 250,250,100,100) Rect4:setFillColor(1,1,1) for i = 1, group.numChildren do if group[i] == Rect3 then group:insert(i, Rect4) -- this puts Rect4 behind Rect3 break end end

The loop iterates through the display group; when it finds Rect3, it inserts Rect4, placing it visually behind Rect 3. You might be able to use this to reorder your display objects to get them just right. 

Thanks James,

I bumped into both the toBack/toFront methods as well as the insert-with-index method in my searches for a solution.

Although I am disappointed to realize Corona may be too limited for what I need, you gave me an idea with the toBack methods.

Perhaps I can tag all my “shadow” images as such, and then have a method that sends all of them to back, then finally re-sends the background to back, or something like this… feels too hacky, but I dont know what else to do.

I may have missed something, but layering objects vs shadows can be solved with display groups.

Here is one possible solution:

local world = display.newGroup() world.shadows = display.newGroup() world.casters = display.newGroup() world:insert( world.shadows ) world:insert( world.casters ) -- Create objects local function newPair( x, y ) local caster = display.newImageRect( world.casters, ... ) caster.shadow = display.newImageRect( world.shadows, ... ) caster.enterFrame = function( self, event ) ... code to update position of caster and shadow self.x = ... self.y = ... self.shadow.x = self.x + ... self.shadow.y = self.y + ... enter Runtime:addEventListener( "enterFrame", caster ) end newPair( 100, 100 ) newPair( 200, 200 )

You may wonder why I didn’t just make two display groups?  I like to have a master group that owns all children, thus ‘world’.  Then I can update the position of all objects in the ‘world’ by moving the world group.  This is useful for camera work.

Thanks @roaminggamer,

Tried your suggestion and it works.

My objects themselves are controlled by physics, so I end up having to just update the position of the shadow to match the object.

I am not crazy about having to programmatically update a shadow’s position (rather than attach it to its caster) but I guess it will have to do.

Hey Danny, 

Display objects will appear back to front reading down your code. Using 

object:toFront()

or

object:toBack()

will allow you to bring objects to the front or back of a display group. That doesn’t seem to be what you want, but it’s worth noting. 

There is no other built in method to reorder display objects, but you could try this:

local group = display.newGroup() local Rect1 = display.newRect(group,100,100,100,100) Rect1:setFillColor(1,0,0) local Rect2 = display.newRect(group,150,150,100,100) Rect2:setFillColor(0,1,0) local Rect3 = display.newRect(group, 200,200,100,100) Rect3:setFillColor(0,0,1) local Rect4 = display.newRect(group, 250,250,100,100) Rect4:setFillColor(1,1,1) for i = 1, group.numChildren do if group[i] == Rect3 then group:insert(i, Rect4) -- this puts Rect4 behind Rect3 break end end

The loop iterates through the display group; when it finds Rect3, it inserts Rect4, placing it visually behind Rect 3. You might be able to use this to reorder your display objects to get them just right. 

Thanks James,

I bumped into both the toBack/toFront methods as well as the insert-with-index method in my searches for a solution.

Although I am disappointed to realize Corona may be too limited for what I need, you gave me an idea with the toBack methods.

Perhaps I can tag all my “shadow” images as such, and then have a method that sends all of them to back, then finally re-sends the background to back, or something like this… feels too hacky, but I dont know what else to do.

I may have missed something, but layering objects vs shadows can be solved with display groups.

Here is one possible solution:

local world = display.newGroup() world.shadows = display.newGroup() world.casters = display.newGroup() world:insert( world.shadows ) world:insert( world.casters ) -- Create objects local function newPair( x, y ) local caster = display.newImageRect( world.casters, ... ) caster.shadow = display.newImageRect( world.shadows, ... ) caster.enterFrame = function( self, event ) ... code to update position of caster and shadow self.x = ... self.y = ... self.shadow.x = self.x + ... self.shadow.y = self.y + ... enter Runtime:addEventListener( "enterFrame", caster ) end newPair( 100, 100 ) newPair( 200, 200 )

You may wonder why I didn’t just make two display groups?  I like to have a master group that owns all children, thus ‘world’.  Then I can update the position of all objects in the ‘world’ by moving the world group.  This is useful for camera work.

Thanks @roaminggamer,

Tried your suggestion and it works.

My objects themselves are controlled by physics, so I end up having to just update the position of the shadow to match the object.

I am not crazy about having to programmatically update a shadow’s position (rather than attach it to its caster) but I guess it will have to do.