Top-Down Layout

Important:

This demo requires the New Input System and won't work unless it's enabled.

Layout Demo.png

Purpose

This demo uses almost every main feature of the TilePlus system.

If you aren't yet familiar with what a SRS is, please see this.

If you look in the Demo code you'll see a SRS folder. There you'll find several SRS scripts:

Next, look at the Demo code's Components folder. Here you'll find:

ChunkingGameController's Start doesn't do much:

At this point the code mostly runs on its own: events from PlayerController cause re-layout as the player character moves around.

As the player character moves around, its position is messaged to all tiles implementing ITpMessaging<PositionPacket>.MessageTarget. This packet is used to send the current Tilemap position of the player character to each of these tiles. If the position sent matches the position of a tile, it posts an Event to TpEvents.

TpEvents is outwardly pretty simple: it just stores references to any tile which 'posts' an event. The ChunkingDemoGameState Service subscribes to TpEvents, and gets notified when an event is posted; setting a flag variable.

Note that ChunkingDemoGameState is less of a 'service' and more like a global variables class; that is, a singleton. In a non-demo app you probably don't want to use this approach but it's easier to understand.

The Update method of ChunkingDemoGameState AND the UpdateTiles method of ChunkingDemoLayout both use TpEvents.ProcessEvents.

Why both?

In ChunkingDemoLayout, TpEvents.ProcessEvents is called after we have sent position data to tiles. So it makes sense to process events here. However, if the app is using the "TileUi" feature of TPT, these tiles can and will send events if a tile is clicked on (or otherwise messaged via the 'New Input System'). Hence, ChunkingDemoGameState's UpdateMethod handles any of these type of UI events.

If you look at the code for ProcessEvents it doesn't look like it does much. However it's a really important part of the system.

Basically, what it does is look at all the tiles in the HashSet of tiles which had posted events. Unless you have a lot of overlapping tiles receiving position messages, there will usually be only one instance to evaluate.

A really simple method would be to look at each tile, determine its type, and have custom code for each situation. Obviously, that's really bad. Hard to add features, easy to create bugs, hard to maintain.

In the TPT system, each tile instance has a field for a scriptable object called an EventAction. There's also one for a ZoneAction, which is a different thing I'll discuss a bit later.

Event Actions have one method: Exec, and one property: Incomplete.

As ProcessEvents looks at each tile in its HashSet it sees if there is an EventAction reference. If there is, it calls Exec and passes in the tile reference along with an optional object called the EventActionObject, which is an object (c# object) with whatever sort of data that you want to pass to EventAction.Exec.

You can see the use of EventActionObjects in UiButtonTile. Here, we pass along a tag string, the zone bounds, and the tile instance (which is redundant). The TileUi demo has UiButtonToAsciiEventAction which uses this mechanism to pass along the target control.

Being able to pass-in arbitrary data from the tile to the EventAction means that the EventAction itself does not have any state information. That's important because it's a ScriptableObject and changing anything in it during Editor-Play will alter the fields. Not good. Avoiding state info in the EventAction means that we can use this S.O. for several tiles of the same type or of any other type.

If the Incomplete property is true, the event isn't discarded and is returned ProcessEvents' caller for further custom processing.

As implemented in this demo, everything is done via Event and Zone actions. Hence, no list for returned tile instances is even provided to ProcessEvents.

Let's examine an EventAction used for TreasureChests.

The minigame's goal is to get all of the chests. Can't change to another level (TScene) until that's met.

Open the Scripts/Actions/TreasureChestEventAction.cs and look at Exec().

After some simple validity checks, the treasure chest count is incremented. This is saved on a per-level basis in the game data which is periodically saved to the filesystem.

It checks to see if it's 'parent' tile actually implements the ITpPersistence interface. If it does, the tile's save data is obtained and 'Poked' into the save data using the ChunkingDemoGameState Service.

Next it does something sort of fun: opens a tile-based dialog box. The dialog box is a pre-made TileBundle which is just loaded to a separate tilemap for UI tiles.

After setting a flag in the GameState to temporarily block other UI (ie - a modal dialog) it just waits for a callback to unblock ui, which occurs when the user closes the box.

Here's an EventAction used for Waypoints

You can find WaypointEventAction in the same folder.

This action checks for the proper type of tile, disables all other waypoints, and checks to see if the waypoint is marked as a level change waypoint.

If not, it saves the game, directly from this S.O. `` If this IS a level change waypoint, is the SceneExitGoalAchieved?

If it is, Save the game and change levels (TScene).

As you can see, the EventActions are away to move object-specific code from a core part of your app back to the object itself. The relatively simple mechanism used by ProcessEvents can handle it all.


Revision #8
Created 13 July 2025 14:30:04 by Vonchor
Updated 22 July 2025 12:01:11 by Vonchor