godotengine / godotengine/godot-docs

TileSetAtlasSource create_tile() method and texture property's codependency not explicit enough

Open
#10,784 0 comments 0 reactions 0 assignees View on GitHub
area:class reference enhancement
Dominant language
reStructuredText
Stars
5.7k
Forks
3.8k
Avg merge
1d 20h
Merged PRs (30d)
25

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 invoking `create_tile()`. Failure to do so will result in undefined texture bounds and may trigger a `!room_for_tile` error.

**Simple Code Example (Correct Usage):**
```gdscript
# 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:
```gdscript
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 https://github.com/godotengine/godot/issues/98991#issuecomment-2548665374

**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.

` 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

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.