Level data format

I hold all my game level info in a master table, that in turn contains a couple of sub-table. Then I save and load this master table as a JSON file.

As far as my table content, it’s usually a lot of entries that hold the name of number of the graphics file and the x and y coordinate.

OK, I have been looking at this endless skateborder game and it also uses randomly generated things.  However, while looking at the menu setup for it I noticed that it uses Tiled.  After watching Youtube for 2 hours I ran across this, along with some honest trailers that were very funny!   https://www.youtube.com/watch?v=ZT_-hPsHF6M 

This shows how you can use tiled  for pictures and objects instead of the old tiled functions.   So it looks like tiled will generate a JSON file which describes how things are to be placed.  Now it is just a matter of figuring out how to make use of that data in the code.  I see a ponytiled.lua module which may do what I want but it is hard to tell without some more doc.  I will have to keep searching around.  

I love how ponywoflf gets so much function out of so little lines of code and the fact that there examples are more than just code snippets.  However, it still can be tough to follow.  

L

Working my way through this by ponywolf now: 

https://coronalabs.com/blog/2016/12/08/using-tiled-for-layout-and-level-design/

Will probably answer all my questions.  We shall see.  

Well it covered using tiled but not what to do with the generated file afterward.  I guess more research is necessary. :( 

OK, last post I swear.  I have dug through ponytiled and it looks to me like this module is looping through the layers generated by the tiled code and loading everything.  It even looks like they load the same image over and over.  This makes some sense because the properties for the later objects might be different even tho the picture is the same.  hmmm, maybe corona knows and only load the picture once. 

This is not what I expected to see.  I expected to see some clever load algorithm around only loading tiles that the camera is currently viewing.  I also expected to see some object re-use based on repeating patterns.  Maybe I am over thinking it as always.  Maybe corona knows you are loading the same image over and over in your world and only loads it once so things all work out.   

If you load things on the fly does the code get jumpy when the loads happen offscreen?   If you spawn your whole world in the beginning you would still need to know when objects were under the camera to start animations right?   Otherwise, an object might just run away and never be seen.  hmmmm

Laura,

Once an image is loaded into a texture buffer, Corona re-uses that buffer for subsequent loads of the same texture.

As I recall, the PonyTiled loader is very slim and has no culling code.  You’ve got to do that yourself and it ain’t a small task because there are side-effects to not drawing scene elements that may have bodies attached to them, may be moving, etc.

If you want to see the complexity to which this kind of thing can go, download the MTE and look at mte.lua.

https://github.com/jsykes/million-tile-engine

Nothing great is ever easy!

@RG:   In my last game I was constantly culling and spawning animated sprites that were off camera.  Injecting them into the game for the player to interact with.  However, I did see jumpy game play sometimes and was looking for some best practice guidance here.

I guess if you only have X number of things in your game on camera at a time you could spawn all of them and keep them off camera until needed and then instead of spawning them a new just re-position them in the layer (DisplayGroup) that is about to cross the screen. 

My last world was small enough I could load the whole thing at once.  I was assuming, maybe incorrectly, that a scrolling game that was really wide would not be able to be loaded all at once and thus I was thinking I needed some type of spawnnewstuff() function that would do it on the fly.  And of course the cull offscreenstuff() function as well.   The use of tiled was just a way of building out the level data without doing it by hand like I did last time.  The end result would be the same though, I giant lua table that says where things go.  

Sorry for the run on scentances.  Inglesh ain’t me stong suit. 

@laura 

  1. Was that written on a mobile device?  

  2. Paragraphs please.  I know I’m weird for this, but I just stop reading when faced with a WOT response. 

Trying to read again…

PS - This is probably a sign that I’m old.  My brain literally feels like it turns off when I try to read posts like that.  I’m not picking on you so much as asking you to make it easier for old farts like me to read and understand your questions.

Wow - that is hard to read! But very nice of Laura to contribute.

As far as culling goes, I don’t think it’s necessary. My game runs pretty swiftly, and I have all level sprites “in existence” right from the start. As far as I’m aware, Corona has built-in culling since the revamped graphics engine, so no need to roll your own.

Still couldn’t entirely penetrate that, but I think you’re asking advise on how to handle really large ‘worlds’, where the content in those worlds may be off screen.

You also said something about culling and stuttering.

I’ll give a partial and unorganized answer, because a full answer would be pages and pages.

1.  There is an upper limit to system and video memory (sometimes a combined value) as well as texture units, buffers, etc.

>> Breaching these limits on any specific system/device will cause the hardware to start swapping resources which will cause noticeable slowdowns.

  1. Even before you reach the limits of memory, you may saturate computing. i.e. You may accumulate too much per-frame work.  This per-frame work is of two general types:

A. Work done by Corona.

B. Your scripts: Listeners, Timers, and the code you call as a result of events.

>> Once you breach the work-per-frame capacity of your CPU you will see skipped frames and other weird behavior.

Between #1 and #2 my guess is you are probably seeing #2 for the most part, but you may also be seeing #1 if you use a lot of different textures or many big textures at the same time. 

You can solve some of this big texture/too many textures problem by combining textures that are often used together into sheets.  This however is a very tricky thing and easily done wrong. 

So I’d save that kind of optimization for last.  In fact, I’d read up a lot on optimization before trying it.  Also, keep a backup of your project in a working state before you start putting in optimizations.  You may want to unroll those changes after you find out they saved you nothing and complicated your design.

What about #1?  How can you minimize encounters with this problem for really large worlds?  Some suggestions:

  • Minimize the amount of work you are doing.
    • If you can defer work to every other frame do it.  
    • If you can pre-calculate values do it.
    • If you can use a less accurate calculation that is faster do it.
    • Don’t draw objects until you need them and don’t delete them till you need to.
    • Consider using object caching (a pool of re-used display objects) for things like particles, or other shapes that don’t need to be modified significantly to re-use.
  • Minimize the cost of the work:
    • As noted in the above, you should consider substitutions that are cheaper in terms of CPU cycles to execute.
      • As an example.  Let’s say you need to take some action if object A is within distance 10 of object B.
      • Traditionally, you’d do a vector calculation and get the distance between A and B, then compare it to 10.
      • Better would be to get the squared distance (skipping the square root calculation in vector lengths) and compare the distance to 100 (i.e. 10 * 10).
    • Be aware of the fundamental costs of actions.
      • A great example of this is that accessing fields on a display objects take up to 30X longer than accessing fields on tables: http://davebollinger.org/robot-sb-dev-blog-get-set-performance/
        • In this case, you might find it valuable to have your world objects be Lua tables and when needed have those tables own and draw a display object.
        • Then do all calculations on the Lua object as much as you can, only making the finial changes on the display object if needed.

Again, this discussion can go on and on and on.  It is very technical, experience based, and requires a huge amount of effort to do right. 

Best of luck on your game.  Enjoy the journey.

To be absolutely clear, I don’t advocate you make any changes for optimization sake till lat in the game, with the exception of one thing.

If you are going to take the proxy object approach (Lua object as game object that owns a display object(s)), you have to do that from the start. 

I don’t actually like this approach as I prefer to deal with raw display objects and tack functionality onto them.  i.e. I eat the access cost because most casual, puzzle, and other smaller games won’t breach their CPU budget anyways.

Once you get into big world games this is however a serious issue.  I would warrant a guess that games like Designer City an d other big world games use some kind of proxy approach.  @sgs could comment on this if he reads the post.

Heh - funny how everybody has his or her own preferences. I differ from roaminggamer in that I prefer to create game objects as “code objects” that have a display objects as one of their variable. There’s no right or wrong here, only habits and preferences.

@thomas6,  

    My last game had no time limits so as a person ran around eating things and exploring, new things needed to be added to keep things interesting.  If a person played a level for 20 minutes, not unheard of,  there would literally be 100s of things spawned in the time.   I didn’t see any way of spawning that many things at the beginning of the level.  In a more controlled game I could see being able to do it all up front. 

   My sprites were also autonomous in the last game so once spawned they just did their own thing.  So I could not know where they might end up at a certain time.  I would end up just culling them if they made it off the game map which was 6840 by 2280.  So they had quite a lot of room to run. 

   When you say culling wasn’t necessary how big of game map are you referring to? 

In an attempt to more succinct:  

If a world is ( landscape ) 1000 pixels high and 35000 pixels wide is it worth setting up a spawning / culling system that only spawns what is in the current camera view?   What about 75000 pixels?  What are the limits?  

Does adding new physics objects take a lot of CPU?  If so, then maybe instead of culling set up a re-use system. 

L

I heard my name… and yes I am somewhat qualified to answer.  So yes I have large worlds in my games that are constantly updating in real time (stats, values, etc.).

As @rg has stated for small arcade games optimisation is not really required and your memory bandwidth is low.  But when your texture memory goes over 100MB and your object count gets into the thousands you absolutely have to consider some culling methods and you cannot rely on Corona doing it for you. 

For example, with Corona doing the culling and a (master) group zoom of 0.5x I was struggling to get over 10fps.  By coding my own display logic I can now sustain 30fps on a decent device (60fps is but a dream!)

I absolutely use a proxy approach - looping pointers is MUCH faster than looping groups.  A building (in my world) is around 4k lines of code and exposes 20-odd interface functions that get manipulated by my game loop/UI. Each building is then inserted into a giant 2D array and accessed as follows

buildings[x][y]:someFunction()

Now even this becomes a point to optimise further.  So I will bake certain values into boolean or integer 2D arrays - for example a 2D boolean array indicating if a tile is occupied or not.  It is much faster (and easier once all coded) to do this simple lookup

if tileIsEmpty[x][y] then ...... end

rather than

if not buildings[x][y].isRoad and not buildings[x][y].isRailway and not buildings[x][y].isBuilding (etc)

Sure there is some small overhead for rebuilding the lookups but this is a single hit every so often.

@Laura, assuming you have say <500 objects (that are not too huge) that make up your scrolling world what I would do is load them in advance and place them in a hidden group.  This way you are culling them as Corona will ignore display objects in a hidden layer.

When they are about to come on screen, you simply insert them into your game group and position accordingly.  When they go out of scope you then re-insert them into your hidden layer.

This will be much faster than a constant create/destroy schema and much less likely to leak memory too.

Ooooo,  I like this hidden layer idea.