Tag Archives: orthoxy2lxy

Workflow: Setting up the Board for movement

Synopsis

This post is going to try to be the most definitive guide to setting up the Board system from scratch for the purposes of movement. This means it will hit the following requirements.

Requirements

  1. Maps using Tiled/TMX via TMX Importer V2
  2. Board setup based on TMX reading
  3. SLG Movement for pathfinding

Mandatory objects

  1. AJAX, for reading TMX files
  2. Board
  3. Browser, for logging
  4. Function2M (or any function plugin)
  5. Keyboard, escaping movement
  6. InstanceGroup, for storing path information from SLG
  7. SLG Movement, for tile pathfinding
  8. SquareTX
  9. TMX Importer V2
  10. TMX-XML Parser

Mandatory behaviours

  1. GridMove, for the movers on the grid.

Setup

tmx_1

  1. AJAX calls the tmx to be loaded, which gets it as a string.Then the trigger AJAX:On completed is called.
  2. SquareTx’s position offset is set to (16,16); ie the position offset is the physical coordinates of LogicXY (0,0). In an orthographic setup (which this event sheet screencap is based), the value of 16 refers to the offset so that the center of the tile would be moved inside the layout, and the top-left corner of the upper-left most tile will be aligned squarely at the layout’s (0,0) coordinates.
    1. In an isometric position the map height plays a part. 
  3. SLG is configured to use the Board as its Board, and an InstanceGroup (ig).
  4. The mover’s GridMove behaviour is configured to use a particular InstanceGroup for its data.
  5. Make sure that any object that is to be instantiated using the TMX process is destroyed. This makes sure that during the instantiation the proper object is being referenced.
  6. When AJAX completes reading of the tmx, it will trigger its On completed event.
  7. Use the TMX Importer V2 (tmx) to import AJAX.LastData using the XML parser. This populates the tmx object.
  8. Then set the SquareTX’s cell width and height to correspond with the tmx.
  9. Set the Board’s width and height (logical entries) to correspond to the size of the map in the tmx.
  10. Then initiate the tile retrieval.

TMX Data Retrieval

I’ve not yet documented the timings of Objects vs Tile retrieval, so I’m not making any dependence on timings.

Tiles are retrieved first before Objects.

Movable areas

When creating areas for movement, I prefer to create a Tiled layer for movable areas and leave tiles blank where it’s not possible to move on, rather than tagging tiles impassable, so I don’t need to check this during the cost function.

On each tile cell

  1. Use Board:Create tile to instantiate the tiles and place them on the board.
    1. Board: Add chess could also be used, but this is more confusing because it only places a logical ‘marker’, and does not instantiate it.
  2. Configure the frame of the tile (ie id of the tileset).
    1. Note that some it’s not always the case that you can configure the tiles after they’ve been created, for example, re-positioning tiles after instantiation didn’t seem to be reliable or possible. But it seems that instance variables are ok.
    2. TMX tile properties are set as necessary

On each object

Same sort of thing as On each tile cell.

  1. Create using Board:Create chess.
  2. Note: pay attention to the Board z-index as well as the C2 layer.
  3. Configure non-Board related stuff as needed.
  4. Note OXY2LXY, which is the ‘Orthogonal to Logical coordinate function’. To repeat a past post, the TMX Object’s position is recorded in orthogonal coordinates, and the Board or SquareTX object have no convenience features to translate those values to isometric. The OXY2LXY function is this translation:

This completes the TMX retrieval.


Initiating the move

The first thing to consider is the first call to move.

In this case it is a LMB on a tile.

  • Note ig:Clean group. This removes all previous path entries (in this case it is “path” referring to the waypoint nodes)
  • The main command is: slg: Get moving path start from <mover> to tile/chess <tile> with moving points to slg.INFINITY and cost to <cost_function> then put result to group <instance_group_name>
    • <mover> refers to the chess that is already on the Board.
    • <tile> refers to the tiles on Z=0 on the Board, which is the basis for moving.
      • Presumably (haven’t checked), if <tile> is at a specific Z index on the Board, then SLG will consider that Z index and pathfind on that level only. But what if the <tile> is at Z=2, for example?
    • <cost_function> is the cost function of SLG which determines the resulting path. We can also call this a path function.
    • <instance_group_name> is the group name inside the InstanceGroup object which stores the UIDs of the pathfinding nodes.
  • Then on the condition that the GridMove is not moving the mover, we pop the first waypoint, which is the first waypointand this is SOL’d as the tile object.
  • With this SOL, direct GridMove to move to that tile.
  • This the initial movement phase.

Cost function (moving path function)

Before dealing with the continuation of the move, we must define the cost function of SLG on the mouse click.

The cost function, also called moving path function, is called by SLG when a moving path is required.

  • The basic definition of a cost function to make set the the return cost to 1.
  • Returning a cost of SLG.BLOCKING will make this tile impassable
  • Use slg.TileUID as the reference to the tile being queried for pathfinding.
  • If the map was generated with blank areas, then there’s no need to check against those, as they won’t be even be considered for pathfinding.

Continuing the move

Once the move has been initiated, then continue to move as long as there are nodes in the InstanceGroup for paths.

  • The continuation of the move is on the GridMove:On reach target trigger, which is triggered when GridMove moves on top of each tile as stored in the InstanceGroup.
  • Use the condition InstanceGroup:Pop one instance <tiles> from group <instance_group_name> in order to determine if it has popped the last one. If it has then GridMove is bypassed.

Movable area function

The movable area function may or may not be used in Citizen 2401, but this is a good time to document this function.

SLG has 2 ‘cost’ functions. One is the movable path, and the other is the movable area.

Focus on the SLG call.

Just like movable path, movable area’s search pattern is to move out from a logical coordinate.

What the events above are trying to do is to generate a list of tiles which the AI can move to that are not LOS’d by the player. The LOS of the tile is determined by another function which switches the los instance variable accordingly, so that only this variable is checked.

The movable area cost function itself (‘p evade area’) does not check for the LOS state, and the reason is described here. Simply put, because of the movable area search ‘creep’ may get blocked by LOS’d tiles, only the tiles are tested on a distance basis (ie movement cost as defined in the SLG:Get moving area)

Then, a filter function is applied on top of the results of the movable area function in order to get rid of those tiles that is LOS’d.


Stopping, changing paths

To stop, simply clear the InstanceGroup path group. This will give GridMove no waypoints to go when GridMove:On reach target is triggered.

When trying to LMB on a tile while still moving, simply clean the Instance path group.

This has the effect of generating a new path while making sure the GridMove still goes to the last assigned waypoint.

Advertisements

Workflow: Tiled to C2 – TMX to Board

Creating tiles on the Board from TMX Importer

Basic stuff. The main idea is to create the Tiles as the TMX is being imported. Rex explains it in the Scirra forums.

Rex’s example for putting tiles into Board based on TMX data.

Layering up Tiles and Chess

Tiles are the elements used for determining the Board’s logical positions. Chess, on the other hand, are elements above the Tiles. As mentioned in Rex’s docs, Tiles are those residing in Z=0, while Chess are those that reside in Z>0. The distinction cropped up because I wanted to generate Edges based on a custom property of a tile.

But what was happening was this: I was generating Tiles on the board using Board’s Action:Create tile. This not only instantiates the Sprite, but registers its presence as a Tile on Board. Now, TMX Importer was generating other Tiles from other layers, so it was overwriting some of the Tiles that I had just placed. When it came to query the Tiles in order to determine where an Edge should appear, it was always referring to the latest Tile that was put in that logical position, and some of those Tiles had no edge requirement.

The solution was to put the TMX tiles as Chess entities on top of each other on the Board in order to distinguish them from one another. It was a matter, then, to decide which TMX tiles would be Board Tiles, and which would be Chess. I decided that the base Floor layer — for now, at any rate — should be the Tiles, and every other TMX tile would be Chess.

As part of my solution, I thought it would be intuitive if the Board’s z-ordering matches the layer ordering in Tiled (rather than C2’s layers matching tiled, since C2’s layers have a different function from Tiled layers anyway). So what I did was to initialise a C2 Dictionary to contain the TMX layer names along with their layer indices (eg tmx_layer[“Floor”] = 0). Then when a Tile is being generated, it knowing what TMX layer it belongs to (eg tmx.LayerName), that is looked up against the Dictionary, and placed in the appropriate z-axis.

So Tiles can be some generic Sprite. But to make it more efficient, the Tiles should represent all ground areas, even those not necessarily impassible, but at the least the areas which are of programmatic interest.

Tiled’s Objects and Board’s Logical Positions

Using Rex's Function2M to convert Tiled orthogonal-type Object parameters to Logical positions.
Using Rex’s Function2M to convert Tiled orthogonal-type Object parameters to Logical positions.

Here are some tricky bits regarding TMX’s Objects (note that Objects, capitalised, pertains to the Object entity in the Tiled prog). In the first place, querying a TMX’s Object’s ObjectX/Y parameters inside Condition:On each object, will give you the coordinates as it is written down in the TMX file (observable in the Tiled Editor). The problem with this is that that the coordinates is not a Logical position, nor is it a screen-space (aka ‘Physical position’) position. It is actually a position in orthogonal-space!

There are no Expressions in Rex’s Board plugin that computes this, because the computation is dependent on the projection, which is reasonable. Instead of maintaining another Board for a trivial lookup, I just implemented my own using functions.

There are some interesting points here. First, in Tiled Objects, the X/Y values use pixel values, and they use the Map’s Tile Height as the normalising factor. Because this is an isometric map, the width and height of the tiles are not the same and because the tiles have indeed been transformed, you have to ask what is the resulting pixel X/Y value that defines the end of one tile and the beginning of another. It turns out that the tiles use the tile height:

tiled_object_layer_positioning1
At (0,0), the ‘upper-left’ corner of the object is aligned with the top-left corner of the grid.
tiled_object_layer_positioning2
With the grid/tile width/height to be 256/149, moving the Object X=+149 pixels brings it up neatly to just the end of that tile. At +150 pixels, it will lie on the adjacent tile.