Tuesday, 29 April 2014

Composer replacement

My composer replacement is coming on quite nicely. I've got the basics working, and created a special class that allows intermediate scenes to be more or less automatic - so you can create a scene which is shown for a specific period of time and then exit.

Monday, 28 April 2014

Working on my own version of Composer.

I'm working on my own version of Composer.

I have separated the Transitions into a separate library - so the transitions bit now just does a transition from one scene to another (or a scene from/to no scene), and a Scene Manager with some scene classes that uses the Transition library.

It sort of works at present - it needs some extra testing, but it allows the declaration of classes for scenes, e.g.

SceneClass = sm.Scene:new()

function SceneClass:create()
local vg = self:getViewGroup()
self.text = display.newText("Scene "..self.id,160,240,native.systemFont,32)
self.text:addEventListener( "tap", self )
print("Scene "..self.id.." create")

function SceneClass:tap(e)
return true

function SceneClass:setup(id,tgt) self.id = id self.tgt = tgt return self end

function SceneClass:getTransitionType() return "slideright" end

s1inst = SceneClass:new():setup("One","secondscene")
s2inst = SceneClass:new():setup("Second one","firstscene")



So it declares a new scene class, which just plonks some text on the scene and attaches a listener to it. which goes to another scene (the 'setup' method is just to name the scene and where it goes after easily, it's a bodge for testing).

The getTransitionType() method defines the transition - it then creates two separate instances of the class (s1inst and s2inst), adds them to the known classes, and goes to the first scene (smgr is an instance of "SceneManager", sm is an instance of the SceneManager library).

It's on github https://github.com/autismuk/Scene-Manager but it is definitely a work in progress.


I find the number of icons required increasingly absurd in Apple and Android applications (obviously, this isn't Corona's fault !)

Both in generating the things and also in the clutter they put in the root directory. Really, the root directory should just contain the configuration files, main.lua and subdirectories, not 317 icons all of which are basically the same.

In an attempt to fix both I have written 'iconcreator.lua' which is a lua script file (so it requires the lua command line interpreter, it's not a Corona Application). This has one file, icon.svg, which is a vector graphic of the icon.

Vector graphics can be scaled to any size without pixellating or any other problems, save for if they get really small detail will be lost.

The script will create icons of the required sizes for Android and Apple. Ouya could be easily supported, but Ouya's icon is not square.

It will also delete the icons - so you can get rid of the things when you are developing, and run the script just before building for release, then remove them again.

You can get it here https://github.com/autismuk/IconCreator

Corona and the Model-View-Presenter pattern - Index

This is the index for my seven part short series on Model-View-Presenter in Corona.

Part 1
Part 2
Part 3
Part 4
Part 5
Part 6
Part 7

Source for the series is available from github

Corona and the Model-View-Presenter pattern (Part 7)

Welcome to the last part of the MVP Tutorial.

One of the main faults of the game is that the control system is pretty dire. It's not that easy to track the little bat with a mouse. So we're going to add a button to do the same thing.

This shows one of the advantages of separating everything and decoupling it (to some extent !). The control is purely a function of the view - if you recall from a previous part, the view detects the button click, and sends a message to the presenter saying "I've had a button click".

So all we need to do is to add a button, and get it to send that button click again - so nothing changes but view.lua, which has the following added to its initialise function.

view.scoreObject = display.newText("<score>",315,5,native.systemFontBold,32) 
view.scoreObject.anchorX,view.scoreObject.anchorY = 1,0

view.actionButton = display.newImage("button.png") -- create an action button
view.actionButton.anchorX, view.actionButton.anchorY = 0.5,1 -- position it, size it, etc.
view.actionButton.x,view.actionButton.y = 160,475
view.actionButton.xScale,view.actionButton.yScale = 0.3,0.25
view.actionButton.alpha = 0.8
view.actionButton:addEventListener( "tap",view )   -- it sends the message as well.

We create a new display object, put it where we want it, and make it slightly transparent. Then we add a second event tap listener. Any tap event here is routed to the view:tap function, which sends a message to the presenter.

- and that's all there is to it....

The final version is available from https://github.com/autismuk/MVP-Intro/tree/master/Part7

Thank you for reading ; any questions please feel free to ask them here, email me or ask on the forum (I might spot them).

Sunday, 27 April 2014

Corona and the Model-View-Presenter pattern (Part 6)

Welcome to the sixth part of the MVP Tutorial.  One left :)

Regarding Part 5 ; the best way of inverting the display, with the minimal amount of change, is simply to process the 'y' coordinates - replacing model.thing.y with 480-model.thing.y - the game will work exactly the same, it will just be upside down.

One of the advantages of this sort of thing is the ability to make changes without those changes having too many knock on effects. This part, and the next, will show those in practice.

Part 6 is dead simple. We are going to replace the circle with an actual picture of a ball.

This will be very very small on a 320x480 resolution, but it is possible to see it, just. If you want it bigger, you can amend the radius in the model. - the radius is part of the 'game data'.

So, we can change this without worrying about the model, or the presenter, it is just a visual change. To do so, we have to change both the initialise functions, and the update functions. This is the 'new' initialise function (the unchanged bits are, as usual in italics)

view.presenter = presenter
view.batObject:addEventListener( "tap",view )

view.ballObject = display.newImage("ball.png")

view.scoreObject = display.newText("<score>",315,5,native.systemFontBold,32)
view.scoreObject.anchorX,view.scoreObject.anchorY = 1,0

All we have done is take out the circle and replace it with an image. From the viewpoint of the model or the presenter it doesn't matter what it is - it could be three sprites, or drawn out of lines, it doesn't matter.

We have to update the view slightly differently - originally we provided a radius for the circle, now we provide a height and width for the sprite.

function view.update(model)
view.batObject.x = model.bat.x 
view.batObject.y = model.bat.y
view.batObject.width = model.bat.width

view.ballObject.x = model.ball.x
view.ballObject.y = model.ball.y
view.ballObject.width = model.ball.radius * 2
view.ballObject.height = model.ball.radius * 2

view.scoreObject.text = ("00000" .. model.score):sub(-5,-1)

Again, very minimal changes in one part - we remove the view.ballObject.radius and replaces it, updating the width and height to twice the radius.

Run it and it works. This is downloadable from https://github.com/autismuk/MVP-Intro/tree/master/Part6

The exercise, the last one, is to replace the bat with the bat graphics included in the download. Or use your own :)

In Part 7, the last part, we will replace the control (clicking on the bat) with an action button which does the same thing.

Corona and the Model-View-Presenter pattern (Part 5)

Hi, and welcome to part 5 of my seven part series.

The last exercise was to add a score to the game. We aren't tracking score, at the moment, so that needs two modifications - adding a score to the model (in model.lua, initialisation function)

model.ball = { x = 100,y = 100, dx = 1,dy = 1,speed = 10, radius = 10 }
model.score = 0 

and adding code to display it in view.lua (one modification to the initialise, one to the update)

view.scoreObject = display.newText("<score>",315,5,native.systemFontBold,32) 
view.scoreObject.anchorX,view.scoreObject.anchorY = 1,0

        view.scoreObject.text = ("00000" .. model.score):sub(-5,-1) 

In this part, we are going to add code to move the ball. I have decided to add the ball moving code to the presenter (the bat moving code is in the model).

Now, there are no fixed rules about this - some people believe in a very very dumb model which just has the data only (known as the "Tyra Banks design pattern" ....). Some people think all the code that manipulates and accesses the model should be in the model.

It doesn't matter too much, really, as long as there is reasonable consistency. This isn't very consistent, obviously, but that's because I'm showing what could be done.

So, in presenter.lua we need to add a call so the update method moves the ball.

function presenter.update(model, view)
presenter.model = model

and then we need to create the ball moving method. This is very simplistic, but like the whole series it's designed to showcase concepts, not produce a real game :)

function presenter.moveBall()
local m = presenter.model.ball -- shortcut to model.
if m.dy < display.contentHeight then   -- stop moving if off the bottom
m.x = m.x + m.dx * m.speed -- move it.
m.y = m.y + m.dy * m.speed

if m.x < 0 or m.x > display.contentWidth then -- bounce off left/right sides
m.dx = -m.dx 

if m.y < 0 then -- bounce off top of screen
m.dy = -m.dy

if m.y > presenter.model.bat.y and m.y-m.dy*m.speed <= presenter.model.bat.y then
if math.abs(m.x-presenter.model.bat.x)<presenter.model.bat.width/2+m.radius then
m.dy = -m.dy -- bounce off the bat, score 10
presenter.model.score = presenter.model.score + 10

So it just bounces off the top, left and right, and the bat. You get 10 points for hitting the bat. Once its gone off the bottom it won't move any more.

With all this done, the game should work. Very simple and primitive, but it does work.

Next time we are going to make some simple changes, to show some of the advantages of this structure (hopefully). 

There is no exercise here (really) but if you want to try something, see if you can make the game play upside-down (bat at the top, goes off the top), changing as little as possible in the game :)

The complete code (without the upside down bit) is at https://github.com/autismuk/MVP-Intro/tree/master/Part5

Corona and the Model-View-Presenter pattern (Part 4)

Hi, and welcome to part 4 of my seven part introduction to Model View Presenter.

First up, the answers to the exercises from Part 3.

1) The background in this case is purely part of the view - it is purely decorative. It doesn't interfere with or even affect the game logic, or the model at all. If it was an 'active' background - say there were obstacles to bounce off, it would have some information about those in the model.

2) Changing the bat height (e.g. thickness) is also view only. In some games - say if there was a second bat half way down the screen - it could have a thickness in the model.

This time, we're going to add something to the game - the ball. The games a bit dull at the moment ... okay, it's very dull.

However, it's just going to be a ball - no actual ball movement (that's in part 5). So this involves changes to the model (the ball data) and the view (the ball representation), there is no 'game logic' for the ball (at the moment).

First to the model. The model needs data representing the various bits of the ball. This involves one new line, as we are doing it all in one go, in model.lua

function model.initialise()
model.bat = { x = 160, y = 440, width = 60 } 
model.bat.dx = 0 
model.bat.speed = 5
model.ball = { x = 100,y = 100, dx = 1,dy = 1,speed = 10, radius = 10 }

So our ball has a position, a vector, a speed, and a radius in the model.

Our view (in view.lua) needs to take this information and render it on the display. So we add this to the 'initialise' function, at the end

view.ballObject = display.newCircle(1,1,12) -- create a ball object (part 4)
view.ballObject:setFillColor( 0,1,1 ) -- make it cyan
view.ballObject.strokeWidth = 2
view.ballObject:setStrokeColor( 0,0,0 )

creating a circle with a border representing the ball. This means it is created when the view is initialised (if you were using composer it would create it response to the 'create' event)

In the update routine, it needs to update the relevant parts of the view from the model, i.e.

function view.update(model)
view.batObject.x = model.bat.x -- update the view from the model.
view.batObject.y = model.bat.y
view.batObject.width = model.bat.width

view.ballObject.x = model.ball.x -- update the ball (part 4)
view.ballObject.y = model.ball.y
view.ballObject.radius = model.ball.radius

Running this will give you an app that looks something like this. As with the last time, though I've added something.

The completed code for part 4 is at https://github.com/autismuk/MVP-Intro/tree/master/Part4

That's the exercise for today, add a score to the game.

Q: What do you add to the view and model ? What is the data that represents the score ? What is the visible thing that represents the score ? Where does each go ?

Q: What, if anything, happens in the presenter ?

A note on Physics

One of the problems with developing games using this structure, or similar structures, is that it is often running counter to the way Corona likes things - everything stuck together.

The Physics engine, for example, operates directly on the display objects, the view. This means that - pretty much - you can't use the Physics Engine a great deal.

One of the concepts of MVP, or indeed any system which separates the model and the view, is that you can replace the view with anything you like, pretty much. Suppose you wanted to have a vector representation, or a Retro look or whatever, or even display it on an external LED panel, you can do all these without changing anything other than a view. You could change it to a first perspective 3D game, and again, you'd change nothing other than the view.

Another advantage, especially if you use more Object Orientated programming, is that you can test things in isolation, even without any view at all. Everything isn't glued together.

It's really a case of pays your money and takes your choice :)

Saturday, 26 April 2014

Corona and the Model-View-Presenter pattern (Part 3)

In part 3, we are going to add the ability to control the paddle - it is a very simple control, it is going to reverse direction whenever you 'tap' on the bat. It's not the most exotic control system ever developed, but it shows the point.

In MVP the communication, whatever it is, between the model or view and the controller goes both ways.  So we need the 'view' to be able to send messages back to the 'presenter' - a "the bat's been tapped" message.

To do this, the view needs to know about the presenter, so it knows where to send the message. To do this, we have to make a little change to main.lua to allow the view to have a reference to the presenter.


at the same time, you can add the bit I forgot at the top to hide the status bar :)

display.setStatusBar( display.HiddenStatusBar )

We have added the presenter as a parameter to the initialisation code for the view, so the view will be changed (slightly) to support this. We also need to add an event listener for the bat object, so it picks up taps (note, the parameter list has changed as well as adding the two new lines)

function view.initialise(presenter)
    view.batObject = display.newRect(0,0,10,10)
    view.batObject:setFillColor( 1,1,0 )
    view.presenter = presenter
    view.batObject:addEventListener( "tap",view ) 

This stores the presenter in a variable called 'presenter', and creates a reference to a table listener event, that will call a function view.tap when it is tapped.

This function "sends a message" back to the presenter

function view:tap(e)

It actually does this by calling a method 'tapMessage' rather than using listeners (aka the Observer pattern) - it calls a function in the presenter "tapMessage".

So those are all the changes to the view. It knows about the presenter, and it calls 'tapMessage' when it receives a tap.

Now, we need to make some changes to the presenter.

Another modification is for the update routine in the presenter to store a reference to the model, so it knows where the data is (otherwise the tapMessage function will not know which model to work on).

function presenter.update(model, view)
presenter.model = model

Last, we need to had a 'handler' for that 'message' (in practice, a function) which 'receives' the message and reverses the bat direction.

function presenter.tapMessage()

When the tapMessage is received - in reality, the function is called, it sets the bat direction to the opposite bat direction.

I have mixed up access here, which is perhaps not ideal - in that the bat direction is read directly from the model but it is updated using a function. In good programming practice, you should really use accessors - setters and getters - for all the data, but this does create a lot of functions and can slow things down. It is a personal decision whether to access the model directly or via functions or not.

In the many years I have been programming - I started with one of these - I've seen so much code which was very well engineered but wouldn't actually work effectively on the hardware we had. Saying 'well, we can wait for faster computers' (yes, Gates, this means you) is not a good option.

You could also make a good case that there should be a function model.reverseDirection() which is called to do this. Again, there are no fixed rules - it is just one way of approaching it. This isn't a 'this is how you do it' - it is more 'this is a way of thinking about doing it'.

As long as you are consistent it doesn't matter. Generally, if it is likely to vary significantly - encapsulate it. In this particular case, writing presenter.model.bat.dx = -presenter.model.bat.dx would be okay.

So, it should now, when running, move the bat backwards and forwards, and when you touch it, or click it on the simulator, it should reverse direction.

It should look like this (Corona Simulator) ->

Exercise 1

... except, there'll be no bricks. It'll just be a black background.

I thought I should let you do some code of your own, rather than just following me, so the exercise for this 'lesson' is to put the background in.

Q: Where does it go ? Is it part of the view, model, or data ? Any or all of them ? Does it have associated 'game data' or 'game logic' ?

There is a working version in the repository at https://github.com/autismuk/MVP-Intro/tree/master/Part3 (which also has a brick graphic). Tiling an object in Corona isn't quite as easy as you might think :)

In part 4, I'll add a new view and model item - the ball.

Exercise 2 (Easy)

When running this I actually found it quite hard to click on the bat. How do you make the bat deeper (e.g. fatter vertically !) so it is easier to click ? Should this affect the view, or the model ? or the presenter ?

Corona and the Model-View-Presenter pattern (Part 2)

In this episode (?) we are going to get the bat moving at the bottom of the screen, and introduce the "presenter". The code in part 1 only had a view - the display bits and a model - the data bits, but no code to actually do anything.

This code will live in the presenter.

Please be aware this code is quite deliberately over engineered. In practice you wouldn't break it up quite as much as this - I'm trying to make it clear what is going on. Hence it does look a bit over the top at times, I'm writing far more code than I would normally.

What we want to do is to get the bat moving backwards and forwards on its own at the bottom of the screen. (in the game, we will touch the bat to make it change direction - okay, so Mass Effect III isn't under threat from this game ....)

So we need more things in the model. As well as its current 'data', the bat has a direction and a speed. So open model.lua and add the following code to model.initialise() (I will try to put 'old code' in italics throughout)

function model.initialise()
model.bat = { x = 160, y = 440, width = 60 } -- initialise the bat
model.bat.dx = 0 -- create model.bat.dx, model.bat.speed
model.bat.speed = 5  

We could, of course, just add dx = 0, speed = 5 to the model.bat "initialiser". We have a couple of 'helper functions' as well.

function model.setBatDirection(newdx)
model.bat.dx = newdx

function model.moveBat()
model.bat.x = model.bat.x + model.bat.dx * model.bat.speed -- move the bat.
if model.bat.x > display.contentWidth - model.bat.width/2 then -- check off the right
if model.bat.x < model.bat.width/2 then -- check off the left.

You could put these in the presenter if you wanted - you could really lose 'setBatDirection' altogether and access the model directly. (This will really upset some OO fanatics ....)

There aren't really any fixed rules or anything about this - it depends how comfortable you are with whats going on, and how much you are likely to change the code in the future and where you think code belongs. These two functions are more about updating the model rather than game handling code itself, so they are better off in the model.

We don't change our view at all - it still looks the same. We do change the main program very slightly.

local model = require("model")
local view = require("view")
local presenter = require("presenter")

model.initialise()        -- initialise the model,view and presenter.

Runtime:addEventListener( "enterFrame", -- added code to update the presenter.
function(e) presenter.update(model,view) end)

Firstly we are now getting the presenter module - we didn't have it before, and it is being initialised. I have also removed the line that updates the view, it is the responsibility of the presenter to do this.

The last line causes the the presenter.update(model,view) line to be called on every frame (currently 30 times a second), so the presenter can keep updating the model, handling actions, updating the view etc etc.

Finally, and most importantly, we have the presenter. This is the code that actually "does stuff" and is all-new.

local presenter = { }

function presenter.initialise() -- our initialisation function does nothing.

function presenter.update(model, view)
model.moveBat()          -- move the bat.
view.update(model) -- update the view from the model

return presenter

Again, there is very little code here. We create a 'presenter' module, there is an initialise() function which does absolutely nothing at the moment, and our update routine, which is called at 30fps.

This does two things - it updates the model - moving the bat - and it then updates the view.

This is the big difference from Corona SDK in a nutshell. In Corona you'd just update the x position of the display object and it would move. The moveBat() call would do absolutely nothing to the visual part of the display - it just updates the data representing the display. The view won't update until you tell it to.

Now, you may well ask, what is the point of this ? What do you get by splitting it up ?

It's a bit of a balance - separating it allows you to easily change things without it having lots of knock on effects, and keep track of things more easily.

Against that, it takes more code, and more processor time to do it, and there is some duplication - x,y and width are kept in two separate places, both in the data model and the objects that make up the view.

Often the best approach can be a bit of a hybrid - so if your game as little bits of extras that aren't really part of the game - for example an explosion, then the view could handle this on its own - it's a graphic effect that doesn't effect the game core at all (in most games anyway !) - this sort of thing you learn by trial , error and experience. It makes changing things and fixing bugs easier - we will show this later on when we change a couple of things in the finished game.

Code for this part is at https://github.com/autismuk/MVP-Intro/tree/master/Part2

Note: someone asked me about Composer/StoryBoard (and Director, I guess ?), how does that fit in to this. Well, they are really part presenter and part model, but mostly view. Composer and the others handle different display sets at different times and transition between them.

However, they do have some concept of 'state' - where we are in the game process, and as ever with Corona SDK, the display objects have their own data as well. In a classical pattern, the data (x,y,rotate,xScale and so on) would belong in the model, the state (what current scene) in the presenter, and all the creating / eventing stuff in the view.

In the next episode, we will control the bat.

Thursday, 24 April 2014

Corona and the Model-View-Presenter pattern (Part 1)

This is the first part in a short series on using a Model-View-Presenter (for the rest of the time,MVP) pattern in Corona. I will implement a simple Pong game using "MVP".

What is MVP ?

MVP is a design pattern. It's a tried and tested way of arranging your programs so that they are clear and maintainable.

The basic idea is simple, but counterintuitive for a Corona developer. There are three 'bits', shown in the diagram.

1) The Model - this is the data that represents your game. So in "Pacman" this would be the ghost and pacman locations, the maze layout and so on, and the code that manipulates it and accesses it.

2) The View - this is the display representation, what you actually see on the screen. That's all it does - there is no code for moving things about, no game code there. All it does is represent the model visually.

3) The Presenter - the middle man that does all the work.  Decides when the characters move, where they go, what happens when they collide etc. and updates the model accordingly.

As you can see from the diagram, the model (the data) and the view (the screen) don't actually know about each other. This is good programming practice, but somewhat different to how Corona operates ; Corona mixes the two up, especially when you add the Physics engine in.

You aren't limited to single things. So a game which has (say) a main screen, a HUD showing status information and a Radar could have three different views, one for each bit.

Why bother ?

Many Corona developers will look at this and say, well, what's the point ? Corona, partly because of the way it is designed, encourages big lumps of spaghetti code that does everything. When you write larger programs, or want to maintain a program, it's much easier if you can separate things into workable bits that you can test independently.

Design Patterns usually use Classes and Objects, though in this 'Pong' game I am going to program it without them, because it introduces an extra layer of confusion.

These are just ideas. Read it, try it if you like it. Try variations. There is no 'right' way of doing things.

Try it out

I'm going to create a very simple 'Pong' one player game. It is very basic, and as such looks ridiculously over-engineered :) You could say, well, with the Physics Engine you could write it in half a dozen lines of lua, and you probably could.

So to start with, create a Corona project, with a main.lua file. Then create four separate files - model.lua, view.lua and presenter.lua. These are going to contain the three parts of the program.

main.lua contains this:

local model = require("model") -- pull in the model and the view.
local view = require("view")

model.initialise() -- initialise the model and the view

view.update(model) -- update the view from the model.

This is only a first step - for this first part we have no presenter, only a model and a view.  So it will start and display, but it won't actually do anything.  All this code does is load the model and view modules, initialise them, and update the view.

The only interesting line is the last one, which updates the view using the data in the model. I do this by simply passing the model over so the view can access it. It is considered good programming practice here to use an interface for this - so the view only gets the bits of the model it needs, but lua doesn't really support interfaces.

(This is an example of pragmatic coding. I rely here on the coder (me) not to abuse the fact that the view has the whole model)

model.lua contains this:

local model = { }

function model.initialise()
model.bat = { x = 160, y = 440, width = 60 } -- initialise the bat

return model

The model only has a representation of the bat in it - where it is and how wide it is .We don't care how 'tall' it is, whether it is represented by a rectangle or a sprite, or anything else. The View does that bit. This has the handy advantage that if we choose to change the visual representation of the bat in some way, we only have to change the view. (Later on, I will show this in action, changing the control method without actually changing very much of the code)

view.lua contains this:

local view = { }

function view.initialise()
view.batObject = display.newRect(0,0,10,10) -- create the bat object. 
view.batObject:setFillColor( 1,1,0 )

function view.update(model)
view.batObject.x = model.bat.x -- update the view from the model.
view.batObject.y = model.bat.y
view.batObject.width = model.bat.width

return view

A view is created at the start - it's a yellow rectangle which we just plonk anywhere, we don't care how wide it is. We do care how high it is, mind, so the fourth parameter does matter.

The update function takes the model information above, and transfers it into the various bits of the view.

Run it and you get a yellow bat at the bottom of the screen (it's a rotated Pong).

You can get it from GitHub https://github.com/autismuk/MVP-Intro/tree/master/Part1

Note: this uses the new Graphics system. I'm not sure if everyone has it yet ! If not, then you will need to set your fill colours to 255,255,0 and the batObject will have to have its reference point set to the middle. I think (!)

But .. but .. but .. but ..

Now, at this point, I can guess pretty much what many people are thinking. There's a lot of duplication here, there's actually two sets of everything - x,y, and width in both the model and the bat object, and you are copying lots of stuff about for no real reason. (There are ways of reducing this so it doesn't look quite so bad).

There are advantages though. The two coordinate systems do not have to be the same, for example, though they are here.

Why is this useful ? Well, suppose you release this to the app stores (let's face it, it's better than Flappy Bird !) and you want to put an advert on the top of the screen, but you forgot and there's no space. You can 'fix' it by scaling your vertical coordinates without changing the model or the game code (which is in presenter.lua, currently there's nothing there at all).

So if you replace the view.update() line with

view.batObject.y = model.bat.y / 2

you instantly free up half the screen,but the game works exactly as it did before. The game bit (the presenter and model) still operate in the full screen, but the actual drawing bit only occupies half of it.

Other useful consequences ; well again, think of Flappy Bird (ideally not just after eating). Flappy Bird has a physical world - the screen layout - and a logical one - an infinite line of pipes and a bird. The physical one is a representation of the logical one. If you do it this way, then your model only has to know about the bird and pipes - the scrolling bit is done by the view.

You could even convert it to 3D, and the game and model part would still be the same, just the view would have changed.

Anyhow, that's it for this time. Next time, we'll actually get something moving, and create some presenter code.

(According to my outline notes, there will be seven parts to this)

Transition Manager Class

I've spent some time writing a library that transitions between two scenes (displayGroups in practice) as part of my experiments in MVP - I don't think Composer will do without a lot of faffing about.

It is a little sneaky this as I have used the transition definitions that were in the legacy storyboard system, though the rest of the code is all mine.

Its usage is like this.

local transitions = require("transitions")

local recipient = {}

function recipient:transitionCompleted()
scene1.isVisible = false
print("Done !")


It doesn't actually return a library instance, it returns a singleton object which is an instance of TransmissionManager - it only needs one. This is then instructed to do a 2 second zoomInOut transition between scene1 and scene2 (I have cut this code out that creates it, but it is in the demo).

scene1 and scene2 are two displayGroups, 1000 is the time allowed for each 'phase' of the transition (so if you fade it will take 2000ms in total).  The recipient is an object which receives notification (as you can see in the example) when it is completed. All it does is print("Done !"). You can pass only one scene if you like, either "from" or "to".

It is slightly different ; it returns the scenes unchanged and visible, so if you run the example it will work and if you took the scene1.isVisible line out it would display both of the scenes.

This is a decision I made, basically :) It returns a stable state back.

It won't really be used on its own, it will be used eventually, probably in modified form, as part of my MVP stack, rather than the Components wrapper, which is probably a dead end, too reliant on Corona SDKs slightly strange methodology. There aren't any overlays, yet.


Tuesday, 22 April 2014

Corona Components Module Wrapper

I do like OOP stuff. Sometimes in Corona you feel like you are fighting a war with the API though. But after a fair bit of meddling, I managed to create an OOP library for Composer, that allows you to implement scenes as objects deriving from a subclass. It always struck me with Scenes that there is a lot of duplicate code about.

Unfortunately, there is no current way round Composer's number 1 limitation - I think - (without hacks anyway !) - that the only way you can create a new scene is by requiring a scene file.

A scene looks a bit like this (this is code from the demo)

local oopScene = require("oopscene")

local Scene2 = oopScene:new()

function Scene2:create(event) 
   self.textBox = display.newText( "Hello world Scene 2 !",160,100,native.systemFont,24)

function Scene2:showOnScreen(event)
   self.textBox:addEventListener( "tap", self )

function Scene2:tap(e)
   return true

function Scene2:hideOnScreen(event)

function Scene2:getEffect()
   return "flip"

return Scene2:new():getScene()

This is actually real code from the example files. It isn't really terribly complicated ; all the OOP wrapper really does is to add the event listeners and split the four events up into six methods. So here, we have create as normal, but we have showOnScreen and hideOnScreen which are sub-events of show and hide, which are called both before and after displaying (or destroying). There are also showOffScreen, hideOffScreen and destroy - the advantage of this approach is if you don't implement the methods the events are just ignored. (actually there are default handlers in the base class !)

The rest of the code is fairly straightforward. It creates a text object and inserts it into the view associated with the scene (there's a helper function insertView() which saves a call here). showOnScreen() inserts a listener, hideOnScreen() removes it.

the getEffect() method gets the transition for leaving this scene, though you can provide it as normal (gotoScene() you may note, does not have an options provided - it defaults to getEffect())

The only thing that isn't part of the wrapper is the tap() table listener which calls gotoScene().

The oddity is at the bottom. This is required because this is designed to be called via gotoScene(). gotoScene expects a real scene object to be returned, not this wrapped one. So the last line creates a new instance of class Scene2, then returns its scene, which is not ideal.

To help with these problems, there is a static method getCurrentSceneInstance() which gets the instance of the scene that is currently being delayed.

What else does it do ; well, this is the shorter of two scenes (the demo has two scenes and an overlay), and the other scene has a counter ; there is a system for persisting data (because scenes can be garbage collected by composer) which is used to persist this counter.  Data that does not exist beyond the life of the event can be used in the Scene as a member variable as normal.

There is also another method/event 'frameEvent' which is called on the enterFrame event, this does need to be programatically turned on. It also has another text item (a bit like the one above) except tapping this one opens an overlay rather than switches to another scene.