godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.85k stars 21.14k forks source link

TileSet methods `tile_get_texture(id).get_size()` and `tile_get_texture(id).get_data()` return wrong data #33366

Open wyattbiker opened 5 years ago

wyattbiker commented 5 years ago

Godot version: v3.2.beta1 OS/device including version: Mac OS X Mojave

Issue description: TileSet methods tile_get_texture ( id ).get_size()and tile_get_texture ( id ).get_data() return the complete TileSet size and texture, instead of the individual tile size and texture as specified by id.

Steps to reproduce: Create a Node2d scene and add a TileMap node. Add the tileset sheet (see below) to the tilemap and edit, slicing out a couple of individual tiles. Add the script below to the TileMap node.

extends TileMap

func _ready() -> void:
    var ts: TileSet=$".".tile_set
    var ids=ts.get_tiles_ids ( )
    var img: Image
    var texture: Texture
    for id in ids:
        print("id=",id, 
            ", Name=",ts.tile_get_name ( id ),
            ", Tile Region=",ts.tile_get_region ( id ),
            ", Tile Size=",ts.tile_get_texture ( id ).get_size())
        img=ts.tile_get_texture ( id ).get_data()
        img.save_png("user://"+ts.tile_get_name ( id )+".png")
    pass # Replace with function body.

I have selected three tiles from tilesheet below and I get this output: id=0, Name=tilesheet1.png 0, Tile Region=(0, 0, 64, 64), Tile Size=(1408, 768) id=1, Name=tilesheet1.png 1, Tile Region=(64, 0, 64, 64), Tile Size=(1408, 768) id=2, Name=tilesheet1.png 2, Tile Region=(128, 0, 64, 64), Tile Size=(1408, 768)

And each one of the saved png files is the complete tilesheet map instead of the individual tiles.

Tilesheet Sample

image

derickvn commented 4 years ago

I can confirm the same issue on 3.2.1.stable on Ubuntu 20.10 tile_get_texture(tile_id) returns a Texture representing the full texture of the sheet.

derickvn commented 4 years ago

I just realised this could be intended behaviour. There could be multiple textures in a tileset and tile_get_texture returns the full texture. You can get the sprite to render from tile_get_region() which returns the info you need to select the correct image... Here's some sample code that pointed me to this conclusion:

func _ready():
    var tile = tileset.find_tile_by_name("TileName")
    var tex = tileset.tile_get_texture(tile)
    var rect = tileset.tile_get_region(tile)
    var player = Sprite.new()
    player.texture = tex
    player.region_enabled = true
    player.region_rect = rect
    player.position = Vector2(0, 0)
    get_node('/root/Main').add_child(player)

This will get a named tile from the tile set and display it onscreen

Duehok commented 4 years ago

Confirmed on 3.2.2, mono, win 10 . That is definetly NOT the intended behavior. The documentation is very clear:

https://docs.godotengine.org/en/3.0/classes/class_tileset.html#class-tileset-tile-get-texture

Texture tile_get_texture ( int id ) const Returns the tile's texture. (emphasis mine)

Workaround that can be very 💩 for performance: load the tileset texture in an AtlasTexture, set the AtlasTexture's region to the tile's region.

FilipLundby commented 4 years ago

Confirmed in 3.2.3 stable.

herbeeg commented 4 years ago

Confirmed on 3.2.2, mono, win 10 . That is definetly NOT the intended behavior. The documentation is very clear:

https://docs.godotengine.org/en/3.0/classes/class_tileset.html#class-tileset-tile-get-texture

Texture tile_get_texture ( int id ) const Returns the tile's texture. (emphasis mine)

Workaround that can be very hankey for performance: load the tileset texture in an AtlasTexture, set the AtlasTexture's region to the tile's region.

Thank you @Duehok - spent a while trying to debug what was wrong here! Can confirm that this workaround does work, 3.2.2 stable, Ubuntu 20.04. I've used the following code in one of my projects for the time being, where getId() is the TileMap ID and get_data() gets me the raw Image:

var atlas = AtlasTexture.new()
var texture = ImageTexture.new()

texture.create_from_image(self.tile_set.tile_get_texture(p1_char1.getId()).get_data())
atlas.atlas = texture
atlas.region = self.tile_set.tile_get_region(p1_char1.getId())

You can then pass atlas in place of where you'd have TileSet::tile_get_texture(). While this might not be very efficient as you say (and my code isn't either!), it's a valid alternative for now, for small projects. Hope this helps anyone.

AnidemDex commented 2 years ago

A simple one workaround without using AtlasTexture in 3.4 (wich is very similar to what you do with atlas):

func get_tile_texture(ID:int) -> Texture:
    var tile_texture:Texture = <TileSet>.tile_get_texture(ID)
    var texture := ImageTexture.new()
    texture.create_from_image(tile_texture.get_data().get_rect(tileset.tile_get_region(ID)))
    return texture
whitestranger7 commented 2 years ago

I can confirm that the issue still not fixed for 3.4.2 godot version

dalexeev commented 2 years ago

I believe this is not a bug. The code snippets below confirm this.

Think of this method not as "returns the texture of the given tile", but as "returns the texture associated with the given tile". If you need the texture of the tile itself, then this should be a separate method, I suppose.

https://github.com/godotengine/godot/blob/eb36566c34e5fe93a00812d801738a86fe2dd022/scene/resources/tile_set.cpp#L223-L237

https://github.com/godotengine/godot/blob/eb36566c34e5fe93a00812d801738a86fe2dd022/scene/resources/tile_set.h#L137

https://github.com/godotengine/godot/blob/eb36566c34e5fe93a00812d801738a86fe2dd022/scene/resources/tile_set.cpp#L398-L401

https://github.com/godotengine/godot/blob/eb36566c34e5fe93a00812d801738a86fe2dd022/scene/resources/tile_set.cpp#L409-L412

https://github.com/godotengine/godot/blob/eb36566c34e5fe93a00812d801738a86fe2dd022/scene/resources/tile_set.cpp#L420-L423