Files
agame/ui/character_select.gd
T
2026-10-03 17:26:42 +08:00

99 lines
3.0 KiB
GDScript

extends Control
## 角色选择:预览模型、内置表情 atlas 与玩家本地表情库。
signal confirmed
signal back_pressed
@onready var _model: Node3D = %Model
@onready var _camera: Camera3D = %PreviewCamera
@onready var _rig: FaceRig = %FaceRig
@onready var _paint_panel: Control = $PaintPanel
@onready var _expression_row: HFlowContainer = %PresetRow
var _expression_buttons: Array[TextureButton] = []
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
_camera.look_at(Vector3(0.0, 0.45, 0.0), Vector3.UP)
%ConfirmButton.pressed.connect(_on_confirm)
%BackButton.pressed.connect(_on_back)
%DrawButton.pressed.connect(_on_draw)
_paint_panel.finished.connect(_refresh_expression_list)
_paint_panel.cancelled.connect(_refresh_expression_list)
_refresh_expression_list()
# 将模型选择180度
if (_model != null):
_model.rotate_y(PI)
## 内置表情用 AtlasTexture 显示对应 UV 小格;本地表情直接显示各自 PNG。
func _refresh_expression_list() -> void:
for child in _expression_row.get_children():
child.free()
_expression_buttons.clear()
for entry in _rig.get_expression_catalog():
var card := VBoxContainer.new()
card.custom_minimum_size = Vector2(96, 116)
card.add_theme_constant_override("separation", 4)
var skin := ColorRect.new()
skin.color = _rig.skin_color
skin.mouse_filter = Control.MOUSE_FILTER_IGNORE
skin.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var button := TextureButton.new()
button.custom_minimum_size = Vector2(96, 96)
button.texture_normal = entry["thumbnail"] as Texture2D
button.ignore_texture_size = true
button.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
button.modulate = Color.WHITE if str(entry["id"]) == _rig.selected_expression_id else Color(0.62, 0.62, 0.62)
button.set_meta("expression_id", str(entry["id"]))
button.pressed.connect(_on_expression_pressed.bind(str(entry["id"])))
_expression_buttons.append(button)
var preview := Control.new()
preview.custom_minimum_size = Vector2(96, 96)
preview.add_child(skin)
preview.add_child(button)
var label := Label.new()
label.text = str(entry["name"])
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
label.custom_minimum_size = Vector2(96, 16)
card.add_child(preview)
card.add_child(label)
_expression_row.add_child(card)
func _on_expression_pressed(expression_id: String) -> void:
if _rig.select_expression(expression_id):
_refresh_expression_highlight(expression_id)
func _refresh_expression_highlight(expression_id: String) -> void:
for button in _expression_buttons:
button.modulate = Color.WHITE if str(button.get_meta("expression_id", "")) == expression_id else Color(0.62, 0.62, 0.62)
## 「自己画」从无底图起手。面板会先快照,取消时还原表情 ID 和笔画。
func _on_draw() -> void:
_paint_panel.open(_rig, true)
_refresh_expression_list()
func _on_confirm() -> void:
_rig.save()
confirmed.emit()
func _on_back() -> void:
back_pressed.emit()