3 Commits

Author SHA1 Message Date
9d5536fd32 ui: update auth scene layout 2026-03-11 00:00:35 +08:00
3eef8c0814 art: refine crayfish npc sprite 2026-03-10 23:56:38 +08:00
fa38b75d8f feat: add crayfish npc to square map 2026-03-10 22:51:38 +08:00
5 changed files with 166 additions and 24 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=40 format=4 uid="uid://5cc0c6cpnhe8"] [gd_scene load_steps=41 format=4 uid="uid://5cc0c6cpnhe8"]
[ext_resource type="Script" uid="uid://b43tvo8cykfrq" path="res://scenes/Maps/BaseLevel.gd" id="1_m4als"] [ext_resource type="Script" uid="uid://b43tvo8cykfrq" path="res://scenes/Maps/BaseLevel.gd" id="1_m4als"]
[ext_resource type="Texture2D" uid="uid://baa5wkuyqouh6" path="res://assets/sprites/environment/standard_brick_128_128.jpg" id="1_rb5kq"] [ext_resource type="Texture2D" uid="uid://baa5wkuyqouh6" path="res://assets/sprites/environment/standard_brick_128_128.jpg" id="1_rb5kq"]
@@ -19,6 +19,7 @@
[ext_resource type="PackedScene" uid="uid://bvfyllcy5fi8o" path="res://scenes/Maps/datawhale_home.tscn" id="16_m4als"] [ext_resource type="PackedScene" uid="uid://bvfyllcy5fi8o" path="res://scenes/Maps/datawhale_home.tscn" id="16_m4als"]
[ext_resource type="PackedScene" uid="uid://rdmrm7j4iokr" path="res://scenes/prefabs/items/notice_board.tscn" id="16_rixdf"] [ext_resource type="PackedScene" uid="uid://rdmrm7j4iokr" path="res://scenes/prefabs/items/notice_board.tscn" id="16_rixdf"]
[ext_resource type="Script" uid="uid://d2od22agputjt" path="res://scenes/prefabs/items/WelcomeBoard.gd" id="16_u1t8b"] [ext_resource type="Script" uid="uid://d2od22agputjt" path="res://scenes/prefabs/items/WelcomeBoard.gd" id="16_u1t8b"]
[ext_resource type="PackedScene" path="res://scenes/characters/crayfish_npc.tscn" id="17_crayfish"]
[ext_resource type="Script" uid="uid://rlkavptfhr4y" path="res://scenes/Maps/DoorTeleport.gd" id="18_0xqio"] [ext_resource type="Script" uid="uid://rlkavptfhr4y" path="res://scenes/Maps/DoorTeleport.gd" id="18_0xqio"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_7nixu"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_7nixu"]
@@ -1082,6 +1083,9 @@ position = Vector2(0, 320)
[node name="NPC" parent="." instance=ExtResource("15_0xqio")] [node name="NPC" parent="." instance=ExtResource("15_0xqio")]
position = Vector2(-81, -64) position = Vector2(-81, -64)
[node name="CrayfishNpc" parent="." instance=ExtResource("17_crayfish")]
position = Vector2(88, 286)
[node name="DataWhaleHome" parent="." instance=ExtResource("16_m4als")] [node name="DataWhaleHome" parent="." instance=ExtResource("16_m4als")]
position = Vector2(8, -128) position = Vector2(8, -128)

View File

@@ -1,29 +1,55 @@
extends CharacterBody2D extends CharacterBody2D
signal interaction_happened(text) # ============================================================================
# 文件名: NPCController.gd
# 作用: 通用 NPC 控制器,负责角色待机表现与交互对话
#
# 主要功能:
# - 播放 NPC 待机动画
# - 响应玩家射线交互
# - 触发聊天气泡与 NPC 对话事件
#
# 依赖: EventSystem, EventNames, ChatBubble
# 作者: Codex
# 创建时间: 2026-03-10
# ============================================================================
signal interaction_happened(text: String)
const CHAT_BUBBLE_SCENE: PackedScene = preload("res://scenes/ui/ChatBubble.tscn")
@export var npc_name: String = "NPC" @export var npc_name: String = "NPC"
@export var dialogue: String = "欢迎来到WhaleTown我是镇长范鲸晶" @export_multiline var dialogue: String = "欢迎来到WhaleTown我是镇长范鲸晶"
func _ready(): @onready var animation_player: AnimationPlayer = $AnimationPlayer
$Sprite2D.texture = preload("res://assets/characters/npc_286_241.png")
$Sprite2D.hframes = 4
$Sprite2D.vframes = 4
# Start Idle Animation func _ready() -> void:
if has_node("AnimationPlayer"): # 播放场景里配置好的待机动画,让不同 NPC 可以复用同一个控制器。
$AnimationPlayer.play("idle") if animation_player.has_animation("idle"):
animation_player.play("idle")
# Ensure interaction layer # 保持 NPC 可被玩家射线与角色碰撞识别。
collision_layer = 3 # Layer 1 & 2 (Blocking) collision_layer = 3
collision_mask = 3 collision_mask = 3
func interact(): # 处理玩家交互,展示气泡并向全局事件系统广播。
func interact() -> void:
show_bubble(dialogue) show_bubble(dialogue)
EventSystem.emit_event(EventNames.NPC_TALKED, {
"npc": self,
"npc_name": npc_name,
"dialogue": dialogue
})
interaction_happened.emit(dialogue) interaction_happened.emit(dialogue)
return null
func show_bubble(text): # 在 NPC 头顶生成一次性聊天气泡。
var bubble = preload("res://scenes/ui/ChatBubble.tscn").instantiate() #
# 参数:
# text: String - 要展示的对话内容
func show_bubble(text: String) -> void:
var bubble: Control = CHAT_BUBBLE_SCENE.instantiate() as Control
if bubble == null:
return
add_child(bubble) add_child(bubble)
bubble.set_text(text) if bubble.has_method("set_text"):
bubble.call("set_text", text)

View File

@@ -0,0 +1,65 @@
[gd_scene load_steps=7 format=3]
[ext_resource type="Texture2D" path="res://assets/characters/crayfish_npc_256_256.png" id="1_texture"]
[ext_resource type="Script" path="res://scenes/characters/NPCController.gd" id="2_script"]
[sub_resource type="RectangleShape2D" id="1_shape"]
size = Vector2(44, 22)
[sub_resource type="Animation" id="2_reset"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Sprite2D:frame")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [0]
}
[sub_resource type="Animation" id="3_idle"]
resource_name = "idle"
length = 1.2
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Sprite2D:frame")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0.0333333, 0.26666665, 0.4666667, 0.8, 1),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1),
"update": 1,
"values": [2, 1, 0, 4, 5]
}
[sub_resource type="AnimationLibrary" id="4_library"]
_data = {
&"RESET": SubResource("2_reset"),
&"idle": SubResource("3_idle")
}
[node name="CrayfishNpc" type="CharacterBody2D"]
script = ExtResource("2_script")
npc_name = "虾小满"
dialogue = "欢迎来到 WhaleTown我是虾小满负责看着喷泉边的水路和码头消息。想找热闹的地方顺着水边走就对啦。"
[node name="Sprite2D" type="Sprite2D" parent="."]
texture = ExtResource("1_texture")
hframes = 4
vframes = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
light_mask = 5
visibility_layer = 5
shape = SubResource("1_shape")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
&"": SubResource("4_library")
}

View File

@@ -9,6 +9,10 @@
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_cjyup"] [sub_resource type="StyleBoxTexture" id="StyleBoxTexture_cjyup"]
texture = ExtResource("4_lnw07") texture = ExtResource("4_lnw07")
content_margin_left = 16.0
content_margin_right = 16.0
content_margin_top = 10.0
content_margin_bottom = 10.0
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hover"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hover"]
bg_color = Color(0.3, 0.6, 0.9, 1) bg_color = Color(0.3, 0.6, 0.9, 1)
@@ -130,9 +134,9 @@ anchor_top = 0.5
anchor_right = 0.5 anchor_right = 0.5
anchor_bottom = 0.5 anchor_bottom = 0.5
offset_left = -140.0 offset_left = -140.0
offset_top = -185.5 offset_top = -205.5
offset_right = 140.0 offset_right = 140.0
offset_bottom = 185.5 offset_bottom = 165.5
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
@@ -160,6 +164,10 @@ layout_mode = 2
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="TopPadding" type="Control" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm"]
custom_minimum_size = Vector2(0, 8)
layout_mode = 2
[node name="UsernameContainer" type="VBoxContainer" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm"] [node name="UsernameContainer" type="VBoxContainer" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm"]
layout_mode = 2 layout_mode = 2
@@ -195,6 +203,8 @@ theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_colors/selection_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/selection_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup") theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "用户名/手机/邮箱" placeholder_text = "用户名/手机/邮箱"
[node name="PasswordContainer" type="VBoxContainer" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm"] [node name="PasswordContainer" type="VBoxContainer" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm"]
@@ -226,9 +236,13 @@ text = "密码不能为空"
horizontal_alignment = 2 horizontal_alignment = 2
[node name="PasswordInput" type="LineEdit" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm/PasswordContainer"] [node name="PasswordInput" type="LineEdit" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm/PasswordContainer"]
custom_minimum_size = Vector2(0, 48)
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "请输入密码" placeholder_text = "请输入密码"
secret = true secret = true
@@ -265,10 +279,14 @@ horizontal_alignment = 2
layout_mode = 2 layout_mode = 2
[node name="VerificationInput" type="LineEdit" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm/VerificationContainer/VerificationInputContainer"] [node name="VerificationInput" type="LineEdit" parent="CenterContainer/LoginPanel/VBoxContainer/LoginForm/VerificationContainer/VerificationInputContainer"]
custom_minimum_size = Vector2(0, 48)
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "请输入6位验证码" placeholder_text = "请输入6位验证码"
max_length = 6 max_length = 6
@@ -314,16 +332,25 @@ text = "密码登录"
[node name="BottomLinks" type="HBoxContainer" parent="CenterContainer/LoginPanel/VBoxContainer"] [node name="BottomLinks" type="HBoxContainer" parent="CenterContainer/LoginPanel/VBoxContainer"]
layout_mode = 2 layout_mode = 2
alignment = 1 alignment = 1
theme_override_constants/separation = 16
[node name="ForgotPassword" type="Button" parent="CenterContainer/LoginPanel/VBoxContainer/BottomLinks"] [node name="ForgotPassword" type="Button" parent="CenterContainer/LoginPanel/VBoxContainer/BottomLinks"]
custom_minimum_size = Vector2(0, 28)
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0.12, 0.4, 0.7, 1)
text = "忘记密码?" theme_override_colors/font_hover_color = Color(0.2, 0.5, 0.8, 1)
theme_override_colors/font_pressed_color = Color(0.1, 0.35, 0.6, 1)
theme_override_font_sizes/font_size = 14
text = "忘记密码?"
flat = true flat = true
[node name="RegisterLink" type="Button" parent="CenterContainer/LoginPanel/VBoxContainer/BottomLinks"] [node name="RegisterLink" type="Button" parent="CenterContainer/LoginPanel/VBoxContainer/BottomLinks"]
custom_minimum_size = Vector2(0, 28)
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0.12, 0.4, 0.7, 1)
theme_override_colors/font_hover_color = Color(0.2, 0.5, 0.8, 1)
theme_override_colors/font_pressed_color = Color(0.1, 0.35, 0.6, 1)
theme_override_font_sizes/font_size = 14
text = "注册居民身份" text = "注册居民身份"
flat = true flat = true
@@ -391,9 +418,13 @@ text = "用户名不能为空"
horizontal_alignment = 2 horizontal_alignment = 2
[node name="UsernameInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/UsernameContainer"] [node name="UsernameInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/UsernameContainer"]
custom_minimum_size = Vector2(0, 48)
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "请输入用户名" placeholder_text = "请输入用户名"
[node name="EmailContainer" type="VBoxContainer" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm"] [node name="EmailContainer" type="VBoxContainer" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm"]
@@ -425,9 +456,13 @@ text = "邮箱不能为空"
horizontal_alignment = 2 horizontal_alignment = 2
[node name="EmailInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/EmailContainer"] [node name="EmailInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/EmailContainer"]
custom_minimum_size = Vector2(0, 48)
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "请输入邮箱地址" placeholder_text = "请输入邮箱地址"
[node name="PasswordContainer" type="VBoxContainer" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm"] [node name="PasswordContainer" type="VBoxContainer" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm"]
@@ -459,9 +494,13 @@ text = "密码不能为空"
horizontal_alignment = 2 horizontal_alignment = 2
[node name="PasswordInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/PasswordContainer"] [node name="PasswordInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/PasswordContainer"]
custom_minimum_size = Vector2(0, 48)
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "请输入密码(至少8位)" placeholder_text = "请输入密码(至少8位)"
secret = true secret = true
@@ -494,9 +533,13 @@ text = "确认密码不能为空"
horizontal_alignment = 2 horizontal_alignment = 2
[node name="ConfirmInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/ConfirmContainer"] [node name="ConfirmInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/ConfirmContainer"]
custom_minimum_size = Vector2(0, 48)
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "请再次输入密码" placeholder_text = "请再次输入密码"
secret = true secret = true
@@ -532,10 +575,14 @@ horizontal_alignment = 2
layout_mode = 2 layout_mode = 2
[node name="VerificationInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/VerificationContainer/VerificationInputContainer"] [node name="VerificationInput" type="LineEdit" parent="CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/VerificationContainer/VerificationInputContainer"]
custom_minimum_size = Vector2(0, 48)
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_colors/font_color = Color(0, 0, 0, 1)
theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_placeholder_color = Color(0.5, 0.5, 0.5, 1)
theme_override_styles/normal = SubResource("StyleBoxTexture_cjyup")
theme_override_styles/focus = SubResource("StyleBoxTexture_cjyup")
theme_override_font_sizes/font_size = 16
placeholder_text = "请输入6位验证码" placeholder_text = "请输入6位验证码"
max_length = 6 max_length = 6