Skip to content

Instantly share code, notes, and snippets.

@Aldlevine
Last active April 6, 2022 00:02
Show Gist options
  • Save Aldlevine/93b8958a39fe7ce7ad8c2b253ccec1c5 to your computer and use it in GitHub Desktop.
Save Aldlevine/93b8958a39fe7ce7ad8c2b253ccec1c5 to your computer and use it in GitHub Desktop.
Adds an animated atlas texture to Godot
extends AtlasTexture
class_name AnimatedAtlasTexture
export(int, 1, 100) var h_frames := 1
export(int, 1, 100) var v_frames := 1
export var fps := 10.0
var previous_frame := 0
var frame := 0
func _init() -> void:
var err = VisualServer.connect("frame_pre_draw", self, "_update")
assert(err == OK)
func _update() -> void:
if atlas:
previous_frame = frame
var img := atlas.get_data()
var size = img.get_size()
var frame_size = size / Vector2(h_frames, v_frames)
frame = int(int(OS.get_ticks_msec() / (1000.0 / fps)) % (h_frames * v_frames))
var frame_pos = Vector2(frame % h_frames, floor(float(frame) / h_frames))
region = Rect2(frame_size * frame_pos, frame_size)
if previous_frame != frame:
emit_changed()
## Use this instead of a TileSet for any TileSet that contains AnimatedAtlasTexture.
## Can be added as a script to any TileSet resource, or you can create it directly in the
## TileMap's tile_set field by selecting "New AnimatedTileSet" instead of "New TileSet"
## The key is that emit_changed() must be called for the TileMap to update.
tool
extends TileSet
class_name AnimatedTileSet
var _needs_update := false
func _init() -> void:
var err
var tiles := get_tiles_ids()
for tile in tiles:
var tex = tile_get_texture(tile)
if tex is AnimatedAtlasTexture:
err = tex.connect("changed", self, "_set_needs_update", [])
assert(err == OK)
err = VisualServer.connect("frame_pre_draw", self, "_update", [], CONNECT_DEFERRED)
assert(err == OK)
func _set_needs_update() -> void:
_needs_update = true
func _update() -> void:
if _needs_update:
emit_changed()
_needs_update = false
[plugin]
name="animated_atlas_texture"
description="Adds an animated atlas texture resource"
author="Aaron Levine"
version=""
script="plugin.gd"
tool
extends EditorPlugin
var resource : AnimatedAtlasTexture
func _enter_tree():
VisualServer.connect("frame_pre_draw", self, "_update")
func _exit_tree():
VisualServer.disconnect("frame_pre_draw", self, "_update")
func handles(object: Object) -> bool:
if object is AnimatedAtlasTexture:
resource = object as AnimatedAtlasTexture
return object is AnimatedAtlasTexture
func _update() -> void:
if resource is AnimatedAtlasTexture:
if resource.atlas:
var img := resource.atlas.get_data()
var size = img.get_size()
var frame_size = size / Vector2(resource.h_frames, resource.v_frames)
var frame = int(int(OS.get_ticks_msec() / (1000.0 / resource.fps)) % (resource.h_frames * resource.v_frames))
var frame_pos = Vector2(frame % resource.h_frames, floor(float(frame) / resource.h_frames))
resource.region = Rect2(frame_size * frame_pos, frame_size)
@Aldlevine
Copy link
Author

Also, I didn't notice earlier, but you are using this in an AnimatedTexture, which isn't right. AnimatedAtlasTexture should replace AnimatedTexture all together (not be used as frame 0). It should animate on it's own (works out of the box with sprites and ui nodes, requires the above workaround to work with tilemaps).

@ek68794998
Copy link

Yeah, when I had it without the AnimatedTexture, it was just invisible in the game so I was using that as a workaround.

The change definitely works; I can see it animated in the TileMap editor now. One thing: I had to add a class_name in the custom TileMap script like so:

image

Otherwise it wouldn't show up in my resources list.

Thanks for looking into this and updating it!

@ek68794998
Copy link

Oh, one more thing; previous_frame is never reassigned. I added this and it still works:

image

@Aldlevine
Copy link
Author

In my setup, previous_frame is reassigned in the beginning of the update function (before frame gets rewritten), but your placement should work just the same.

Also, I've updated the solution for TileMaps by creating a custom TileSet instead. The gist was updated to include animated_tile_set.gd. I believe this is a better solution.

And one more thing, you can get better in editor previewing by turning on "Editor Settings -> Interface -> Editor -> Update Continuously", but obviously this will make the editor more resource intensive.

@boruok
Copy link

boruok commented Apr 6, 2022

Changed a lil bit, maked script straightforward + added support per AtlasTexture region (if you want make mutltiple AtlasTextures from single StreamTexture)

animated_atlas_texture.gd

class_name AnimatedAtlasTexture extends AtlasTexture


export(int, 1, 100) var h_frames := 1
export(int, 1, 100) var v_frames := 1
export var fps := 10.0

var frame_position : Vector2
var frame_size : Vector2
var frame_last := 0
var frame := 0


func init() -> void:
	frame_size = region.size / Vector2(h_frames, v_frames)
	frame_position = region.position
	VisualServer.connect("frame_pre_draw", self, "_on_frame_pre_draw")


func _on_frame_pre_draw() -> void:
	if !atlas: return

	frame = int(OS.get_ticks_msec() / (1000.0 / fps)) % (h_frames * v_frames)
	region = Rect2(
		frame_position + (frame_size * Vector2(frame % h_frames, frame / h_frames)),
		frame_size)

	if frame_last != frame:
		frame_last = frame
		emit_changed()

tilemap.gd

extends TileMap


func _ready() -> void:
	for tile in tile_set.get_tiles_ids():
		var texture := tile_set.tile_get_texture(tile)

		if texture is AnimatedAtlasTexture:
			texture.init()
			texture.connect("changed", self, "_on_AnimatedAtlasTexture_changed")


func _on_AnimatedAtlasTexture_changed() -> void:
	tile_set.emit_changed()

example regions
test

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment