Content in Coin Time

Coin Time is a sample project that represents how a full CocosSharp project might be organized. Coin Time’s structure aims to simplify the addition and maintenance of content. It uses .tmx files created by Tiled for levels and XML files to define animations. Modifying or adding new content can be achieved with minimal effort.

While this approach makes Coin Time an effective project for learning and experimentation, it also reflects how professional games are made. This guide explains some of the approaches taken to simplify adding and modifying content.

Working with TMX Files

Coin Time levels are defined using the .tmx file format, which is output by the Tiled tile map editor. For a detailed discussion of working with Tiled, see the Using Tiled with Cocos Sharp guide.

Each level is defined in its own .tmx file contained in the CoinTime/Assets/Content/levels folder. All Coin Time levels share one tileset file, which is defined in the mastersheet.tsx file. This file defines the custom properties for each tile, such as whether the tile has solid collision or whether the tile should be replaced by an entity instance. The mastersheet.tsx file allows properties to be defined only once and used across all levels.

Editing a Tile Map

To edit a tile map, open the .tmx file in Tiled by double-clicking the .tmx file or opening it through the File menu in Tiled. Coin Time level tile maps contain three layers:

Entities – this layer contains tiles which will be replaced with instances of entities at runtime. Examples include the player, coins, enemies, and the end-of-level door.

Terrain – this layer contains tiles which typically have solid collision. Solid collision allows the player to walk on these tiles without falling through. Tiles with solid collision can also act as walls and ceilings.

Background – the Background layer contains tiles that are used as the static background. This layer does not scroll when the camera moves throughout the level, creating the appearance of depth through parallax.

As we will explore later, the level-loading code expects these three layers in all Coin Time levels.

Editing Terrain

Tiles can be placed by clicking in the mastersheet tileset and then clicking on the tile map. For example, to paint new terrain in a level:

Select the Terrain layer

Click on the tile to draw

Click or push and drag over the map to paint the tile

The top-left of the tileset contains all of the terrain in Coin Time. Terrain, which is solid, includes the SolidCollision property, as shown in the tile properties on the left of the screen:

Editing Entities

Entities can be added or removed from a level – just like terrain. The mastersheet tileset has all entities placed about halfway horizontally, so they may not be visible without scrolling to the right:

New entities should be placed on the Entities layer.

CoinTime code looks for the EntityType when a level is loaded to identify tiles which should be replaced by entities:

Once the file has been modified and saved, the changes will automatically show up if the project is built and run:

Adding New Levels

The process of adding levels to Coin Time requires no code changes, and only a few small changes to the project. To add a new level:

Open the level folder located at <CoinTime Root>\CoinTime\Assets\Content\levels

Copy and paste one of the levels, such as level0.tmx

Rename the new .tmx file so it continues the level number sequence with existing levels, such as level8.tmx

In Visual Studio or Visual Studio for Mac, add the new .tmx file to the Android levels folder. Verify that the file uses the AndroidAsset build action.

Add the new .tmx file to the iOS levels folder. Be sure to link the file from its original location and verify that it uses the BundleResource build action.

The new level should appear in the level select screen as level 9 (level file names start at 0, but the level buttons begin with the number 1):

Level Loading

As shown earlier, new levels require no changes in code – the game automatically detects the levels if they are named correctly and added to the levels folder with the correct build action (BundleResource or AndroidAsset).

The logic for determining the number of levels is contained in the LevelManager class. When an instance of the LevelManager is constructed (using the singleton pattern), the DetermineAvailbleLevels method is called:

private void DetermineAvailableLevels()
{
// This game relies on levels being named "levelx.tmx" where x is an integer beginning with
// 1. We have to rely on MonoGame's TitleContainer which doesn't give us a GetFiles method - we simply
// have to check if a file exists, and if we get an exception on the call then we know the file doesn't
// exist.
NumberOfLevels = 0;
while (true)
{
bool fileExists = false;
try
{
using(var stream = TitleContainer.OpenStream("Content/levels/level" + NumberOfLevels + ".tmx"))
{
}
// if we got here then the file exists!
fileExists = true;
}
catch
{
// do nothing, fileExists will remain false
}
if (!fileExists)
{
break;
}
else
{
NumberOfLevels++;
}
}
}

CocosSharp does not provide a cross-platform approach for detecting if files are present, so we have to rely on the TitleContainer class to attempt to open a stream. If the code for opening a stream throws an exception, then the file does not exist and the while loop breaks. Once the loop finishes, the NumberOfLevels property reports how many valid levels are part of the project.

The LevelSelectScene class uses the LevelManager.NumberOfLevels to determine how many buttons to create in the CreateLevelButtons method:

The NumberOflevels property is used to determine which buttons should be created. This code considers which page the user is currently viewing and only creates a maximum of six buttons per page. When clicked, the button instances call the HandleButtonClicked method:

This method assigns the CurrentLevel property which is used by the GameScene when loading a level. After setting the CurrentLevel, the GoToGameScene method is raised, switching the scene from LevelSelectScene to GameScene.

The GameScene constructor calls GoToLevel, which performs the level-loading logic:

LoadLevel

The LoadLevel method is responsible for loading the .tmx file and adding it to the GameScene. This method does not create any interactive objects such as collision or entities – it simply creates the visuals for the level, also referred to as the environment.

The CCTileMap constructor takes a file name, which is created using the level number passed in to LoadLevel. For more information on creating and working with CCTileMap instances, see the Using Tiled with CocosSharp guide.

Currently, CocosSharp does not allow reordering of layers without removing and re-adding them to their parent CCScene (which is the GameScene in this case), so the last few lines of the method are required to reorder the layers.

CreateCollision

The CreateCollision method constructs a LevelCollision instance which is used to perform solid collision between the player and environment.

Without this collision, the player would fall through the level and the game would be unplayable. Solid collision lets the player walk on the ground and prevents the player from walking through walls or jumping up through ceilings.

Collision in Coin Time can be added with no additional code – only modifications to tiled files.

ProcessTileProperties

Once a level is loaded and the collision is created, ProcessTileProperties is called to perform logic based on tile properties. Coin Time includes a PropertyLocation struct for defining properties and the coordinates of the tile with these properties:

The foreach loop evaluates each tile property, checking if the key is either EntityType or RemoveMe. EntityType indicates that an entity instance should be created. RemoveMe indicates that the tile should be completely removed at runtime.

If a property with the EntityType key is found, then TryCreateEntity is called, which attempts to create an entity using the property matching the EntityType key:

Adding New Entities

Coin Time uses the entity pattern for its game objects (which is covered in the Entities in CocosSharp guide). All entities inherit from CCNode, which means they can be added as children of the gameplayLayer.

Each entity type is also referenced directly through a list or single instance. For example, the Player is referenced by the player field, and all Coin instances are referenced in a coins list. Keeping direct references to entities (as opposed to referencing them through the gameLayer.Children list) makes code which accesses these entities easier to read and eliminates potentially expensive type casting.

The existing code provides a number of entity types as examples of how to create new entities. The following steps can be used to create a new entity:

1 - Define a new class using the entity pattern

The only requirement for creating an entity is to create a class which inherits from CCNode. Most entities have some visual, such as a CCSprite, which should be added as a child of the entity in its constructor.

CoinTime provides the AnimatedSpriteEntity class which simplifies the creation of animated entities. Animations will be covered in more detail in the Animated Entities section.

2 – Add a new entry to the TryCreateEntity switch statement

Instances of the new entity should be instantiated in the TryCreateEntity. If the entity requires every-frame logic like collision, AI, or reading input, then the GameScene needs to keep a reference to the object. If multiple instances are needed (such as Coin or Enemy instances), then a new List should be added to the GameScene class.

3 – Modify tile properties for the new entity

Once the code supports the creation of the new entity, the new entity needs to be added to the tileset. The tileset can be edited by opening any level .tmx file.

The tileset is stored separate from the .tmx in the mastersheet.tsx file, so it must be imported before it can be edited:

Once imported, properties on selected tiles are editable, and the EntityType can be added:

After the property is created, its value can be set to match the new case in TryCreateEntity:

After the tileset has been changed, it must be exported – this makes the changes available for all other levels:

The tileset should overwrite the existing mastersheet.tsx tileset:

Entity Tile Removal

When a tile map is loaded into a game, the individual tiles are static objects. Since entities require custom behavior such as movement, Coin Time code removes tiles when entities are created.

ProcessTileProperties includes logic to remove tiles which create entities using the RemoveTile method:

This animation only contains a single frame, resulting in the Spike entity displaying a static image. Entities can use .achx files whether they display single or multi-frame animations. Additional frames can be added to .achx files without requiring any changes in code.

Frames define which image to display in the TextureName parameter, and the coordinates of the display in the LeftCoordinate, RightCoordinate, TopCoordinate, and BottomCoordinate tags. These represent the pixel coordinates of the frame of animation in the image which is being used – mastersheet.png in this case.

The FrameLength property defines the number of seconds that a frame in an animation should be displayed. Single-frame animations ignore this value.

All other AnimationChain properties in the .achx file are ignored by Coin Time.

AnimatedSpriteEntity

Animation logic is contained in the AnimatedSpriteEntity class, which serves as the base class for most entities used in the GameScene. It provides the following functionality:

Loading of .achx files

Animation cache of loaded animations

CCSprite instance for displaying the animation

Logic for changing the current frame

The Spikes constructor provides a simple example of how to load and use animations:

The propAnimations.achx only contains one animation, so the constructor accesses this animation by index. If a .achx file contains multiple animations, then animations can be referenced by name, as shown in the Enemy constructor:

Summary

This guide covers the implementation details of coin time. Coin Time is created to be a complete game, but is also a project which can be easily modified and expanded. Readers are encouraged to spend time making modifications to levels, adding new levels, and creating new entities to further understand how Coin Time is implemented.