Quick question about the performance of containers

@Quitalizner,

  1. When you say container, do you mean the object produced by this function display.newContainer()  or do you mean display groups and containers?

  2. What do you mean when you say ‘interlinked’?  Do you mean you’ve added a physics body to the group/container?

Can you show some code demonstrating what you mean?

(Please remember to format code posts for legibility with the <> button above).

PS - I wouldn’t worry too much about optimization early on.  If you’re just learning this stuff it is better to take a more ad hoc approach initially.  Then, as you proceed you will find which solutions are superior and which are worse.

Yes, it’s the display.newContainer.

To explain the scenario of what i meant by interlinked.

If you have played the game mmm fingers. You’d have a more clear picture of what i’m about to say.

For example:- If have a simple a rotating obstacle. Its defined in a container and i set a unique name to that container.

Now if I want a more advanced form of that rotating obstacle. I create another container and add few more obstacles to it and check if it has a feasible path for player to pass through, again I set a unique name to that container. 

Now that I have two different containers which are preset. I call them randomly and stack them on top of each other.

I know interlinked is not the right word, just that i want to make sure there is a feasible path for the player to pass through, which would be difficult to create if i just randomize all the obstacles.

So below is a module for creating moving obstacles.

I have just presented a simple obstacle here. Its a grip which has a enterFrame event added to make it move in a certain manner.

Some might have collision listeners as well( the non-physical kind https://docs.coronalabs.com/tutorial/games/nonPhysicalCollision/index.html

Since I am creating many containers. Can it run smoothly, I see that its running fine on the simulator. But can a mobile handle it.

I call atmost  60 containers.

local function createContainer(currentHeight) local container = display.newContainer(measure.cliffWidth, 0) container.x = measure.cliffStart container.y = currentHeight container.anchorX = 0 container.anchorY = 0 container.anchorChildren = false return container end function \_M.movingSingleGrip(currentHeight) local container = createContainer(currentHeight) local grip = display.newCircle(container,measure.cliff4,60,30) grip.type = "grip" grip.speed = 1 grip.isMoving = true local height = 120 -- takes one container space container.height = container.height + height container.numElements = 1 container.row = 1 container.name = "msg" function container:eachFrame() local circle = container[1] if circle.x \< measure.cliff2 then circle.speed = 1 elseif circle.x \> measure.cliff6 then circle.speed = -1 end circle.x = circle.x + circle.speed end eachFrame.add(container) return container end

unless you actually need the masking (and it’s not apparent that you do) then just use plain old groups – any question about container performance then becomes moot.

I don’t need the masking, but it seems easy to position elements in a container and make presets of them. And since we cant( not aware if its possible) explicitly set the height and width of the group. I thought I’d go with containers, it seems to be working fine. But I just wanna be sure whether it may cause any problems down the road.

Yes, it can cause problems.  Excessive use of containers can cause performance issues. 

As Dave pointed out, each container involves a masking operation.  Aside from being expensive to do a bunch of unnecessary masking operations, if you start the put containers in containers you’ll soon hit the masking depth limit.

You can easily give a group a known dimension:

  1. Create a 16x16 transparent PNG and add it to your project folder of in a subfolder. (I call mine fillT.png)

  2. For a 400 x 400 ‘group’ do this:

    local group = display.newGroup() local fill = display.newImageRect( group, “fillT.png”, 400, 400 ) 

Now, as long as you do not place objects in the group where those objects’ bounds fall outside the bounds of ‘fill’ you are good to go.

Oh I see, thanks for the great advice roaminggamer. One last question, can I position the elements in the group relative to it similar to the way we position elements in the container. 

For example:-

local contain = display.newContainer(200, 200) local object = display.newRect(contain, 0,0, 40,40) -- This will be positioned at the center of the container. -- Whereas local group = display.newGroup() local fill = display.newImageRect(group, "fillT.png", 200,200) local object = display.newRect(group, 0,0, 40,40) -- This will be positioned at the origin of the screen, instead of the group. Should I use something like contentBounds to make position it inside the group.

I’m not sure what the confusion is, but these are essentially the same (minus masking)

local contain = display.newContainer(200, 200) local object = display.newRect(contain, 0,0, 40,40) contain.x = display.contentCenterX contain.y = display.contentCenterY -- Object is at \<0,0\> in the container. -- The center \<0,0\> of the container is in the center of the screen -- Thus, the center of the rect is also in the center of the screen

local group = display.newGroup() local object = display.newRect(group, 0,0, 40,40) group.x = display.contentCenterX group.y = display.contentCenterY -- Object is at \<0,0\> in the group. -- The center \<0,0\> of the group is in the center of the screen -- Thus, the center of the rect is also in the center of the screen

Tip: I said something above that isn’t entirely true.  Groups do not have a center.  That said, <0,0> can be treated like the center, then a group behaves just like a container (excluding masking).

Oh, alright then. I’ll give it a try and see if i have any doubts. Thanks a lot roaming gamer for your advice on transparent filling.

Containers are a relatively new addition to Corona, compared to the age of the SDK. Therefore I never bothered switching to them, and used a method similar to RG.

If a display group will have known bounds, say a sidebar or a popup window, I create a newRect of that size and add it to the group, and store it as a parameter ‘box’ on the group. I then know to place an object at the top left of the group, I give it a topLeft anchor point and place it at -group.box.width/2, -group.box.height/2.

This also allows me to size objects dynamically depending on the size of the group. If I want a panel in the group that fills the left half, I make the size group.box.width/2, group.box.height. If I then decide the group needs to be bigger, I don’t need to change the size and position of all the components within it.

As RG says in order for this to work you have to ensure no objects go outside of the bounds or the positioning of objects after that may be off.

I tried it out, now I understand how I can go about replacing containers with groups.

I only used containers because i read the containers width and height have to be defined and it isn’t dynamic. So it felt easier to handle and also we can set anchors to the container which is what i needed. 

Now I see that we can do the same with groups, since we can’t set anchor property to the group, we can set it to box object that we use for sizing. 

But one thing I’m not sure yet is whether using any transition or enterFrame events and moving an object outside the width of the box, might increase the bounds of the box.

Thanks for the advice nick. 

Beginners like me really appreciate certain tips and tricks that experienced guys like you all use.

Well, I must add I’ve only ever used this for static UI elements, or when objects within the group don’t move even if the group itself does.

If objects must move outside of the ‘box’ then they don’t really belong in the box. For something like a drag-drop mechanism, when something is picked up and being dragged I will remove it from the ‘container-style’ group and place it in the scene group. Then when it’s dropped, re-insert and re-position into the appropriate group based on where it was dropped.

I have another doubt, @roaminggamer. Will using groups with transparent fills cause performance issues. Since transparency might require some additional rendering( correct me if i’m wrong ). I would be using a lot of groups with that transparent fill object method.

How does corona engine go about rendering transparent images.

I haven’t come across any problems and I have a LOT of display objects, transparent and opaque on screen at once. I wouldn’t worry about a potential problem unless you actually come across it. Drawing a lot of objects might take some time and then I would stagger them to prevent locking up the main thread, but once drawn you should be ok.

If you’re comparing it to something like Unity where you’re constantly worrying about draw calls and batching textures, with different shaders for transparent and opaque objects, this is an area where I think Corona has the upper hand. It’s very difficult to reach a performance limit with just on-screen objects. If you have a scrolling game with lots of stuff potentially off-screen, then it would be time to write a culling routine that makes sure these objects aren’t actually drawn until they can be ‘seen’.

Tip: You can avoid the fill object all together and set up a hybrid solution based on Nick’s suggestion and a bit of other goodness like this:

local function containerBuilder( x, y, w, h ) local group = display.newGroup() -- group.x = x group.y = y -- group.box = { width = w, height = h, left = function() return group.x - w/2 end, right = function() return group.x + w/2 end, top = function() return group.y - h/2 end, bottom = function() return group.y + h/2 end, } -- return group end -- Later, let's make a 200 x 300 group like this. -- local group = containerBuilder( display.contentCenterX, display.contentCenterY, 200, 300 ) print( group.x, group.y, group.box.width, group.box.height ) print( group.box.left(), group.box.right(), group.box.top(), group.box.bottom() ) group.x = group.x - 100 group.y = group.y + 100 print( group.x, group.y, group.box.width, group.box.height ) print( group.box.left(), group.box.right(), group.box.top(), group.box.bottom() )

Reminder: All of this falls apart if you put objects in the group and those objects’ bounds are outside the ‘assumed’ bounds of the group.

You could even do this:

function display.myNewGroup( x, y, w, h ) local group = display.newGroup() -- group.x = x group.y = y group.w = w group.h = h function group.left() return group.x - w/2 end function group.right() return group.x + w/2 end function group.top() return group.y - h/2 end function group.bottom() return group.y + h/2 end -- return group end -- Later, let's make a 200 x 300 group like this. -- local group = display.myNewGroup( display.contentCenterX, display.contentCenterY, 200, 300 ) print( group.x, group.y, group.w, group.h) print( group.left(), group.right(), group.top(), group.bottom() ) group.x = group.x - 100 group.y = group.y + 100 print( group.x, group.y, group.w, group.h) print( group.left(), group.right(), group.top(), group.bottom() ) &nbsp;

all of that is correct - a visible rect with a texture that just happens to be transparent will still be fully rendered (and will require an alpha blend per pixel) chewing up the gpu’s overall fill rate.  just use a simple display.newRect() and set .isVisible=false, then won’t be rendered at all, but will still affect the bounds of the group it’s within… if you need it to…

or,… reconsider why you even care what the group’s bounds are.  are you hit-testing against the group bounds itself rather than its contents?  (if so, what happens when the group is sparsely populated?)  because if you hit-test against the contents then who cares what size the group’s bounds are?

@nicksherman , I do have a lot of objects off-screen and they move in a top-down scroller way towards the player. All the objects in their respective container are randomly generated before the player has access to it. I really need to write a culling function, but I’m not aware how I need to approach it. Can you please provide a gist of it. 

@roaminggamer, Thanks for the advice on creating groups without fillObject. I’ll check if I would run into any problems with it. I have to see how it will handle dynamic change of height of group as I add more obstacles to it.

@davebollinger, Thanks for clarifying me on transparency. I googled and could only find articles in unity. I thought since this is a 2D engine the transparent images don’t require rendering. Since you asked if I am hit-testing against the group bounds. I do hit-testing for certain containers, so that they activate when player is within the bounds of that container. I am not hit-testing all the groups for the player. Only for containers with a certain obstacle.

I’m looking forward for any advice on culling as nick suggested. Will,  If all the objects are drawn and set to isVisible = false while offScreen and switching them to isVisible = true when closer to screen bounds work?

you’re talking “low-level culling” - taking an already-defined “thing” and decide whether to actually render it.  in corona terms, it would be like having an existing rectangle, then based on checking its bounds vs screen set .isVisible accordingly per frame.  corona already does this internally, with native code, and you’re unlikely to beat it with interpreted lua.

I think Nick was talking “high-level culling”:  simply defer creating all those offscreen objects until you actually need them ((fe within some threshold distance of the visible screen) then remove them once beyond the screen in the other direction.  anything that’s been “high-level culled” wouldn’t even exist in the display, so doesn’t even factor into “low-level culling”.

that’s how you (potentially) beat lower-level native code with higher-level interpreted code (otherwise don’t bother, you could even make it worse rather than better!)

Ah, I see now. Maybe I’ll populate all the coordinates and properties of respective objects into a table and make a call to draw them. It would get complex very fast. But I’ll make an attempt and see how it goes. Thank you davebollinger