Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
meisei4 opened this issue Mar 21, 2025 · 0 comments

Comments

@meisei4
Copy link

meisei4 commented Mar 21, 2025

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):

# 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant