working keyboard and plugin for better tilemaps
This commit is contained in:
70
addons/TileMapDual/AtlasWatcher.gd
Normal file
70
addons/TileMapDual/AtlasWatcher.gd
Normal file
@@ -0,0 +1,70 @@
|
||||
##[br] Watches a TileSetAtlasSource for changes.
|
||||
##[br] Causes its 'parent' TileSetWatcher to emit terrains_changed when the atlas changes.
|
||||
##[br] Also emits parent.atlas_autotiled when it thinks the user auto-generated atlas tiles.
|
||||
class_name AtlasWatcher
|
||||
|
||||
## Prevents the number of seen atlases from extending to infinity.
|
||||
const UNDO_LIMIT = 1024
|
||||
## Stores all of the atlas instance id's that have been seen before, to prevent autogen on redo.
|
||||
static var _registered_atlases := []
|
||||
|
||||
## The TileSetWatcher that created this AtlasWatcher. Used to send signals back.
|
||||
var parent: TileSetWatcher
|
||||
|
||||
## The Source ID of `self.atlas`.
|
||||
var sid: int
|
||||
|
||||
## The atlas to be watched for changes.
|
||||
var atlas: TileSetAtlasSource
|
||||
|
||||
func _init(parent: TileSetWatcher, sid: int, atlas: TileSetAtlasSource) -> void:
|
||||
self.parent = parent
|
||||
self.sid = sid
|
||||
self.atlas = atlas
|
||||
atlas.changed.connect(_atlas_changed, ConnectFlags.CONNECT_DEFERRED)
|
||||
var id := atlas.get_instance_id()
|
||||
# should not autogen if atlas was created through redo, i.e. its instance id already existed
|
||||
if _atlas_is_empty() and id not in _registered_atlases:
|
||||
_registered_atlases.push_back(id)
|
||||
if _registered_atlases.size() > UNDO_LIMIT:
|
||||
_registered_atlases.pop_front()
|
||||
atlas.changed.connect(_detect_autogen, ConnectFlags.CONNECT_DEFERRED | ConnectFlags.CONNECT_ONE_SHOT)
|
||||
|
||||
|
||||
func _atlas_is_empty() -> bool:
|
||||
return atlas.get_tiles_count() == 0
|
||||
|
||||
|
||||
## Returns true if the texture has any opaque pixels in the specified tile coordinates.
|
||||
func _is_opaque_tile(image: Image, tile: Vector2i, p_threshold: float = 0.1) -> bool:
|
||||
# We cannot use atlas.get_tile_texture_region(tile) as it fails on unregistered tiles.
|
||||
var region := Rect2i(tile * atlas.texture_region_size, atlas.texture_region_size)
|
||||
var sprite := image.get_region(region)
|
||||
if sprite.is_invisible():
|
||||
return false
|
||||
# We're still not sure if the tile is empty or not.
|
||||
# Godot's auto-gen considers 0.1 opacity as "transparent" but not "invisible".
|
||||
for y in range(region.position.y, region.end.y):
|
||||
for x in range(region.position.x, region.end.x):
|
||||
if image.get_pixel(x, y).a > p_threshold:
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
##[br] HACK: literally just tries to guess which tiles the terrain autogen system would make
|
||||
##[br] Called once, and only once, at the end of the first frame that a texture is created.
|
||||
func _detect_autogen() -> void:
|
||||
var size := Vector2i(atlas.texture.get_size()) / atlas.texture_region_size
|
||||
var image := atlas.texture.get_image()
|
||||
var expected_tiles := []
|
||||
for y in size.y:
|
||||
for x in size.x:
|
||||
var tile := Vector2i(x, y)
|
||||
if atlas.has_tile(tile) != _is_opaque_tile(image, tile):
|
||||
return
|
||||
parent.atlas_autotiled.emit(sid, atlas)
|
||||
|
||||
|
||||
## Called every time the atlas changes. Simply flags that terrains have changed.
|
||||
func _atlas_changed() -> void:
|
||||
parent._flag_terrains_changed = true
|
||||
Reference in New Issue
Block a user