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-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-01-23 14:21:53 +00:00
|
|
|
const MOVE_CONFIG = {"animation": "", "velocity": Vector2.ZERO, "duration": 10000000.0}
|
|
|
|
|
|
|
|
|
|
|
|
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-01-25 05:57:28 +00:00
|
|
|
# 从 intro 到 next 的配置信息
|
|
|
|
var auto_checkout_dict = {
|
|
|
|
# intro -> {state_config instance}
|
|
|
|
}
|
|
|
|
# 播放 animation 的同时,进行移动
|
|
|
|
var animation_velocity = {
|
|
|
|
# animation -> velocity
|
|
|
|
}
|
|
|
|
|
|
|
|
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():
|
|
|
|
return
|
|
|
|
_load_config()
|
|
|
|
if autostart and animation:
|
|
|
|
# 制造一点错差
|
|
|
|
frame = randi() % sprite_frames.get_frame_count(animation)
|
|
|
|
play()
|
|
|
|
animation_finished.connect(_on_animation_finished)
|
2025-01-24 14:19:17 +00:00
|
|
|
animation_looped.connect(_on_animation_finished)
|
2025-01-23 13:13:40 +00:00
|
|
|
animation_changed.connect(_on_animation_start)
|
|
|
|
animation_looped.connect(_on_animation_start)
|
|
|
|
|
|
|
|
|
|
|
|
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):
|
|
|
|
animation_velocity[move_config.animation] = move_config.velocity
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
func _on_animation_start():
|
|
|
|
if not animation or not animation_velocity.has(animation):
|
|
|
|
velocity = Vector2.ZERO
|
|
|
|
return
|
|
|
|
velocity = animation_velocity[animation] * speed_scale
|
2025-01-24 14:19:17 +00:00
|
|
|
# if GlobalConfig.DEBUG:
|
|
|
|
# print("animation ", animation, " velocity=", velocity)
|
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-01-26 13:11:38 +00:00
|
|
|
if flip_h:
|
|
|
|
position.x -= velocity.x * delta
|
|
|
|
else:
|
|
|
|
position.x += velocity.x * delta
|
|
|
|
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:
|
|
|
|
animation_velocity[anim] = v
|
|
|
|
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)
|