Modulous development

Hi Richard,

If you require a module in different places of your code, in reality it will only be loaded once. All extra times it is required, it will just be a sort of copy, or reference to the same already loaded module.

In reality this is super handy, because you could, for example, have a module that contains the game score, or settings that are needed across your app. Everywhere you require this module, you will have a single “store of values” that is the same one. In a way, it is like a “global” module wherever you require it.

So, how do you create multiple, separate instances of a single module? Here’s how:

Create a module called npcClass.lua:

local npcClass = {} npcClass.new = function(startingHealth, playerSpeed) local self = {} self.health = startingHealth self.speed = playerSpeed return self end -- npcClass.new() return npcClass = {}

In the example above you could replace “self” with “newPlayer” if that reads better for you, but “self” is more standard and more practical, trust me.

Then two create two separate instances of an npc just say - in main.lua for example, or another module:

loyal npcClass = require("npcClass") local player1 = npcClass.new(100,5) -- for health and speed local player2 = npcClass.new(90,2)

That’s it! This is very simple, but also extremly powerful.

Bingo, I think it’s all clicking in to place now, thanks!

You’re welcome. Let us know if you need clarification.

Honestly, I’ve programmed in many languages, and I do feel that for 99% of my programming needs, the pseudo-OOP approach of Lua is a wonderfully quick and simple way to do things.

When you require a Lua module, it gets loaded, executed and it returns a table.

When you require it again you simply get a reference to the same table.

Ah yes, excellent point by Arteficio: when you require a module, the code inside the module is executed, which is superhandy. And then the variable that is returned at the end (typically a table containing functions and variables) is returned.

The second time you require the module, the code is not executed anymore.

Hi,

What I am going to demonstrate here is extremely abstract. If there is interest I can follow up with a more detailed blog post on some various methods of building Lua based plugins at some point, but for now this will hopefully give you something to study.

This is aimed at answering your original question. There are a number of different things to consider depending on the functionality of your specific plugin, including scope, and whether there will be any asynchronous activity within separate modules. Such as methods from the Corona network library.

When developing in Lua, I tend to lean toward composition; passing specific modules to other modules where that functionality is needed. But again, this is very dependent on what you are trying to achieve with the plugin. Some methods are better than others.

In my experience, there are three main approaches; class instances, composition, and event driven. As noted, I tend to use composition, and because of the asynchronous nature of Corona, event driven more often. As others have mentioned, there is always a central point of entry which handles the other modules accordingly.

With all of that in mind, here are the various pieces presented in an abstract way and only with your original question in mind based on the composition methodology.

So first the project tree:

plugin myplugin dataHandling.lua engine.lua rendering.lua myplugin.lua main.lua

And the files:

myplugin.lua

local Library = require "CoronaLibrary" -- Create library local lib = Library:new{ name='myplugin', publisherId='com.develephant' } return setmetatable( lib, { \_\_index = require("plugin.myplugin.engine") } )

engine.lua

--============================================================================= --== Plugin Components --============================================================================= local rendering = require("plugin.myplugin.rendering") local dataHandling = require("plugin.myplugin.dataHandling") --============================================================================= --== Main Engine --============================================================================= local engine = {} --============================================================================= --== Engine Methods --============================================================================= function engine.doSomething() --call a render method rendering.doSomething() --do some data handling dataHandling.doSomething() end function engine.doSomethingWithInput(var1, var2) --call rendering with argument local result = rendering.doRenderThing(var1) --return the result return result end function engine.renderWithDataHandling() local res = rendering.withDataHandling(dataHandling) end --============================================================================= --== Engine Export --============================================================================= return engine

rendering.lua

--============================================================================= --== Rendering Component --============================================================================= local rendering = {} function rendering.doSomething() --render something end function rendering.renderThing(var) --render a thing, return result return some\_result end function rendering.withDataHandling(dataHandling) local result = dataHandling.getSomeData() --do some rendering stuff return some\_result end return rendering

dataHandling.lua

--============================================================================= --== DataHandling Component --============================================================================= local dataHandling = {} function dataHandling.doSomething() --data handle and return result return some\_result end function dataHandling.getSomeData() --get some data return some\_result end return dataHandling

main.lua

local myplugin = require("plugin.myplugin") --These following methods live in the engine.lua myplugin.doSomething() local result = myplugin.doSomethingWithInput("hello")

This may end up leaving you with more questions than answers, but it does demonstrate a method to solve your original question in regards to plugins.

Hope this helps at least give you some initial direction.

-dev

Thanks very much, everybody.

Develephant, your approach I feel is probably the most proper way to go about this and removes the possibility that two libraries might try to create the same function name, but I can foresee engine.lua filling up with functions this way, which almost defeats the purpose of making things modulous.

What I’ve ended up doing, and I’m quite pleased with it, is creating the main engine.lua as just require()'s and init()'s as per Arteficio’s approach, and then at the top of each required library file, I’m require()'ing a main globals.lua for the global variables as per the example given on https://docs.coronalabs.com/tutorial/basics/globals/index.html , plus also require()'ing any of the other liraries that this library relies on.

I’m happy with how tidy this approach is and I like that I now effectively have global variables that are only global within the scope of the plugin, and I especially like that the init() approach means I can pick and choose which functions to make available to the plugin user without having to declare them any differently for use internally. My only concern now is that I don’t think two libraries would be able to require each other without this causing an infinite loop. I suppose though, this just means I’m forced to be more logical with the structuring which is probably a good thing.

Main plugin file:

[lua]local Library = require “CoronaLibrary”

local lib = Library:new{ name=‘foobah’, publisherId=‘foo.bah’ }

local module1 = require “plugin.foobah.module1”

module1.init(lib)

local module2 = require “plugin.foobah.module2”

module2.init(lib)

return lib[/lua]

Modules:

[lua]local vars = require “plugin.foobah.globals”

local module = {}

local someLocalVariable = 1

module.someFunction = function()

    someLocalVariable = someLocalVariable + vars.someGlobalVariable

    return someLocalVariable

end

module.init = function(main)

    main.someFunction = module.someFunction

end

return module

[/lua]

And the globals:

[lua]local module = {}

module.someGlobalVariable = 1

return module[/lua]

Out of interest, is this just how Lua works as a language, or is it Corona’s interpretation of Lua that means subsequent require()'s don’t create new instances?

All of Corona is pure Lua, to exact specification, and the proprietary API for the Corona engine.

I’m curious about what your plugin will be doing, that it has such stringent code organisation needs :slight_smile:

All of the above, including my final snippet, is of course pseudo code. The plugin I’m actually building is an isometric game engine… coming along nicely too, I just needed to tidy it up somewhat before moving forwards and thanks to everybody here, that’s now done.

So far it converts Tiled maps into engine maps with layered tiles, renders them in isometric, incorporates pinch-to-zoom functionality and can snap the camera to any tile position. It can convert between any of raw tile co-ordinates, cartesian co-ordinates, isometric co-ordinates, and screen co-ordinates so in turn can convert a screen click in to which tile was selected or even which specific point within a tile was selected.

Swipe to pan and snap to object/character will be supported, allowing for free-roam isometrics or character-roam isometrics. AStar pathfinding algorithms will be built in allowing for object/character movement by simply passing a new tile position to walk to. Individual tile or full map data injection/removal will be supported allowing, basically, for tile layers to be removed or appended to a tile, for when objects need placing/taking or doors need opening/closing, for example. And I might, just might, build in some crude lighting engine by basically layering black over each tile and poking gradient holes to effectively create darkness masks… but I’ve not yet decided on the feasibility of this one.

I’m building this really for internal use more than anything else, but will be releasing to the Corona marketplace where there appears to be a distinct lack of isometric engines.

@richard11

That sounds like an awesome plugin.  Count me in!

Richard,

Great idea for a plugin.  I am looking forward to seeing it.

Bob

Thanks both =).

A very crude sample apk attached for anybody interested. Android only at the moment as we don’t have an iOS license yet, but it’s entirely Lua so should be cross platform when we come to a proper release.

The sample allows panning the map by dragging with one finger, zooming in/out with pinch gestures, and centering on to specific tiles by tapping them.

To produce this sample is just a few simple lines:

[lua]-- Load plugin library

local qiso = require “plugin.qisoengine”

– First, tell qisoengine where maps and sprite files are.

qiso.setAssetsFolder(“assets”)

– Load a Lua formatted Tiled Map Editor tilemap into a Qiso map.

qiso.loadTiledMap(“sample-tilemap”)

– Enable zooming when the screen is pinched

qiso.enablePinchZoom()

– Enable camera panning when the screen is dragged

qiso.enableCameraPan()

– Center on the tapped tile

function tapTile(event)

    – Get the tile located where the screen was tapped. If there’s no tile here, getTile returns nil.

    local tile = qiso.getTile(event.x, event.y)

    if(tile ~= nil) then

        qiso.goTo(tile.x, tile.y)

    end

end

– Main game loop

function mainLoop()

    – Render the map

    qiso.clearGraphics()

    qiso.renderMap()

end

– Do something when the screen is tapped

Runtime:addEventListener(‘tap’, tapTile)

– Run the main game loop

Runtime:addEventListener(‘enterFrame’, mainLoop)[/lua]

There are a few other functionalities that this sample doesn’t show off, and still lots to do before it’s ready for release, but the general idea is to make developing isometric games as simple as possible without losing any flexibility.

In the hopes this sparks some excitement, we’ve set up a mailing list that you can subscribe to if you’d like to be notified when this plugin is released.

http://campaigns.qweb.co.uk/h/i/3A142358D02BBDE3

Hmm. I can’t tell if I’m just not looking in the right place, or if my attempt to attach the apk to that post just didn’t work. Here’s a hosted version: https://development.qweb.co.uk/qiso/tilemaptest.apk

Updated the APK with a better example, now that a) the engine properly supports layering and b) we have some proper graphics created. Also attached a screenshot for those who don’t have Android to play with it on.

I won’t update here again as it’s really not the right place for it, so please do sign up to the above mailing list if you want to be notified when released.

When can you release this plugin?. I am very excited

Had to change priorities a little for a while but progressing with this development again now and hoping to release within the next few weeks. Probably early - mid February. Performance isn’t brilliant at the moment (mainly need to incorporate off-screen tile culling) and I really want to get path-finding algorithms in place for the first release.

If you haven’t already signed up to the mailing list at http://campaigns.qweb.co.uk/h/i/3A142358D02BBDE3 please do so, and we’ll notify you the moment we release =).

Regards.