Workflow: Multiple Tilesets

Dealing with multiple tilesets

C2 Sprite are used as tilesets in conjunction with TMX Importer. One Tile Sprite is considered a single tileset.

This the solution I’ve come up so far.

Use of Family Object and Nickname

Tilesets are grouped into a Family called ts_f (stands for tileset family). They have the instance variables that mimick the previous ’tile’ instance.

tileset_family_variables2

The addition is the Nickname plugin and its corresponding behaviour. These are essential to both instantiate and pick the correct instance of the tileset.

It’s worth nothing that I have tried native C2 methods of generating and picking instances into the SOL. However, what I have observed is that while I am able to instantiate specific Family members (by compare-picking Family instance variables), these newly created Family members were not being picked up properly into the SOL, and thus configuring these new instances were not possible.

Nickname, however, is able to generate instances and then place into the Family again. But unlike C2’s own methods, Nickname somehow allows me to reference the newly-created object by accessing the Family.

This might be a worthwhile bug report.

Because of Nickname I was able to retain all my events save several modifications as to the placement of the Tile/Chess objects. Instead of using Action:Create chess/tile, I had to use the System’s Create object to generate the Sprite instance, position it using the Board’s convenience positioning expressions (eg Board.PXY2LXY), and then use Board’s Action:Add chess/tile, which adds the entity into the Board without additionally creating an instance of it.

In summary, I am loading in tilesets manually in C2 and assigning them the name that corresponds with the ones used in Tiled. When TMX is being loaded, during Tile Placement, the tmx.TileSetname is queried and stored. This is the same being used by Nickname to instantiate that tileset/spritesheet.

Just in case it doesn’t immediately become apparent to my future self, the reason why ts_f even exists is simply to conform all tileset’s instance variables. All tilesets are put into the family to inherit the variables.

 

Advertisements

Gotchas: Losing ‘scope’ while calling function

losing_scope_function1

The above shows a certain situation where function calls are not aware of certain changes made by other functions.

In the picture above, I call “TanimChange by uid”. Part of the events look like this:

losing_scope_function2

Note that I create an instance using Nickname. All ts_f set variable directives work fine.

At the end of “TanimChange by uid” I return the uid of the newly created ts_f.

Back to the top image: when “TcoChange repost tile (ts_f.UID)” is called, it’s actually a very simple function:

losing_scope_function3

All I do here is get reference the UID argument and search ts_f for the UID. Unfortunately, it doesn’t see the ts_f.

In fact, when I bypass the check and let the For each ts_f loop run, it doesn’t register the newly-created ts_f instance.

I consider this a bug, and would try to get report going and try to replicate this without using 3rd-party plugins.

Workflow: Changing Tiles

Overview and purpose

I’ve worked out a method of changing tiles at run-time using the same kind of Object ‘tagging’ system I’ve outlined in previous posts (eg Storage) using the same reasoning: the need to identify entities uniquely.

The purpose of changing tiles is to indicate a state of a Tile/Chess, and visually represent the change.

For example, a door’s opened or closed state, or a floor’s damaged or otherwise modified state.

Implementation

tco_explainer

Three elements are needed for this implementation that I call Tile Change Object (TCO).

  1. TCO Object (defined in the TMX)
  2. TCO Sprite (created in C2 through TMX loading)
  3. TCO Dictionary (created in C2 in conjunction with TCO Sprite)
  4. Tiles ‘vars’ property
A TCO Object in the TMX is defined by a 'tco' type. Additionally, it needs a custom property called 'layers' to define the extent of its control over which Tile is on which Layer.
A TCO Object in the TMX is defined by a ‘tco’ type. Additionally, it needs a custom property called ‘layers’ to define the extent of its control over which Tile is on which Layer.

TCO Object

The TCO Object defined in the TMX is any Object with type ‘tco’. It requires a custom property called ‘layers’ which is a comma-separated string defining a list of Layers that is meant to be changed. by this TCO. For example:

layers=Walls,Floor

Again, the TCO Object only marks the tile as changeable. The configuration of how it changes is defined in the Tile itself.

TCO Sprite

The TCO Sprite is a Chess object that resides in its own z-layer in the Board (“Tco”). The Sprite is the effective marker of the TCO in the Board.

TCO Dictionary (TcoDict)

When a TCO Sprite is created on the Board, it creates a TCO Dictionary (TcoDict). This TcoDict’s keys is thus added using the TCO Object’s ‘layers’ property values:

TcoDict["Floor"] = ""
TcoDict["Walls"] = ""

Note that the value is empty, and this means it is in its nominal state.

Tiles ‘vars’ Property

The 'vars' property define the possible states that the Tile could be in, and the Tile ID to use if in that state. A value of -1 means to use the 'self' Tile ID, and implicitly means that the nominal state of the Tile is as defined.
The ‘vars’ property define the possible states that the Tile could be in, and the Tile ID to use if in that state. A value of -1 means to use the ‘self’ Tile ID, and implicitly means that the nominal state of the Tile is as defined.

(Edit: the ‘vars’ property syntax is always in a state of flux so this part has been edited a few times)

The Tiles in the TMX which are expected to change need a property called ‘vars’. This ‘vars’ property define a state, and the Tile ID it’s supposed to use when the Tile and Layer is switched to that state. Example of a ‘vars’ value:

vars=open:42,closed*:-1,broken:5,burning:a,burninglow:a:2

The ‘vars’ line is tokenised by commas ‘,’, which separate it into what we’ll call ‘subvars’

open:42
closed*:-1
broken:5
burning:a
burninglow:a:2

Then it is further tokenised by colon ‘:’.

The first token is the possible state that this Tile could be in. If that state has a *, it is considered to be the default state of the Tile. This is important in the initial creation of the map, to initialise the state of each Tile.

The second token is the Tile ID (of the same Tilesheet) that represents that state. If -1 is specified, it means to use the original Tile ID that the Tile originally began with.

If ‘a’ is specified in the second token, then that means that an animated Sprite is intended.

If ‘a’ was specified in the second token, then the third token can also be specified to denote to use another Tile’s animated Sprite.

For reference this is the syntax template for a subvar:

<state>[*]:<Tile_ID>|a[:<Alt_Tile_ID>]

Refer to the Animated Tiles post on how this is used.

Workflow: Persistent data using a custom Storage entity

Storage

As a continuation from the post Workflow: Room loading with TMX, I’ve implemented one persistent data type called Storage to be able to demonstrate a workflow that will keep data between TMX loads.

Storage is an entity placed in Tiled (an Object). It is named s#. When the TMX is read, it instantiates a ‘storage’ Sprite onto the Board on its own ‘Storage’ z-layer.

It is then initialised: the Tiled property ‘name’ is copied to the C2 instance, and the room (my term for the currently processing TMX file) is also populated thus. (Unrelated, there is also a property called ‘content’ which is the initialising value for the Storage’s contents.)

After the Storage Sprite is initialised, it is effectively a TMX representation in C2. Now, I associate a Dictionary (named ‘StorageDict’) with this Storage Sprite. StorageDict has a ‘name’ and ‘room’ instance variable which are the two mandatory associations to match itself up with the Storage Sprite.

When a Storage Sprite is initialised it searches for its StorageDict partner using ‘name’ and ‘room’ instance variables. If it find it, then nothing needs to happen further — just making sure that there is actually a container for that storage. If it doesn’t find it, then it makes one. The only connection between a Storage Sprite and StorageDict is the ‘name’ and ‘room’ variables/properties.

When the TMX is unloaded and another reloaded, the StorageDict remain in memory. Loading and saving C2 will also keep the data. In other words, the association is ‘soft’.

EDIT: I’ve decided to directly relate the storage Sprite by adding an instance variable. This connection is always made every scene load, but it does save me from having to re-evaluate which StorageDict a storage Sprite is referring to.

Where to from here?

This demonstrate the use of a Dictionary to maintain C2 instance data. It is in itself the data and up this point there is no requirement to visually represent changes (eg tile id change based on a new value).

But I suppose this is where the next challenge is: changes in tile usage depending on saved data.

Workflow: Room loading with TMX

InstGroup

The call to load another TMX is easy because I’ve designed the events to be generic. However, all C2 instances generated by the TMX, which have been placed on the board, need to be deleted before another one could take its place.

I’ve used InstGroup in this way: I store all the different types of Object Instances in the InstGroup, such as Edges, Tiles, Chess, Portals, etc, into a InstGroup group called ‘board’.

Then there is a nice convenience feature in InstGroup that simply says ‘Destroy instances’ and will allow you to pick which group. This is super-clean, though I noticed that the UID numbers of destroyed instances (eg Tiles) do not get re-used, so the numbers keep on going up.

Portals and player_start

In Tiled, I’ve used Object Layers to define portals and start positions. The player_start entity is only defined by its name ‘player_start’.  It may be a good idea to make this more generic, but I’ll leave that for later.

Portals, on the other hand, I expect to be placed a lot more, so this object has the type property called ‘portal’. The name of the portal refers to its identifier in the context of the room. For now, the syntax goes like p#, where # is simply the number.

In Tiled, the Portal entity has one custom property called ‘destination’, and the syntax for that is r#.p#. The r# refers to the room number, which is the same as the TMX file (eg r1.tmx). Later, in C2, this ‘destination’ property is tokenised to give out the TMX file to be opened, and the portal to go to.

In C2, the Portal exists in a z-layer called ‘Portal’.  To clarify, a z-layer refers the Board’s z-index. During TMX loading, when a Portal Object is encountered, it places a Portal Sprite into that ‘Portal’ z-layer and is tested later when there is an attempt to use the Portal (Cell is occupied)

I’ve used a global variable to track the intended Portal, since it doesn’t seem that there are any advantages to doing this through some parameter in the TMX loading.

So, the TMX is loaded by the tokenised ‘destination’, the portal, also tokenised, is stored as a global variable; then the TMX goes through its processing, and when processing Object Layers, sees the portal that the player must start in, and places the player there.

Persistence

Some thoughts re save/load functionality: saving and loading the TMX object will yield only the data that had already been previously read, but the TMX data itself could not be changed by C2 because the TMX Importer doesn’t have methods to do that.

So what’s happening here is that the TMX is loaded, and I transfer pertinent information from TMX into C2 through positioning, instance variables, etc. So what I have is a functionally static TMX as the basis for a scene/room, but loading/saving of this TMX file means nothing in this context.

So how to go about persistence?

If we load the TMX every time there is a portal movement, instances are deleted, and then recreated and applied the changes post-load, very much like Maya’s referenceEdits.

Or, could we use multiple C2 Layouts and jump between them? However, C2 destroys objects when moving to another Layout unless that object is flagged as global.

So, do we store state data for specific things before room transfer and then apply them back on when loading? The issue here is knowing which entity held the data to begin with. For example, if a Chess sits as a container (of data), like a chest, then when it is recreated again by room loading, how do we identify that Chess uniquely again? I consider tile ids to be volatile.

I think the only way to uniquely identify something is by using Objects.

TMX Importer v2 – TMX and external tilesheets

Unfortunately, external tilesheets are not supported, since they are referenced in the TMX as external, and TMX Importer v2, understandably, does not open up the reference.

However, it is possible to copy-and-paste changes made to the external tilesheet into the TMX. Perhaps a tool could be made to replace the TMX’s tilesheet with another .tsx file.