Description
Your Godot version:
4.4
Issue description:
TileSetAtlasSource's create_tile()
and texture
codependency /ordering not explicit enough.
Proposal:
Update the documentation for TileSetAtlasSource.create_tile()
and the texture
property to clearly state that the atlas texture must be assigned/valid before calling create_tile()
. This is necessary because the texture defines the atlas grid's dimensions, and failing to assign it first leads to undefined texture bounds and errors such as !room_for_tile
.
For example, the docs should include a note like:
Note: Always call
set_texture()
with a valid texture before invokingcreate_tile()
. Failure to do so will result in undefined texture bounds and may trigger a!room_for_tile
error.
Simple Code Example (Correct Usage):
# Assume the following constants are defined:
const TILE_SIZE: Vector2i = Vector2i(16, 16) # Each tile's size: 16x16
const IMAGE_TEXTURE_SIZE: Vector2i = Vector2i(16, 16) # Size of the drawn image per tile
const TEXTURE_REGION_SIZE: Vector2i = Vector2i(16, 16) # Region size in the atlas (16x16)
const MARGIN: Vector2i = (TEXTURE_REGION_SIZE - IMAGE_TEXTURE_SIZE) / 2 # Margin (0,0 in this case)
const ATLAS_MARGINS: Vector2i = Vector2i(0, 0) # No extra atlas margins
const GRID_TILE_SIZE: Vector2i = Vector2i(1, 1) # One grid cell per tile
const ATLAS_SEPARATION: Vector2i = Vector2i(0, 0) # No extra separation between tiles
const SOURCE_ID: int = 1234 # Unique ID for TileMap.set_cell()
func create_and_save_tileset() -> TileSet:
var tile_colors = [Color.RED] # Single tile: red only
var tileset: TileSet = TileSet.new() # Create a new TileSet resource
tileset.set_tile_size(TILE_SIZE) # Set tile size to 16x16
var atlas_source: TileSetAtlasSource = TileSetAtlasSource.new() # Create an atlas source
atlas_source.set_margins(ATLAS_MARGINS) # Set atlas margins (none)
atlas_source.set_separation(ATLAS_SEPARATION) # Set tile separation (none)
atlas_source.set_use_texture_padding(true)
atlas_source.set_texture_region_size(TEXTURE_REGION_SIZE)
# Create an atlas image sized for one tile (16x16)
var atlas_texture_width = TEXTURE_REGION_SIZE.x
var atlas_texture_height = TEXTURE_REGION_SIZE.y * tile_colors.size()
var atlas_image: Image = Image.create_empty(atlas_texture_width, atlas_texture_height, false, Image.FORMAT_RGBA8)
for x in range(IMAGE_TEXTURE_SIZE.x): # Loop over tile width (16 pixels)
for y in range(IMAGE_TEXTURE_SIZE.y): # Loop over tile height (16 pixels)
atlas_image.set_pixel(x, y, tile_colors[0])
var final_texture: ImageTexture = ImageTexture.create_from_image(atlas_image)
# CORRECT: Assign valid texture it BEFORE creating tiles
atlas_source.set_texture(final_texture)
# CORRECT: Now, create the tile definition safely with a valid texture
atlas_source.create_tile(Vector2i(0, 0), GRID_TILE_SIZE)
tileset.add_source(atlas_source) # Add the atlas source to the TileSet
tileset.set_source_id(0, SOURCE_ID) # Set the unique source ID for later use in TileMap.set_cell()
ResourceSaver.save(tileset, "res://Resources/TileSets/red_tileset.tres") # Save the TileSet
return tileset
Misleading/potential issue code:
func create_and_save_tileset_error() -> TileSet:
var tile_colors = [Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW]
var tileset: TileSet = TileSet.new()
tileset.set_tile_size(TILE_SIZE)
var atlas_source: TileSetAtlasSource = TileSetAtlasSource.new()
[...]
for i in range(tile_colors.size()):
var color = tile_colors[i]
var x_offset: int = MARGIN.x + ATLAS_MARGINS.x
var y_offset: int = TEXTURE_REGION_SIZE.y * i + MARGIN.y + ATLAS_MARGINS.y
for x in range(IMAGE_TEXTURE_SIZE.x):
for y in range(IMAGE_TEXTURE_SIZE.y):
atlas_image.set_pixel(x_offset + x, y_offset + y, color)
var atlas_coords: Vector2i = Vector2i(0, i)
# ERRONOUS: Create the tile before assigning the texture (triggers "!room_for_tile" error)
atlas_source.create_tile(atlas_coords, GRID_TILE_SIZE)
var final_texture: ImageTexture = ImageTexture.create_from_image(atlas_image)
# ERRONOUS: Assigning the texture to the atlas source was too late, and thus the tiles are created without a valid texture
atlas_source.set_texture(final_texture)
tileset.add_source(atlas_source)
tileset.set_source_id(0, SOURCE_ID)
ResourceSaver.save(tileset, "res://Resources/TileSets/erroneous_tileset.tres")
return tileset
original ticket regarding the solution and discovery of the issue godotengine/godot#98991 (comment)
URL to the documentation page (if already existing):
https://docs.godotengine.org/en/stable/classes/class_tilesetsource.html
NOTE: this is my first attempt in participating in the godot engine repo's, so if possible I would also appreciate any sort of guidance on a potential code fix if that is more appropriate. For example, the error message that occurs from the out of order tileset property instantiation is very misleading because it arises in other areas unrelated to order of texture instantiation/tileset creation.
<C++ Error> Condition "!room_for_tile" is true.
is not descriptive enough in the case of improper texture state when creating the tileset, so if possible a code refactor might be more effective here rather than just a documentation update