xiandie/addons/property-inspector/pro_animation_sprite2d/pro_animated_sprite.gd

214 lines
6.1 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@tool
class_name ProAnimatedSprite2D extends AnimatedSprite2D
@export var autostart := true
@export var action_configs: Array[Dictionary] = []
@export var move_configs: Array[Dictionary] = []
# 0.0 means no light
# negative value means subtract, positive value means add
@export_range(-1.0, 1.0) var light_energy := 0.0:
set(val):
light2d.energy = abs(val)
if val > 0:
light2d.blend_mode = Light2D.BLEND_MODE_ADD
else:
light2d.blend_mode = Light2D.BLEND_MODE_SUB
light_energy = val * modulate.a
@export var debug_mov_animation := ""
@export_tool_button("debug 刷新 mov 目标") var debug_mov_projection := _debug_mov_projection
const ACTION_CONFIG = {
"animation_intro": "", "intro_loop": 1, "animation_wait_time": 0.0, "animation_next": ""
}
# 参数 {duration} 目前无需求,暂且保留
const MOVE_CONFIG = {
"animation": "",
"velocity": Vector2.ZERO,
"duration": 10000000.0,
"movement_x": 0.0,
"animation_next": ""
}
static func new_move_config() -> Dictionary:
return MOVE_CONFIG.duplicate()
static func new_action_config() -> Dictionary:
return ACTION_CONFIG.duplicate()
@onready var debug_mov_onion_sprite2d = $DebugMovOnionSkinSprite2D as Sprite2D
# 从 intro 到 next 的配置信息
var auto_checkout_dict = {
# intro -> {state_config instance}
}
# 播放 animation 的同时,进行移动
var animation_mov_dict = {
# animation -> {velocity, ...}
}
var light2d := PointLight2D.new()
func _ready() -> void:
if light_energy != 0.0:
add_child(light2d)
frame_changed.connect(_on_frame_changed)
if Engine.is_editor_hint():
_debug_mov_projection()
return
_load_config()
debug_mov_onion_sprite2d.queue_free()
if autostart and animation:
# 制造一点错差
frame = randi() % sprite_frames.get_frame_count(animation)
play()
animation_changed.connect(_on_animation_start)
animation_looped.connect(_on_animation_start)
animation_finished.connect(_on_animation_finished)
animation_looped.connect(_on_animation_finished)
func _debug_mov_projection():
_load_config()
if debug_mov_animation and animation_mov_dict.has(debug_mov_animation):
var mov_config = animation_mov_dict[debug_mov_animation]
# 展示 accumulated animation 的目标位置
debug_mov_onion_sprite2d.position.x = mov_config.movement_x
debug_mov_onion_sprite2d.texture = sprite_frames.get_frame_texture(debug_mov_animation, 0)
elif debug_mov_animation:
printerr("Debug move config not found:", debug_mov_animation)
func _load_config():
# load auto_checkout_dict
for i in range(action_configs.size()):
action_configs[i].merge(ACTION_CONFIG)
var state_config = action_configs[i]
# 不必关闭循环
# if sprite_frames and sprite_frames.has_animation(state_config.animation_intro):
# intro 的 animation 关闭循环
# sprite_frames.set_animation_loop(state_config.animation_intro, false)
auto_checkout_dict[state_config.animation_intro] = action_configs[i]
# load animation_frame_offsets
for i in range(move_configs.size()):
move_configs[i].merge(MOVE_CONFIG)
var move_config = move_configs[i]
if sprite_frames and sprite_frames.has_animation(move_config.animation):
animation_mov_dict[move_config.animation] = move_config
func _on_frame_changed():
if light_energy == 0.0:
return
var texture = sprite_frames.get_frame_texture(animation, frame)
if not texture:
return
light2d.texture = texture
light_energy = light_energy * modulate.a
func _reset_loop(intro: String):
for c in action_configs:
if c.animation_intro == intro:
auto_checkout_dict[intro].intro_loop = max(1, c.intro_loop)
func _on_animation_finished() -> void:
var intro = animation
if auto_checkout_dict.has(intro):
auto_checkout_dict[intro].intro_loop -= 1
if auto_checkout_dict[intro].intro_loop <= 0:
# reset loop
_reset_loop(intro)
var wait_time = auto_checkout_dict[intro].animation_wait_time
var next = auto_checkout_dict[intro].animation_next
if wait_time > 0:
pause()
get_tree().create_timer(wait_time).timeout.connect(play.bind(next))
else:
play(next)
var velocity := Vector2.ZERO
var mov_x := 0.0
var mov_x_next_animation := ""
var accumulated_mov_x := 0.0
var prev_animation := ""
func _on_animation_start():
if prev_animation != animation:
accumulated_mov_x = 0.0
prev_animation = animation
velocity = Vector2.ZERO
mov_x = 0.0
mov_x_next_animation = ""
if not animation or not animation_mov_dict.has(animation):
return
# velocity = animation_mov_dict[animation] * speed_scale
velocity = animation_mov_dict[animation].velocity
mov_x_next_animation = animation_mov_dict[animation].animation_next
if mov_x_next_animation:
# 只有在 next animation 存在时mov_x 才有意义
mov_x = animation_mov_dict[animation].movement_x
func _physics_process(delta: float) -> void:
if Engine.is_editor_hint() or not velocity or not is_playing():
return
var diff_x = velocity.x * delta
if flip_h:
position.x -= diff_x
else:
position.x += diff_x
# 检查是否切换 animation
if mov_x != 0.0 and mov_x_next_animation:
accumulated_mov_x += diff_x
if absf(accumulated_mov_x) >= absf(mov_x):
if GlobalConfig.DEBUG:
print(
"切换 animation:", mov_x_next_animation, " accumulated_mov_x=", accumulated_mov_x
)
play(mov_x_next_animation)
if not velocity.y:
return
if flip_v:
position.y -= velocity.y * delta
else:
position.y += velocity.y * delta
# temporary set velocity
func set_animation_velocity(anim: String, v: Vector2) -> void:
if not animation_mov_dict.has(anim):
animation_mov_dict[anim] = new_move_config()
animation_mov_dict[anim].velocity = v
if GlobalConfig.DEBUG:
print("set_animation_velocity:", anim, v)
func play_with_velocity(anim: String, velocity: Vector2) -> void:
set_animation_velocity(anim, velocity)
play(anim)
# -1 means loop forever, 0 is invalid (which means no loop and return)
func play_with_loop(anim: String, loop := -1, wait := 0.0, next := "") -> void:
if loop < 0:
auto_checkout_dict.erase(anim)
sprite_frames.set_animation_loop(anim, true)
elif loop == 0:
return
else:
auto_checkout_dict[anim] = {
"animation_intro": anim,
"intro_loop": loop,
"animation_wait_time": wait,
"animation_next": next
}
# 设置完配置后播放
play(anim)