Building physics for the map

In order to use physics to drive our game interactions, we'll need some physics content for our game world to give characters some floors (and walls) to interact with.

Getting ready

Open the file categories.lua and locate the table being returned (currently empty).

Getting on with it

We'll create a long physics block for every unbroken row of block tiles.

Defining the floor category

In categories.lua, add a new entry to the table being returned:

return {
  ground = {
    groupIndex = ID.general;
    categoryBits = ID.wall,
    maskBits = ID.sprite;
  },
}

Save this file, open world.lua, and load this module at the top of the file.

local categories = require "categories"

local function matchPointPlacement(target, referenceX, referenceY, frame, targetX, targetY)
Since the module is working with collisions, it will also need physics.
Local physics = require "physics" 
local categories = require "categories"

Clearing the map

Open the file world.lua and locate the self:Load(map) function. After looping over the map, make sure that in case the world object is being reused, any previous platform objects are cleared:

    end
    if self.Blocks then
      for _, block in ipairs(self.Blocks) do
        display.remove(block)
      end
    end
    self.Map = map

Then, make sure that the world starts with its list of solids being newly empty:

        display.remove(block)
      end
    end
    self.Blocks = {}
    self.Map = map

Scanning the rows and identifying block runs

Look over each row of the map at a time:

    self.Blocks = {}
    for h, row in ipairs(map) do
    end
    self.Map = map

Then, for each row, gather the position, ground style, and width of each run of blocks using a custom iterator (which we'll write next):

    for h, row in ipairs(map) do
      for start, kind, width in scanRow(row) do
      end
    end

Before using these dimensions to create the blocks, we'll back out for a moment to create the iterator that will treat the map row as a sequence of blocks. Move back up the file before the module function and frame in the form of an iterator that works with numeric indices:

  until dY == -1
end

local function advance(row, start)
end
local function scanRow(row)
  return advance, row, 0
end

return function (terrain, columns, rows)

When the generator comes back to a row at the start or middle of a run of ground blocks, it scans to the end of that run before proceeding:

local function advance(row, start)
  while row[start] and row[start].isGround do
    start = start + 1
  end
end

It then advances until it finds a block that contains solid ground:

    start = start + 1
  end
  repeat
    start = start + 1
  until row[start].isGround
end

If it reaches the end of the row first, scanning is complete and it ends the loop by returning nil:

  repeat
    start = start + 1
    if not row[start] then
      return
    end
  until row[start].isGround

Once it has the beginning of a block run, it scans forward on a new counter until it finds the end of that run or the row:

  until row[start].isGround
  local stop = start + 1
  while row[stop] and row[stop].isGround do
    stop = stop + 1
  end
end

Finally, it returns the start position as well as the function used to create the blocks in the right size and style, along with the width, obtained by subtracting the beginning from the end:

    stop = stop + 1
  end
  return start, row[start].isGround, stop - start
end

We here take advantage of a feature of Lua, where any non-nil value is considered true. So we can store functions with ground spaces that create the right sort of ground, and nil on those spaces that don't count as ground.

Creating the blocks

Back inside the loop, it's time to take the information from the iterator and make blocks with it. Multiply the index of the current row as well as the start and width of the block by the world's tile dimensions to determine the dimensions for the new floor:

      for start, kind, width in scanRow(row) do
        local block = kind(self, (start - 1) * self.HSize, (h - 1) * self.VSize, width * self.HSize, self.VSize)

And finally we store the block in the list we created earlier:

        physics.addBody(block, 'static', {filter = categories.ground})
        table.insert(self.Blocks, block)
      end

What did we do?

We picked out individual streaks of consecutive blocks in each row of the tile map, and gave each one a physics identity, allowing it to block the passage of solid characters. We gave these blocks a generic grouping so that they will collide with nearly all other objects.

What else do I need to know?

There are two reasons to consolidate adjacent blocks rather than give each block tile its very own physics square:

  • Box2D performs significantly faster when you give it fewer objects to handle, and the smaller number of blocks uses less memory as well
  • Sometimes when another object scrapes along a series of stacked blocks, Box2D gets confused and makes it stop at one of the boundaries, like a person tripping over the seam in a sidewalk

Also, the iterator we created to scan for blocks is a bit inefficient, but fixing it (by returning the ends of blocks instead of their beginnings) makes the code's usage harder to understand and isn't worth it unless it has a significant impact on load times.

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

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