213 lines
5.9 KiB
GDScript3
213 lines
5.9 KiB
GDScript3
|
@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()
|