xiandie/scene/entity/ux/ppt_helper.gd

213 lines
5.9 KiB
GDScript

@tool
class_name PPTHelper
extends Node2D
signal slide_changed(new_slide_index: int)
signal presentation_finished
## 转场配置数组,按顺序播放
@export var configs: Array[PPTHelperConfig] = []
## 如果为 true, 将在场景加载完成后自动播放
@export var auto_start := true
## 如果为 true, 播放到结尾后将从头开始
@export var loop := false
## 当前激活的配置索引
var current_index := -1
## 动画播放锁,防止在转场时重复触发
var is_playing := false
## 编辑器工具按钮:自动配置 Slides
@export_tool_button("自动配置 Slides") var auto_config_slides = _auto_config_slides
func _ready() -> void:
if Engine.is_editor_hint():
return
if auto_start:
play()
## 开始或重新开始播放
## @param start_index 从指定的索引开始播放
func play(start_index := 0) -> void:
if configs.is_empty():
printerr("PPTHelper: No configs to play.")
return
if start_index < 0 or start_index >= configs.size():
printerr("PPTHelper: 'start_index' is out of bounds.")
return
# 重置状态
is_playing = false
current_index = -1
# 隐藏所有页面,准备从头开始
for config in configs:
if config and config.canvas_item:
var node := get_node_or_null(config.canvas_item) as CanvasItem
if is_instance_valid(node):
node.modulate.a = 0.0
# 开始播放指定的页面
_transition_to(start_index)
## 手动切换到下一个页面
func next() -> void:
if is_playing or current_index == -1:
return
var next_index := current_index + 1
if next_index >= configs.size():
if loop:
next_index = 0
else:
presentation_finished.emit()
print("PPTHelper: Reached the end of the presentation.")
return
_transition_to(next_index)
## 跳转到指定的页面
func goto(target_index: int) -> void:
if target_index < 0 or target_index >= configs.size():
printerr("PPTHelper: 'target_index' for goto() is out of bounds.")
return
if target_index == current_index or is_playing:
return
_transition_to(target_index)
# 核心转换函数
func _transition_to(target_index: int) -> void:
if is_playing:
return
is_playing = true
var from_index := current_index
var tween: Tween = create_tween()
tween.set_parallel(true) # 并行处理 in 和 out 动画
# --- 处理上一页的 "Ease Out" ---
if from_index != -1:
var from_config := configs[from_index]
var from_node := get_node_or_null(from_config.canvas_item) as CanvasItem
if is_instance_valid(from_node):
if from_config.ease_out_duration > 0:
(
tween
. tween_property(from_node, "modulate:a", 0.0, from_config.ease_out_duration)
. set_trans(_get_trans_type(from_config.ease_out_trans_type))
. set_ease(_get_ease_type(from_config.ease_out_type))
)
else:
from_node.modulate.a = 0.0
else:
printerr("PPTHelper: Invalid node for ease out at index %d." % from_index)
# --- 处理目标页的 "Ease In" ---
var target_config := configs[target_index]
var target_node := get_node_or_null(target_config.canvas_item) as CanvasItem
if is_instance_valid(target_node):
if target_config.ease_in_duration > 0:
target_node.modulate.a = 0.0 # 确保开始时透明
(
tween
. tween_property(target_node, "modulate:a", 1.0, target_config.ease_in_duration)
. set_trans(_get_trans_type(target_config.ease_in_trans_type))
. set_ease(_get_ease_type(target_config.ease_in_type))
)
else:
target_node.modulate.a = 1.0
else:
printerr("PPTHelper: Invalid node for ease in at index %d." % target_index)
is_playing = false # 如果节点无效,立即解锁
return
tween.tween_interval(0.001) # 最小延迟防止无效
await tween.finished
# --- 更新状态并检查自动播放下一个 ---
current_index = target_index
is_playing = false
slide_changed.emit(current_index) # 发射信号
# 如果配置为自动转到下一个,等待指定时间后执行
if target_config.ease_out_to_next:
if target_config.ease_out_wait_time > 0.0:
await Util.wait(target_config.ease_out_wait_time)
next()
else:
presentation_finished.emit()
# 辅助函数:将字符串映射到 Tween.TransitionType
func _get_trans_type(trans_str: String) -> Tween.TransitionType:
match trans_str.to_lower():
"linear":
return Tween.TRANS_LINEAR
"sine":
return Tween.TRANS_SINE
"cubic":
return Tween.TRANS_CUBIC
"quart":
return Tween.TRANS_QUART
"quint":
return Tween.TRANS_QUINT
"expo":
return Tween.TRANS_EXPO
"elastic":
return Tween.TRANS_ELASTIC
"bounce":
return Tween.TRANS_BOUNCE
"spring":
return Tween.TRANS_SPRING
_:
return Tween.TRANS_LINEAR
# 辅助函数:将字符串映射到 Tween.EaseType
func _get_ease_type(ease_str: String) -> Tween.EaseType:
match ease_str.to_lower():
"in":
return Tween.EASE_IN
"out":
return Tween.EASE_OUT
"inout":
return Tween.EASE_IN_OUT
_:
return Tween.EASE_IN_OUT
#### TOOL BUTTON
func _auto_config_slides() -> void:
if not Engine.is_editor_hint():
return # 只在编辑器中运行
# 清空现有配置
configs = []
# 获取所有直接子节点
var children := get_children()
if children.is_empty():
printerr("PPTHelper: No child nodes found for auto-configuration.")
notify_property_list_changed()
return
# 从下到上配置(反转数组)
children.reverse()
var configured_count := 0
for child in children:
if child is CanvasItem:
var config := PPTHelperConfig.new()
config.canvas_item = get_path_to(child) # 设置 NodePath 到子节点
# 其他属性使用默认值(如 ease_in_duration = 0.0 等),可手动调整
configs.append(config)
configured_count += 1
else:
print("PPTHelper: Skipping non-CanvasItem child: %s" % child.name)
if configured_count > 0:
print("PPTHelper: Auto-configured %d slides from child nodes." % configured_count)
else:
printerr("PPTHelper: No valid CanvasItem children found for auto-configuration.")
# 刷新编辑器属性以显示更新
notify_property_list_changed()