Category Archives: Rex’s plugins

Slidewalk TMX and C2 system

The Slidewalk system, taken from 2400AD is implemented by using several components in Tiled, and hooking them up in C2.

Tiled/TMX Component

The Slidewalk is comprised of several objects which are the children of an ObjectGroup; the ObjectGroup, in effect, is a single Slidewalk entity.

Components of a Slidewalk

What identifies an existence of Slidewalk is not the ObjectGroup, but the individual Objects which must be named starting with the prefix sw. Each Object component of the Slidewalk contains the name of the Slidewalk it belongs to. The name of the Slidewalk is the name of the ObjectGroup.

A Slidewalk must always have a path. A path is a Tiled polyline. It must be named swpath. The path will later define the waypoints of the Slidewalk. The swpath Object must also contain an attribute called speed. This is the speed value of this Slidewalk.

A Slidewalk must have at least one entry-only point. This point is defined by using an Object (parented under the Slidewalk in question). It must be named swin.

A Slidewalk must have at least one end-only point. This point must be named swend.

A Slidewalk may optionally have an exit point. This exit point may also be a potential entry point. Every point, whether it is an entry, exit, or end point, must begin with sw. Then use the keyword in to indicate this is an entry point, and the keyword out to indicate it is an exit point; these keywords may be used in the same point, such as swinout.

The exit and the end points must also contain a custom property called exitdir. This is a GridMove direction that specifies which direction the player will move towards when deciding the exit, in order to get off the movement effect of the Slidewalk.

C2 Implementation

In C2 the first step is to get the swpathObject to mark the waypoints of the MTiles. In the same procedure, they are marked with the name of the Slidewalk they belong to. The waypoint sequence index is internally based on how Tiled writes it, which is sequential anyway, so the C2 loop iterator does this conveniently.

As the MTiles are being tagged as waypoints, they are being added into the InstGroup with a group named after the Slidewalk’s name, which uniquely identifies these series of MTiles for SLG and GridMove. They are added in the order they are looped, so the sequence is still preserved at this point.

MTiles are then further marked with their entry/exit/end values, as well as the custom properties as inputted in Tiled.

When the player moves on top of the Slidewalk, the On GridMove reach target trigger will check whether the player is on an entry point. If it is, a function called CreatePathFromIG is called whereby it takes the Slidewalk InstGroup waypoints and transfers those waypoints to the player’s own InstGroup moving path. CreatePathFromIG does something more, though: it considers the possibility of multiple entry points; it finds the MTile that the player has entered from, and starts retrieving MTile waypoints from that point until the end.

 

 

MTile/GTile edge definition syntax

Though this syntax was developed many months ago, I had forgotten to document this.

Overview

This edge definition refers to the edges that exists in any GTile tile/sprite. This obviously means that edge definition is a Tiled property of the GTile, which is then passed on in C2 to generate MTile edges at runtime.

Note that a GTile refers to a graphics tile which is a bigger-sized tile. An MTile refers to a movement tile which is smaller and forms the grid of possible tiles to move to. Every GTile is subdivided into equal square MTiles.

Edge definition

Below is an image representing one GTile, and the MTile IDs within it:

For every GTile, there can exist any number of edges. Using this image as an example:

That sprite is mapped out as:

Here, we introduce the edge syntax used to define those edges.

<mtile_id>:<gridmove_direction>[,<mtile_id>:<gridmove_direction>...]

Where mtile_id is the id of the MTile that needs an edge definition (because it is neighbouring a GTile edge), and gridmove_direction is the direction where the edge lies on that MTile specified in the GridMove format.

In the sprite above, the edge definition is:

1:0,5:0,8:3,9:3,10:3,11:3

That is, the MTile ID 1 and 5 have a edges at Direction 0 (east), IDs 8, 9, 10, and 11 have edge at Direction 3 (north). Note that it’s possible to have defined other MTiles, such as:

6:1,7:1

…to replace 10:3, 11:3. It doesn’t matter, as long as the same edge is not specified twice.

Tiled

Note that the edge definition is inputted in Tiled in its tilesheet editor. Currently, there are two Tile properties associated with edge: edge and edge4. edge was the original use, which subdivided each GTile into 2×2 MTiles. edge4 subdivided it further (4×4) giving it a maximum of 16 MTiles per GTile (this depicted in the image above).

In the C2 project, only one type is used and can be interchanged or modified depending on final design choices.

Workflow: Diagonal movement across edges

The basic problem is that when moving diagonally, the edges are ignored because edges only exist at the sides of the square, and not at the corners.

Moving across diagonally travels across the corner of the logical square, which do not have any edges.

The solution to this was to query both the start tile (slg.PreTileUID) and the target tile (slg.TileUID) and from there, determine the GridMove/Board direction index that the current path was taking.

Using the principal direction (let’s assume it was a diagonal value of 6 (as demonstrated in the above image), the two other directions flanking the main one was also queried.

In the above image, the movement from the start tile is at direction 6. Direction 2 and 3 were derived from this by a lookup that defined two flanking direction for any given direction.

Then the target tile was also queried, but this time, the principal direction was the reverse of the start tile (computing for the reverse tile was made simpler by using a lookup, although a simple modulus would have done it, too). In other words, 4 is the direction. Using the same lookup, 0 and 1 were looked up as the flanking reverse directions.

Then a check is made against existing edges: are there any edges on the 2 and 3 directions of the start tile? If so, movement cost is BLOCKING. If not, then check the target tile’s 0 and 1 direction edges if they exist. And if they do block the movement.

The method used in determining the principal direction was simply comparing the logical X and Y positions of the start and end tiles.

 

Gotcha: SLG, moving across edge issue

There is a gotcha with using SLG movement and edges which relates to cache cost (a parameter in SLG movement).

The image above describes how a movement from tile [1,13] to [1,12] should yield a path that comes around. However if the cache cost parameter is set to Yes then it will refuse to yield a path at all.

Setting it to No will solve the issue.

Not sure what this is about, but noting it down for future reference.

Workflow: Z-Sorting by Y

In the RND test project, I ran into performance issues with ZSorter due to the high number of instances I was using. I’ve easily re-implemented the C2 ‘native’ action of z sorting and the performance is much better.

However there is a gotcha.

The native version, despite what seems to be a vague reference of layering behaviour in the manual (search for Sort Z ordermoves instances which might have been in other layers to layer that had been sorted last. If the sorting is done per tick, there is no way to force a particular instance to another layer and expect that to render accordingly. (For example, I created a ghost clone over my main sprite. The ghost sits on a top layer, but is an instance of that sprite. Because the ghost belongs to the same sort family in which I base the sort, the ghost also gets sorted. And because the action moves the ghost into the same layer as the other instances, it loses its layer blending purpose.

The solution looks more like a hack: in cases like the above, where instances need to be separated, the sort reference variable (the variable that the action uses as a basis for a sort), should be overridden. In the case above, the ghost instance’s sort reference variable had an extremely large value so that the sort engine will always have to place it on top.

Workflow: Using AnimationLoader, dynamic tilesets

As usual, Rex has done it again. Or rather, he’d already done it.

The look development of the game is what I’ve always been concerned about. I’m creating tiles, binning them, replacing them. The tile management in Tiled is a bit too simple for my needs.

First came the issue of volatile tile IDs. Then the inability of re-arranging the tiles. So what I’ve done now is to offload all the actual organisation of data to 3d and C2 workflows. What this means is that I no longer have to use tile IDs to reference.

This is an outline of what I’m thinking of designing (and have implemented it for the most part at the end).

Renders

3d renders, or wherever the tile renders may come from are rendered directly to the C2 folder’s /Files directory. They have the following name convention.

_<tileset_name>_<tileset_id>.png

This collapses the tilset name and tile id into one name. These are then put into a flat hierarchy in the /Files directory.

C2 animation name

Back in C2, I create a single tile sprite. The content of this sprite can be empty because the idea here is that the image will be dynamically loaded in.

This is the gtiles sprite (GBoard tiles), and contains (I think) all of the tilesets used in the game. Now the tilesets are stored in C2’s animation blocks. So in the gtiles sprite, I create all the tileset names that should exist:

proto_gnd
proto_walls
proto_canal
..etc

This refers to the naming convention of the .png described above.

C2 animation frames

After the animation blocks/tilesets have been created, then I note down how many frames each tileset should have. I create the exact number of frames for them though the frames are blank. If I don’t I will get errors in the Browser console, but it won’t stop the execution of the game.

AnimationLoader

Rex’s AnimationLoader handles the loading of the renders in the /Files folder by going through every animation block in the gtiles sprite, and then through every frame that it has. I’ve written events that concats it properly, adds the underscores to conform with the convention, and it loads the images into those frames.

Tiled

This is the important bit.

In Tiled, when I load in the tiles into the tileset, I must reference the png files in the /Files folder, because when the TMX is read, the image source from the tile is also read. This image source TMX attribute is the string that is parsed in order to determine which tileset this belongs to, and what the tile ID is. So, in effect, by the naming convention alone, regardless of which tileset the tiles belong to in Tiled, the fact that the file contains the tileset and id, that’s enough to make a connection to C2. In this way, it doesn’t matter how I arrange my tiles in Tiled because I only use the name of the png for the correspondence.

Collisions

Collisions are often defined in the C2 image editor, and the information is stored in the caproj. However, when dynamically loading sprites, the corresponding collisions must also be dynamically loaded.

I’m currently implementing a solution to this which involves writing out .col files (collision files) that is partnered with their respective images. This much in the same vein as .imagepoint files.

Collision polygons are defined in Photoshop using paths. A ‘Work Path’ must be created, and a jsx to save that path is run. This outputs a .col file alongside the .png file that is being edited.

Back in C2, I’m currently modifying Rex’s AnimationLoader to take in this .col file implicitly, by reading the file, and then injecting the information directly into the poly_pnts array of the Sprite object.