Jump to content

[TOPIC: topicViewTemplate]
[GLOBAL: userSmallPhoto]
Photo

When does it make sense to employ a module instead of a function within a scene file?
Started by danpreneur Jun 28 2016 06:15 PM

10 replies to this topic
[TOPIC CONTROLS]
This topic has been archived. This means that you cannot reply to this topic.
[/TOPIC CONTROLS]
[modOptionsDropdown]
[/modOptionsDropdown]
[reputationFilter]
[TOPIC: post.html]
#1

danpreneur

[GLOBAL: userInfoPane.html]
danpreneur
  • Enthusiast

  • 40 posts
  • Corona SDK

This question has boggled me for a sometime now. As an amateur programmer, I have worked on projects that require no more than 3 files at most. But, ever since I started working on my mobile game, I realized that I need multiple files to handle the development. I see the benefit in having multiple files, but there are times when I'm confused about the following:

 

1. When does it make sense to create a module instead of functions within a file?

 

For instance, there are functions I have created for a level selection scene. The functions include making a grid, sliding, saving and loading. I am not sure whether I should place them in an external module that manages level selection.

 

2. How do you determine where a function belongs?

 

Should the function for creating a grid with level buttons be placed in a level selection scene or an external module?

 

3. How should files interact with one another?

 

Suppose that there are three files in question, game_scene.lua, level_selection_scene.lua, and level_manager_module.lua. The purpose of the level_selection is to instantiate methods, such as saving and loading, locking and unlocking, and displaying points. The methods are all placed in the level manager module which are imported in the level_selection page. After a game level ends and a score needs to be saved, should the information regarding score be dispatched directly to the level manager module or via the level_selection_scene file?

 

4. What are some common Do's and Not Do's when handling multiple files?

 

 

Thank you in advance.



[TOPIC: post.html]
#2

roaminggamer

[GLOBAL: userInfoPane.html]
roaminggamer
  • Corona Geek

  • 7,666 posts
  • Corona SDK

I make modules when:

  • I'm going to use the function in more than one file/scene.
  • I'm going to use the same code on a future project.
  • I want the option of using the same code on a future project.
  • When I want to create other 'like' and/or related functions and have the associated in them same locality.

Answers to specific numbered questions
 
#2 I would put 'builders' of any type in modules.  Writing long hand 'button creation code' (for example) over and over or even duplicating it between scene files would be extremely wasteful and error prone as well as hard to maintain.
 
#3 - I generally avoid the mess of direct calls to data managing modules by using custom events and listeners.  That way the module can do it's work without knowing about the code that uses it and vice versa.  However, some folks are more comfortable with direct function calls and sometimes that simply make more sense.

 

Personally, my scene files are practically empty.  They just have the scene methods and then call modules to do all the heavy lifting.  In reality I'm simply using composer scenes to:

  • Get nice transitions.
  • Get easy memory handling.
  • Get easy organization of content, but most of this is actually handled by well organized modules.

My main menu scene file may do a little work to make buttons and stuff, but my playgui.lua scene file just calls the game module and other modules to do all the work.

 

 
#4 - Don't require files from each other (at least not at the file-level).
a.lua

local b = require "b" -- file-level require

local public = {}

function public.doit()
end

return public

b.lua


local a = require "a" -- file-level require

local public = {}

function public.doit()
end

return public

However, you can do this:
a.lua

local public = {}

function public.doit()
   local b = require "b" -- function-level require
   b.doit()
end

return public

b.lua


local a = require "a" -- file-level require

local public = {}

function public.doit()
end

return public

(I know the example is stupid, but it shows how to cross-require files safely). 



[TOPIC: post.html]
#3

Rob Miracle

[GLOBAL: userInfoPane.html]
Rob Miracle
  • Moderator

  • 26,682 posts
  • Enterprise

Adding on to Ed's most excellent answer....

 

When you get files that are too many lines and you're using functions defined at the top of the file at the bottom of the file (common with Composer scenes) you end up scrolling your editor a lot. It takes time to scroll. You loose your place where you were editing and so on. If your code file is too long, perhaps it's time to look to pull out code into another file. Most editors support tabs or having multiple windows open so each tab/window can be at the code your working at, it's quicker to switch between the two and not loose your place or time scrolling.

 

The other thought is keep the MVC (Model-View-Controller) design pattern in mind. In MVC, Model is your data. You should be able to have code that manages your data without knowing anything about the rest of the app. In database terms, this would be having functions that created, update, read and delete records (CRUD). Your data shouldn't care if your screen is blue or red or if you draw the last name first, or first name first. You would have a module around maintaining your data/game etc. 

 

The View is what's being displayed. The view doesn't care how the data is managed. It knows about certain databits and it's focused on drawing them.  In Composer, at least at an abstract level a "scene" is the view. We even named the display group "view".

 

The job of the controller is to marry the Model to the View. If for instance your view is going to show the players name, but the database has two fields. The controller's job is to take data from the model and massage it into what the view is expecting.

 

MVC is a good way to organize your app. The game scene technically doesn't care how enemies get spawned, just that they do. But the scene does care where the enemy is.

 

The result is if you find your scene files getting too long it's time to start pulling things out. As Ed pointed out, things that might get used multiple times are great candidates for extraction to their own modules.

 

Rob



[TOPIC: post.html]
#4

danpreneur

[GLOBAL: userInfoPane.html]
danpreneur
  • Enthusiast

  • 40 posts
  • Corona SDK

Thank you both for quality responses. I now have a clarity over when and why I should create an external module and how to manage multiple files in a big project. :)



[TOPIC: post.html]
#5

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 987 posts
  • Corona SDK

I'll share my personal habit here, focused on games with spawned objects.

 

1) I create a separate modules for every enemy or bullet or item. This makes things really easy to code, because you get a lot small, highly specialised modules. I'm oversimplifying a bit, but I think that the more separate modules you have, the better!

 

2) Very important: think in hierarchical steps, and create seperate modules for every "level" in the hierarchy. For example, if I have bullets, then I have the following classes:

 

bullet.lua - this module (and pseudo-class) is responsible for bullet creation, the bullet movement and collision detection(in a frameloop) and bullet destruction. But every bullet is only aware of himself and nothing else.

 

bulletManager.lua - this module is responsible for keeping track of all bullets, and for destruction and cleanup of all bullets at the end of the level.

 

gameEngine.lua - this module is responsible for top-level events like loading, starting and ending levels.

 

So when a level starts or runs, the gameEngine.lua module tells the bulletManager to add a bullet to his list of bullets. The bulletManager.lua module does this by creating a new instance of the bullet.lua module. Please note that this is a style of pseudo-OOP.

 

In the same vein, when a levels ends, there is a function in gameEngine.lua that tells the bulletManager.lua to do general cleanup, and bulletManager.lua tells each bullet in his list to "destroy".



[TOPIC: post.html]
#6

danpreneur

[GLOBAL: userInfoPane.html]
danpreneur
  • Enthusiast

  • 40 posts
  • Corona SDK

I'll share my personal habit here, focused on games with spawned objects.

 

1) I create a separate modules for every enemy or bullet or item. This makes things really easy to code, because you get a lot small, highly specialised modules. I'm oversimplifying a bit, but I think that the more separate modules you have, the better!

 

2) Very important: think in hierarchical steps, and create seperate modules for every "level" in the hierarchy. For example, if I have bullets, then I have the following classes:

 

bullet.lua - this module (and pseudo-class) is responsible for bullet creation, the bullet movement and collision detection(in a frameloop) and bullet destruction. But every bullet is only aware of himself and nothing else.

 

bulletManager.lua - this module is responsible for keeping track of all bullets, and for destruction and cleanup of all bullets at the end of the level.

 

gameEngine.lua - this module is responsible for top-level events like loading, starting and ending levels.

 

So when a level starts or runs, the gameEngine.lua module tells the bulletManager to add a bullet to his list of bullets. The bulletManager.lua module does this by creating a new instance of the bullet.lua module. Please note that this is a style of pseudo-OOP.

 

In the same vein, when a levels ends, there is a function in gameEngine.lua that tells the bulletManager.lua to do general cleanup, and bulletManager.lua tells each bullet in his list to "destroy".

 

Thank you for the example. So to build on what you said, if I have another class element, let's say, rifle which fires the bullet. I would have a rifle_class.lua responsible for rifle creation and properties based on rifle types and a rifle_manager.lua that loads and destroys, right?

 

Now suppose that a bullet fires from a rifle and the user taps on the rifle. If there are manager files that are responsible for tracking, spawning and destroying the objects, what might be the purpose of the show scene in the gameEngine.lua in this case?

 

Would that be used to pass in data to the UI element, let's say keeping track of and displaying score or rounds fired?



[TOPIC: post.html]
#7

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 987 posts
  • Corona SDK

Hi!

 

First of all: "it depends", and there's many ways of doing things.

 

Now, there's always a couple of questions to check up: if you will only have 1 piece of something, then you don't need to make a class with constructor methods etc. and you don't need a manager. So it depends: do you see it as multiple rifles that need to be managed, or as one rifle that can change in type. I would go for the latter. "manager" modules, in my mind, are only necessary when you have multiple instances of the same thing existing at the same time. But your way of thinking may be different, and that's okay.

 

Could I ask you something? Can you describe the game you want to make, briefly? That will help me to say how I would structure it.

 

p.s. regarding my first point: I don't know how good your OOP-knowledge is, but people sometimes call something a "singleton" class, when there will never be more than one instance of it. A user inferface, for example, is a perfect example of a singleton class, because typically you will only have one of these in existence in your game. Singleton classes are simpler modules, and don't require "managers".



[TOPIC: post.html]
#8

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 987 posts
  • Corona SDK

By the way, in my latest project each of my enemy types is a separate module. Each bullet type is a separate module. My hero is a separate module. Coins, laser, switch, keys, doors etc... are all a separate module.

 

So I have about 70 different modules, and some are pretty small. But it works great this way, because it's super easy to code. Fix a bug with my hero passing through a door? Then it's either in the hero.lua or door.lua file. Error when a bullet hits a moving platform? Then it's either the bullet.lua or platform.lua file.

 

Okay, or maybe the bulletManager.lua file ;)

 

In my opinion, I would try to go for the biggest amount of separate modules, as long as they are "logical". There's no sense in making a separate "leg.lua" module for my hero.lua character. And maybe there is no reason to keep your rifle out of your here character, by the way.



[TOPIC: post.html]
#9

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 987 posts
  • Corona SDK

One more thing, sorry: I don't use any pre-made scenemanager, so I can't answer your question about scene stuff, unfortunately. But regarding a UI for showing score and shots fired etc... I would create a singleton class or module called gameScoreUI.lua or something like that.

 

Then I'd have one function for creating the displayObjects on screen, one to destroy all displayobject at the end, and then a couple of function to update the score bar etc... And I'd just require the gameScoreUI.lua modula in from any other modules that needs to tell the game that the score has changed.

 

Remember that when you require a module in from many different places, it is only loaded the first time. The other requires just create a reference to the same module - which is superhandy, because this way all of these other module will see the same value for a variable like gameScoreUI.mainScore and gameScoreUI.shotsFired

 

It's magic! ;)



[TOPIC: post.html]
#10

danpreneur

[GLOBAL: userInfoPane.html]
danpreneur
  • Enthusiast

  • 40 posts
  • Corona SDK

I see! Currently I'm building a 2D mini-golf game and as you suggested I started implementing some aspects of OOP. I don't have that much experience with it, but I am aware of what it is.

 

So basically, it seems what I should do is every time there's an entity with distinct features and functions, I'd want it to contain its own module. If let's say the rifle module is loaded into bullet module to pass in data of some sort, does that break the concept of OOP? Or, should there be another file manager serves as a bridge for the two distinct classes?



[TOPIC: post.html]
#11

thomas6

[GLOBAL: userInfoPane.html]
thomas6
  • Contributor

  • 987 posts
  • Corona SDK

Hi Dan,

 

Basically you create separate modules, that have their own methods ( = functions or actions or whatever ) and their own properties ( = variables, or data or parameters or whatever). When module A needs access to functions or variables of module B, you need to require module B in module A. This doesn't break the concept of OOP.

 

By the way, in Corona and Lua game programming OOP can be as simple as splitting your code into modules, or distinct "objects" that each have their own functions and variables. The cool thing is that if you build all your separate simpler modules so that they can never fail or crash, then your program will always work more or less instead of crash!

 

One step up in complexity is simple pseudo-OOP using prototypes, which is basically a difficult way to say that you create modules that you can spawn - like creating instances from classes.

 

Then moving on in complexity you get inheritance and polymorphism etc... Inheritance is not too difficult actually. It's really cool stuff. I'm not a programmer from background so it took me a while to get my mind around some stuff, but organising my modules is something I really, really, really enjoy now.

 

It also helps me to clean my code up tremendously! My gameEngine.lua code reads something like this:

levelManager.readCurrentLevel()
graphicsLoader.loadGraphics()
levelManager.buildLevel()
uiBuilder.buildUI()
gameEngine.startGame()

Because all of the "hard" code is in separate external module, I get something in the end that is really easy to read, follow and understand. Tres cool! :)

 

One thing to watch out for: you can not create infinite "loops" in requiring. That means that if module A requires module B, then you can not also require module B in module A. This is sometimes tricky, and makes you scratch your head from time to time, but it's actually quite fun. A solution for this, by the way, is creating a module C that requires module A and B. This way A and B see eachother "in both directions" and can communicate easily. But there are other solutions. Creating this structure or architecture is challenging at first, but it actually makes life a LOT easier, and coding faster once you get the hang of it.




[topic_controls]
[/topic_controls]