Best way to separate level logic from core game interactions?

Hi everyone! I’m new to Corona, and I’m hung up on how to structure my game so that I can keep my basic interactions separate from level-specific logic. I’ll try to provide a barebones example of what I need.

Let’s say we’re making a simple dodging game where the main character (a circle) must dodge the bad guys, (the squares). The circle is controlled by the player, and the squares are generated and moved in different ways based on the level (e.g level 1 might have falling squares, and level 2 might have squares coming in from the sides).

The “core mechanics” here are that the circle is controlled by the player, and that if the circle touches any of the squares, it’s game over. What is the right way to structure the game code so that these core mechanics can be included in every level with little duplication?

That’s it for the question, but here’s what I’m thinking so far (I could be totally off):

circle.lua : exposes a method to create a new circle, and takes care of player control.

square.lua : exposes methods to create and move a square as a level might require.

collisions.lua : handles collisions between the circle and squares.

level (x).lua: the level-specific files that will include the above three.

The challenge with this approach is in the “collisions.lua” file. It needs to know the location of the circle and every square in the scene so that it can determine when there is a collision. I’m not confident enough to guess the best way to make that happen.

Any insight you can offer to put me in the right direction is appreciated!

you probably also need a “world.lua” in your hierarchy, to “own” the level/circle/square’s along with “glue” to tie them all together.  (i’d give every “child” of “world”, ie the circles and squares, a reference to their parent, so that they could traverse upward for functionality if/as needed)

in that convention “level(x).lua” would just be data for “world” - or use any other combination of names, whatever makes sense to you, but doesn’t really matter - the key being that at some point you need your level “description” files to be free of code, because THAT is what will give you the no-duplication “flexibility” you’re after.  (restating:  do NOT put mechanics in every levelx.lua, put it in world)

if by “collisions” you mean where you’d place event listeners for the physics library then they’ll get references to the colliding bodies in the event data.  if you mean something else, like your own collision handling, then as long as “world” passes the relevant data to “collisions” it ought to be able to sort it all out and resolve the references.  only you can know what you need here.

Ah, “world.lua” is definitely something I needed. Thanks for the insight! I guess I can keep each individual object as dumb and self-concerned as possible and handle the interactions between objects in “world” itself, because that is, after all, the space in which all objects interact. This way “world” itself becomes another dumb object that only takes care of its duties for its own “physics” (I’m not using the physics library). Other events (like losing) can be signaled to something higher, like a state manager perhaps. The world simply needs to shout “Hey, circle hit a square!” and the state manager can choose what to do. Hmmm… or maybe not. But either way, this helps my thinking a lot, so thanks!

One more question: Are there any sufficiently complex example projects that would be worth looking at to learn more about best practices, etc? Preferably something with dynamic, varying levels (not just maps).

(On a side note, in the actual game I’m making I do not intend for the levels to simply be data, as they’ll have dynamic events that pertain exclusively to the level. But it still makes sense not to include any core mechanics in the level.)

see this and this for message-based communication

Corona Cannon is perhaps the most “complex” example (though it’s maps per level)

the “no code” was just a rule of thumb, to convey the concept.  as long as you get the division of labor, and each “level” exposes same interface to “world”, then if a level needs its own level-specific code go ahead and break the rule.  iow, each levelx.lua (lowercase instance) should appear as (or actually be) an instance of Level.lua (uppercase class), then you’re good as far as “world” is concerned.

Really helpful information! Thank you for all your help. Corona Cannon is a great example.

you probably also need a “world.lua” in your hierarchy, to “own” the level/circle/square’s along with “glue” to tie them all together.  (i’d give every “child” of “world”, ie the circles and squares, a reference to their parent, so that they could traverse upward for functionality if/as needed)

in that convention “level(x).lua” would just be data for “world” - or use any other combination of names, whatever makes sense to you, but doesn’t really matter - the key being that at some point you need your level “description” files to be free of code, because THAT is what will give you the no-duplication “flexibility” you’re after.  (restating:  do NOT put mechanics in every levelx.lua, put it in world)

if by “collisions” you mean where you’d place event listeners for the physics library then they’ll get references to the colliding bodies in the event data.  if you mean something else, like your own collision handling, then as long as “world” passes the relevant data to “collisions” it ought to be able to sort it all out and resolve the references.  only you can know what you need here.

Ah, “world.lua” is definitely something I needed. Thanks for the insight! I guess I can keep each individual object as dumb and self-concerned as possible and handle the interactions between objects in “world” itself, because that is, after all, the space in which all objects interact. This way “world” itself becomes another dumb object that only takes care of its duties for its own “physics” (I’m not using the physics library). Other events (like losing) can be signaled to something higher, like a state manager perhaps. The world simply needs to shout “Hey, circle hit a square!” and the state manager can choose what to do. Hmmm… or maybe not. But either way, this helps my thinking a lot, so thanks!

One more question: Are there any sufficiently complex example projects that would be worth looking at to learn more about best practices, etc? Preferably something with dynamic, varying levels (not just maps).

(On a side note, in the actual game I’m making I do not intend for the levels to simply be data, as they’ll have dynamic events that pertain exclusively to the level. But it still makes sense not to include any core mechanics in the level.)

see this and this for message-based communication

Corona Cannon is perhaps the most “complex” example (though it’s maps per level)

the “no code” was just a rule of thumb, to convey the concept.  as long as you get the division of labor, and each “level” exposes same interface to “world”, then if a level needs its own level-specific code go ahead and break the rule.  iow, each levelx.lua (lowercase instance) should appear as (or actually be) an instance of Level.lua (uppercase class), then you’re good as far as “world” is concerned.

Really helpful information! Thank you for all your help. Corona Cannon is a great example.