TileSet in Godot – Complete Guide

Tile-based games have a charm that has endured through decades, and the core element that brings these games to life is the tileset. Working with tilesets allows developers to create intricate levels and worlds with a finite set of art assets, simplifying the development process while expanding creative potential. The Godot Engine, with its powerful TileSet class in Godot 4, provides a robust set of tools for developers to create these worlds more efficiently. Let’s dive into what TileSet is, how it works, and why learning to work with it can be a significant boost to your game development skills.

What is TileSet in Godot 4?

A TileSet in Godot 4 is essentially a library of tiles that can be used with a TileMap node to create game levels, whether they’re side-scrolling platforms, top-down adventures, or isometric wonders. Tiles are individual pieces of your game map, like puzzle pieces, that can be laid out to form the environment in which players interact.

What is TileSet Used For?

TileSet serves as the backbone for level design in tile-based games within Godot. It manages different sources of tiles, whether they’re image-based from TileSetAtlasSource or scene-based from TileSetScenesCollectionSource. With features supporting physics, navigation, and more, TileSet enables developers to create rich and interactive game levels without a nightmare of complexity.

Why Should I Learn to Use TileSet?

Learning to use TileSet is key for any aspiring game developer focused on creating tile-based games. TileSets allow for:

– Efficient level design by reusing tile assets.
– Quick prototyping of game levels, making iteration fast and responsive.
– The creation of rich environments with fewer resources, ideal for indie developers or small teams.

Understanding TileSet will enable you to bring your game environments to life with less overhead and more creativity. Ready to start tiling? Let’s get our hands dirty with some practical examples in the next sections!

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Creating a Basic TileSet

Before we can begin laying out our game level, we need to create a basic TileSet. First, prepare your tile images by making sure they’re all the same size and arranged in a grid format within a single image file. Here’s how to get started with a new TileSet in Godot 4:

var tile_set = TileSet.new()

After you have a new TileSet instance, you’ll want to create a TileSetAtlasSource, which you can think of as a ‘page’ of tiles:

var atlas_source = TileSetAtlasSource.new()
tile_set.add_source(atlas_source)

Now you must set up the region from where to pick the tiles. This is where we tell Godot where our tiles actually are within our tile sheet image:

var texture = preload("res://path_to_your_tileset.png")
atlas_source.set_texture(texture)
atlas_source.set_region(Rect2(0, 0, texture.get_width(), texture.get_height()))

Finally, define each individual tile by specifying the portion of the image they occupy:

var tile_id = atlas_source.add_tile(Rect2(x, y, tile_width, tile_height))

Repeat this step for each tile in your tileset by adjusting the x and y coordinates to the position of each tile in the grid.

Editing Tile Properties

Once you have your tiles set up, you can start editing their properties. Godot 4 tilesets are very powerful and allow you to add collision, occlusion and navigation shapes directly to the tiles:

// Define a collision shape for your tile
var collision_shape = ConvexPolygonShape2D.new()
collision_shape.set_point_cloud([Vector2(0,0), Vector2(tile_width,0),
                                 Vector2(tile_width,tile_height), Vector2(0,tile_height)])
tile_set.tile_set_shape(0, tile_id, 0, collision_shape)

// Define an occlusion shape for your tile
var occlusion_polygon = OccluderPolygon2D.new()
occlusion_polygon.set_polygon([Vector2(0,0), Vector2(tile_width,0),
                               Vector2(tile_width,tile_height), Vector2(0,tile_height)])
tile_set.tile_set_occluder(0, tile_id, occlusion_polygon)

// Define a navigation shape for your tile
var navigation_polygon = NavigationPolygon.new()
navigation_polygon.add_polygon(Vector2Array([Vector2(0,0), Vector2(tile_width,0),
                                             Vector2(tile_width,tile_height), Vector2(0,tile_height)]))
tile_set.tile_set_navigation_polygon(0, tile_id, navigation_polygon)

Remember to replace `tile_width` and `tile_height` with the actual size of your tiles.

Creating a TileMap and Adding Your TileSet

With your TileSet created and configured, it’s time to use it in a TileMap. Create a new TileMap node and set your TileSet as its TileSet property:

var tile_map = TileMap.new()
tile_map.set_tileset(tile_set)
add_child(tile_map)

Now, you’re ready to start painting tiles onto the TileMap:

// This will place a tile at grid coordinates (5, 5)
tile_map.set_cell(5, 5, tile_id)

Iterating Over Tiles and Painting Levels

If you want to create levels programmatically or paint multiple tiles at once, you can iterate over the grid coordinates of the TileMap and set tiles accordingly:

// Fill a 10x10 area with a specific tile
for x in range(10):
    for y in range(10):
        tile_map.set_cell(x, y, tile_id)

This is particularly useful when you want to establish a base layer for your level, like a grass field or a dungeon floor.

With these code examples, you’ve learned how to create a TileSet, edit its tile properties, create a TileMap, and begin painting tiles to form your game’s world. Remember that these are the foundational steps, and there’s much more to explore with Godot’s TileSet and TileMap capabilities! Stay tuned for further examples where we’ll delve into more advanced features.

Advanced TileSet Features

Godot’s TileSet offers many advanced features that allow for more complex and dynamic level designs. Let’s keep expanding our game development toolkit by diving into some of these features with practical code examples.

Animations can bring your tilesets to life, for instance, animating water or lava. Here’s how you can define an animation for a tile:

// Create an animation for the tile
var animation = TileSetAnimation.new()
animation.set_steps_per_second(5) // Set the animation speed
animation.set_frames([TileSetAnimationFrame.new(tile_id, 0.2), 
                      TileSetAnimationFrame.new(tile_id + 1, 0.2),
                      TileSetAnimationFrame.new(tile_id + 2, 0.2)])
tile_set.tile_set_animation(tile_id, animation)

You should replace `tile_id`, `tile_id + 1`, and `tile_id + 2` with the IDs of the tiles you want to be part of the animation.

Autotiling is an essential feature that automatically selects the appropriate tile based on neighboring tiles, which is great for terrain that blends together like dirt paths or rocky terrain. Here is how you could set up an Autotile:

// First, tell the tile source it uses autotiling
atlas_source.set_tiles_mode(TileSetAtlasSource.TILE_MODE_AUTOTILE)

// Then, setup the bitmask mode and autotile bitmasks
atlas_source.set_bitmask_mode(tile_id, TileSetAtlasSource.BITMASK_3X3_MINIMAL)
var bitmasks = PoolIntArray([
  0,1,1, 1,1,1, 1,1,0,
  1,1,1, 1,1,1, 1,1,1,
  0,1,1, 1,1,1, 1,1,0
])
atlas_source.set_bitmask(tile_id, bitmasks)

Using a `PoolIntArray`, you define a 3×3 grid bitmask where 1s represent neighboring tiles that must match for this tile to be selected.

Z-Index & Occlusion features allow you to stack tiles and create an occlusion effect where, for example, the player can walk behind tall grass or a tree.

// Setting the z-index for a tile, tiles with higher z-index will be drawn on top
tile_set.tile_set_z_index(tile_id, 1)

// Creating an occlusion shape to prevent lower Z-index tiles from being seen behind this one
var occlusion_shape = OccluderPolygon2D.new()
occlusion_shape.set_polygon([Vector2(0, 0), Vector2(tile_width, 0),
                             Vector2(tile_width, tile_height), Vector2(0, tile_height)])
tile_set.tile_set_occlusion_polygon(tile_id, 0, occlusion_shape)

Focusing now on the collision aspect of TileSets, you can easily create complex collision shapes for your tiles. Let’s say your game features sloped surfaces, which you can define like this:

// Create a collision shape for a slope
var slope_collision_shape = ConcavePolygonShape2D.new()
slope_collision_shape.set_segments([Vector2(0, tile_height), Vector2(tile_width, 0)])
tile_set.tile_set_shape(tile_id, 0, slope_collision_shape)

Terrain Sets offer a way to manage different terrains within a single TileSet by grouping associated tiles. You can set a tile as part of a terrain set like so:

// Define a terrain set
var terrain_set = TileSet.TerrainSet.new()
terrain_set.add_tile(tile_id)
atlas_source.set_terrain(terrain_set)

With these advanced features, you’re now equipped to handle sophisticated scenarios and truly bring your levels to life. Whether it’s animating tiles, crafting seamless terrains, or ensuring interactions with slopes are natural, these tools reflect the strength and versatility of the Godot Engine.

Remember that there’s plenty more to explore, and with practice, you’ll become adept at using TileSets to their full potential. Keep experimenting and refining your skills, and watch as your game worlds transform from basic grids to rich, interactive landscapes.In the previous sections, we’ve addressed various features and how to implement them using Godot’s TileSet. Now, let’s push the envelope further and delve into more complex uses like using scene-based tiles, handling multiple tile layers, and scripting behaviors for tiles.

Scene-based Tiles: Tiles need not be restricted to static images. Godot 4 allows you to use entire scenes as tiles, meaning that your tiles can have scripts, animations, and even physics processes running on them.

Here’s a quick way to add a scene as a tile:

// Assume we have a scene called 'InteractiveTile.tscn' which we want to use as a tile
var scenes_collection_source = TileSetScenesCollectionSource.new()
var scene = preload("res://path_to_your_scene/InteractiveTile.tscn")
scenes_collection_source.add_scene(scene)
tile_set.add_source(scenes_collection_source)

// The id for a scene tile is given when the scene is added. Let's fetch and use it
var scene_tile_id = scenes_collection_source.find_tile_by_scene(scene)

Handling Multiple Layers: Godot’s TileMap node allows you to work with multiple layers via child TileMap nodes. This means you can separate your ground tiles from your decoration tiles, for example.

Creating a new layer to hold decorative elements can be achieved like this:

// Create a new TileMap node for the decorative layer
var decoration_layer = TileMap.new()
decoration_layer.set_tileset(tile_set)
decoration_layer.set_name("DecorationLayer")
add_child(decoration_layer)

// Change the Z index so it renders above the base layer
decoration_layer.set_z_index(1)

To place tiles on this layer, you would do something similar to what we did earlier, but referencing `decoration_layer` instead.

Scripting Behaviors: Giving tiles behavior makes your world interactive. You can create a tile that changes when a player walks over it as an example.

First, create a script for a scene you want to use as an interactive tile and then attach it to a scene based tile:

// path_to_your_script.gd would be the path to a script that you want to attach
var script = preload("res://path_to_your_script.gd")
scenes_collection_source.set_scene_script(scene_tile_id, script)

In your script, you can handle signals emitted by the TileMap, such as when it’s stepped on:

func _on_TileMap_cell_occupied(position):
    # Change this tile to another, indicating it's been interacted with
    $TileMap.set_cellv(position, altered_tile_id)

Note that you’d have to emit the `cell_occupied` signal from the script controlling your character’s movements.

Dynamic Tile Adjustments: Sometimes you’ll want to change tiles in response to player actions or game events, like destroying a block or changing the ground type.

Here’s how you might programmatically ‘destroy’ a tile, triggering an animation and then removing it:

func destroy_tile_at(position):
    var tile_id = $TileMap.get_cellv(position)
    if tile_id == destructible_tile_id:
        # Trigger some kind of animation or effect here

        # After the effect finishes, remove the tile
        $TileMap.set_cellv(position, -1)  // -1 clears the cell

For altering the terrain type dynamically:

func change_terrain_type(position, new_terrain_tile_id):
    $TileMap.set_cellv(position, new_terrain_tile_id)

These examples showcase the level of control and fidelity you can achieve with Godot’s TileSet and TileMap classes. Whether it’s creating a multilayered map, introducing responsive and interactive tiles with behaviors, or altering your map dynamically at runtime, the potential for creativity is vast. Practice these concepts, intertwine them with your game mechanics, and your worlds will not only look captivating but also react in a rich and engaging manner, providing players with an immersive experience.

Continuing Your Game Development Journey

Now that you’ve taken your first steps into the world of tile-based game creation with Godot 4, you’re probably eager to continue expanding your knowledge and skills. Mastering the art of game development is a journey of continuous learning and practice, and we at Zenva are here to support you every step of the way.

We encourage you to explore our Godot Game Development Mini-Degree, where you’ll find a comprehensive series of courses to further your game development education. Our learning paths are designed to help you create cross-platform games, covering an array of essential topics, from the intricacies of 2D and 3D game creation to advanced mechanics and gameplay systems.

For those of you who are looking to delve into a wider array of Godot topics, our full collection of Godot courses will set you on the right track. Whether you’re a beginner or ready to level up your existing development skills, our courses are tailored to help you go from learning to doing.

Join us on your journey to becoming a game development pro, build a diverse portfolio, and solidify your skills with hands-on experience in the Godot Engine!

Conclusion

Embarking on this adventure through the world of tile-based games with Godot 4 has set you up with the foundational knowledge to build engaging, vibrant levels for your players. Remember, the concepts and skills you have acquired are just the beginning. There is a universe of possibilities waiting for you to explore, and every game you create is a stepping stone towards mastering game development.

We at Zenva understand the importance of practice and perseverance in this journey. Whether you are just starting out or seeking to add advanced features to your games, our Godot Game Development Mini-Degree is designed to take your skills to the next level. You’re not just learning to code; you’re learning to create worlds. So, keep experimenting with TileSets, refining your TileMaps, and remember: each line of code brings you one step closer to your masterpiece. Let the creativity flow, and see you at Zenva for the next chapter in your game development saga!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.