Creating the interface

While many game designers consider games that require no visible interface to be the platonic ideal of their craft, nearly every game requires some sort of extra-diegetic interface element, something that provides information about the game world and accepts commands into the game world, but is not itself part of the game world. To show the player how well they're doing, we'll add a layer above the game world, with a number showing the player's current score, and increment it whenever their score changes.

Getting on with it

First, we're going to prepare the interface module. This code will live in a separate file to make it easy to maintain without changing the game code, and vice versa, once the initial connection is made. The interface module will be a function that takes a game object and returns the group containing the various interface elements. Start by creating a new text file in the project source directory named interface.lua and framing in the outline of the function:

return function(game)
  local self = display.newGroup()

  return self
end

This gives us the groundwork for a function that returns a new group. Now we can start filling in the body with elements for the group (or one element, in this case).

Adding visible information

First we create the text object that will display the score:

  local self = display.newGroup()
  self.ScoreDisplay = display.newText(self, "000", 20, 10, native.systemFont, 24)
  self.ScoreDisplay:setReferencePoint(display.CenterRightDisplayPoint)

Setting the reference point doesn't actually move anything on the screen, but it does change which point in the text is considered by Corona to be the x and y coordinates of the object. This will make it easier to keep the text aligned as we update it later.

Updating an information display

We want the score display to change when things happen in the game, so it will need to listen for the relevant events:

  self.ScoreDisplay:setReferencePoint(display.CenterRightDisplayPoint)
  function self.ScoreDisplay:Score(event)

Because the score display object will be used as a listener that responds whenever the game's score changes, it will need a function field that responds to Score events.

  function self.ScoreDisplay:Score(event)
    local x, y = self.x, self.y

Because Corona text objects are not naturally aligned, some juggling is needed whenever one might change its text contents and therefore its size. The x and y values we record here are the ones where we always want the center of the text's right edge to appear.

    local x, y = self.x, self.y
    self.text = string.format("%03d", event.total)

The format call ensures that the displayed score is always three digits long, with leading zeroes as needed. This keeps the score looking consistent.

    self.text = string.format("%03d", event.total)
    self:setReferencePoint(display.CenterRightDisplayPoint)
    self.x, self.y = x, y
  end

This ensures that we will be placing the text at its new center-right anchor. Also, it sets that point back to the originally recorded coordinates. Now that we've concluded the function to respond to score changes, we need to register that we're interested in hearing about the scores, before we return the new interface to the game creating it:

  end
  game:addEventListener('Score', self.ScoreDisplay)
  return self
end

Linking the interface to the game

Save the file and close it. We now have a functional interface layer, but it isn't yet being created or used. Open the game.lua file and locate the scene:createScene function block. After the lines that create and insert the world group, add similar lines for the interface module:

  group:insert(self.World)
  self.Interface = require "interface" (self)
  group:insert(self.Interface)

  self.World:addEventListener('Death', self)

We load the interface creator function, and call it, passing it a reference to the game it will listen to for Score events. We then insert the interface into the game scene's display group at a higher layer than the world layer, so that it rides on top (otherwise, the world background would hide the interface).

Triggering a game event from a world event

However, although the interface layer is now being created and displayed, and it's listening for Score events on the game, these events are not being published yet. Near the top of the open game.lua file, above the scene:Despawn function, add a function, similar in form to the despawn function, to handle death events and modify the game's score accordingly.

local scene = storyboard.newScene()

function scene:Death(event)

The first thing we will need to do when we detect that a creature has died in the world (rather than just leaving the world borders and despawning) is increment the game score:

function scene:Death(event)
  self.ScoreTotal = self.ScoreTotal + 10

Then, after changing the actual score, we need to broadcast that the score has changed. This will finish the Death response handler:

  self.ScoreTotal = self.ScoreTotal + 10
  self:dispatchEvent{name = 'Score'; total = self.ScoreTotal}
end

function scene:Despawn(event)

Tip

Lua is a language that's light on semi-colons, making their use between statements optional in almost all cases. In this case, table constructors allow items being added to the new table to be separated with either commas (most of the time) or semi-colons (only occasionally). I like to use them to separate table elements into groups; in this case, I use one to separate the event name (which all events have) from the other parameters (which are particular to each specific event).

Now, the score should increment as you successfully tap the bat flying over (if you want to test it more thoroughly, try changing the game scene's StartingCount field from 1 to 5). The last precaution we need to take is to reset the score properly if the scene is reused for a new game without being unloaded first, by adding an event inside the scene's enterScene response:

function scene:enterScene( event )
  self.ScoreTotal = 0
  self:dispatchEvent{name = 'Score'; total = self.ScoreTotal}
  self.StartingCount = 5

What did we do?

We created a display to show the game score, and set it to update automatically as the game score increases. We made the display aware of the specific game whose score it will display, by passing that game to the interface constructor. We also modified the game to actually produce these events, so that the interface will have some updates to process.

What else do I need to know?

The world object doesn't register itself with the game object for events; it lets the game object do that for it and only provides the response. Why do we instead give the game object to the interface layer and let it register itself?

The main reason is that the world is primarily a source of events for the game to listen to, whereas the interface is primarily interested in events that the game object generates. In a more complex game, it would be very difficult for the game object to anticipate every possible event that the interface might need to display, and every new event the interface wanted to handle would require modifying the game module as well to register it. This approach lets the interface call the shots as far as registration, whereas the game can reasonably assume that it will drive most communication that goes from the game into the world.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.182.179