2025-01-23 13:13:40 +00:00
|
|
|
|
@tool
|
|
|
|
|
class_name ProAnimatedSprite2D extends AnimatedSprite2D
|
|
|
|
|
|
|
|
|
|
@export var autostart := true
|
|
|
|
|
@export var action_configs: Array[Dictionary] = []
|
|
|
|
|
@export var move_configs: Array[Dictionary] = []
|
2025-01-25 05:57:28 +00:00
|
|
|
|
# 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
|
2025-01-26 13:11:38 +00:00
|
|
|
|
light_energy = val * modulate.a
|
2025-06-17 09:10:48 +00:00
|
|
|
|
@export var debug_mov_animation := ""
|
|
|
|
|
@export_tool_button("debug 刷新 mov 目标") var debug_mov_projection := _debug_mov_projection
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
2025-01-23 14:21:53 +00:00
|
|
|
|
const ACTION_CONFIG = {
|
|
|
|
|
"animation_intro": "", "intro_loop": 1, "animation_wait_time": 0.0, "animation_next": ""
|
|
|
|
|
}
|
2025-01-31 10:44:30 +00:00
|
|
|
|
# 参数 {duration} 目前无需求,暂且保留
|
2025-06-17 09:10:48 +00:00
|
|
|
|
const MOVE_CONFIG = {
|
|
|
|
|
"animation": "",
|
|
|
|
|
"velocity": Vector2.ZERO,
|
|
|
|
|
"duration": 10000000.0,
|
|
|
|
|
"movement_x": 0.0,
|
|
|
|
|
"animation_next": ""
|
|
|
|
|
}
|
2025-01-23 14:21:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func new_move_config() -> Dictionary:
|
|
|
|
|
return MOVE_CONFIG.duplicate()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func new_action_config() -> Dictionary:
|
|
|
|
|
return ACTION_CONFIG.duplicate()
|
|
|
|
|
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
2025-06-17 09:10:48 +00:00
|
|
|
|
@onready var debug_mov_onion_sprite2d = $DebugMovOnionSkinSprite2D as Sprite2D
|
|
|
|
|
|
2025-01-25 05:57:28 +00:00
|
|
|
|
# 从 intro 到 next 的配置信息
|
|
|
|
|
var auto_checkout_dict = {
|
|
|
|
|
# intro -> {state_config instance}
|
|
|
|
|
}
|
|
|
|
|
# 播放 animation 的同时,进行移动
|
2025-06-17 09:10:48 +00:00
|
|
|
|
var animation_mov_dict = {
|
|
|
|
|
# animation -> {velocity, ...}
|
2025-01-25 05:57:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var light2d := PointLight2D.new()
|
|
|
|
|
|
|
|
|
|
|
2025-01-23 13:13:40 +00:00
|
|
|
|
func _ready() -> void:
|
2025-01-25 05:57:28 +00:00
|
|
|
|
if light_energy != 0.0:
|
|
|
|
|
add_child(light2d)
|
|
|
|
|
frame_changed.connect(_on_frame_changed)
|
2025-01-23 13:13:40 +00:00
|
|
|
|
if Engine.is_editor_hint():
|
2025-06-17 09:10:48 +00:00
|
|
|
|
_debug_mov_projection()
|
2025-01-23 13:13:40 +00:00
|
|
|
|
return
|
|
|
|
|
_load_config()
|
2025-06-17 09:10:48 +00:00
|
|
|
|
debug_mov_onion_sprite2d.queue_free()
|
2025-01-23 13:13:40 +00:00
|
|
|
|
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)
|
2025-06-17 09:10:48 +00:00
|
|
|
|
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:
|
2025-06-18 03:05:22 +00:00
|
|
|
|
push_warning("Debug move config not found:", debug_mov_animation)
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _load_config():
|
|
|
|
|
# load auto_checkout_dict
|
|
|
|
|
for i in range(action_configs.size()):
|
2025-01-23 14:21:53 +00:00
|
|
|
|
action_configs[i].merge(ACTION_CONFIG)
|
2025-01-23 13:13:40 +00:00
|
|
|
|
var state_config = action_configs[i]
|
2025-01-24 14:19:17 +00:00
|
|
|
|
# 不必关闭循环
|
|
|
|
|
# if sprite_frames and sprite_frames.has_animation(state_config.animation_intro):
|
|
|
|
|
# intro 的 animation 关闭循环
|
|
|
|
|
# sprite_frames.set_animation_loop(state_config.animation_intro, false)
|
2025-01-23 13:13:40 +00:00
|
|
|
|
auto_checkout_dict[state_config.animation_intro] = action_configs[i]
|
|
|
|
|
# load animation_frame_offsets
|
|
|
|
|
for i in range(move_configs.size()):
|
2025-01-23 14:21:53 +00:00
|
|
|
|
move_configs[i].merge(MOVE_CONFIG)
|
2025-01-23 13:13:40 +00:00
|
|
|
|
var move_config = move_configs[i]
|
|
|
|
|
if sprite_frames and sprite_frames.has_animation(move_config.animation):
|
2025-06-17 09:10:48 +00:00
|
|
|
|
animation_mov_dict[move_config.animation] = move_config
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
2025-01-26 13:11:38 +00:00
|
|
|
|
|
2025-01-25 05:57:28 +00:00
|
|
|
|
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
|
2025-01-26 13:11:38 +00:00
|
|
|
|
light_energy = light_energy * modulate.a
|
|
|
|
|
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
|
|
|
|
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
|
2025-06-17 09:10:48 +00:00
|
|
|
|
var mov_x := 0.0
|
|
|
|
|
var mov_x_next_animation := ""
|
|
|
|
|
var accumulated_mov_x := 0.0
|
|
|
|
|
var prev_animation := ""
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_animation_start():
|
2025-06-17 09:10:48 +00:00
|
|
|
|
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):
|
2025-01-23 13:13:40 +00:00
|
|
|
|
return
|
2025-06-17 09:10:48 +00:00
|
|
|
|
# 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
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _physics_process(delta: float) -> void:
|
2025-01-25 05:57:28 +00:00
|
|
|
|
if Engine.is_editor_hint() or not velocity or not is_playing():
|
2025-01-23 13:13:40 +00:00
|
|
|
|
return
|
2025-06-17 09:10:48 +00:00
|
|
|
|
var diff_x = velocity.x * delta
|
2025-01-26 13:11:38 +00:00
|
|
|
|
if flip_h:
|
2025-06-17 09:10:48 +00:00
|
|
|
|
position.x -= diff_x
|
2025-01-26 13:11:38 +00:00
|
|
|
|
else:
|
2025-06-17 09:10:48 +00:00
|
|
|
|
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)
|
2025-01-26 13:11:38 +00:00
|
|
|
|
if not velocity.y:
|
|
|
|
|
return
|
|
|
|
|
if flip_v:
|
|
|
|
|
position.y -= velocity.y * delta
|
|
|
|
|
else:
|
|
|
|
|
position.y += velocity.y * delta
|
2025-01-23 13:13:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# temporary set velocity
|
|
|
|
|
func set_animation_velocity(anim: String, v: Vector2) -> void:
|
2025-06-17 09:10:48 +00:00
|
|
|
|
if not animation_mov_dict.has(anim):
|
|
|
|
|
animation_mov_dict[anim] = new_move_config()
|
|
|
|
|
animation_mov_dict[anim].velocity = v
|
2025-01-23 13:13:40 +00:00
|
|
|
|
if GlobalConfig.DEBUG:
|
|
|
|
|
print("set_animation_velocity:", anim, v)
|
2025-01-24 14:19:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|