feature/whaletown-developer-extra-feature #19
BIN
assets/characters/crayfish_npc_256_256.png
Normal file
BIN
assets/characters/crayfish_npc_256_256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -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="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://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="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"]
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_7nixu"]
|
||||
@@ -1082,6 +1083,9 @@ position = Vector2(0, 320)
|
||||
[node name="NPC" parent="." instance=ExtResource("15_0xqio")]
|
||||
position = Vector2(-81, -64)
|
||||
|
||||
[node name="CrayfishNpc" parent="." instance=ExtResource("17_crayfish")]
|
||||
position = Vector2(88, 286)
|
||||
|
||||
[node name="DataWhaleHome" parent="." instance=ExtResource("16_m4als")]
|
||||
position = Vector2(8, -128)
|
||||
|
||||
|
||||
@@ -1,29 +1,55 @@
|
||||
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 dialogue: String = "欢迎来到WhaleTown,我是镇长范鲸晶"
|
||||
@export_multiline var dialogue: String = "欢迎来到WhaleTown,我是镇长范鲸晶"
|
||||
|
||||
func _ready():
|
||||
$Sprite2D.texture = preload("res://assets/characters/npc_286_241.png")
|
||||
$Sprite2D.hframes = 4
|
||||
$Sprite2D.vframes = 4
|
||||
|
||||
# Start Idle Animation
|
||||
if has_node("AnimationPlayer"):
|
||||
$AnimationPlayer.play("idle")
|
||||
|
||||
# Ensure interaction layer
|
||||
collision_layer = 3 # Layer 1 & 2 (Blocking)
|
||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||
|
||||
func _ready() -> void:
|
||||
# 播放场景里配置好的待机动画,让不同 NPC 可以复用同一个控制器。
|
||||
if animation_player.has_animation("idle"):
|
||||
animation_player.play("idle")
|
||||
|
||||
# 保持 NPC 可被玩家射线与角色碰撞识别。
|
||||
collision_layer = 3
|
||||
collision_mask = 3
|
||||
|
||||
func interact():
|
||||
# 处理玩家交互,展示气泡并向全局事件系统广播。
|
||||
func interact() -> void:
|
||||
show_bubble(dialogue)
|
||||
EventSystem.emit_event(EventNames.NPC_TALKED, {
|
||||
"npc": self,
|
||||
"npc_name": npc_name,
|
||||
"dialogue": dialogue
|
||||
})
|
||||
interaction_happened.emit(dialogue)
|
||||
return null
|
||||
|
||||
func show_bubble(text):
|
||||
var bubble = preload("res://scenes/ui/ChatBubble.tscn").instantiate()
|
||||
# 在 NPC 头顶生成一次性聊天气泡。
|
||||
#
|
||||
# 参数:
|
||||
# text: String - 要展示的对话内容
|
||||
func show_bubble(text: String) -> void:
|
||||
var bubble: Control = CHAT_BUBBLE_SCENE.instantiate() as Control
|
||||
if bubble == null:
|
||||
return
|
||||
add_child(bubble)
|
||||
bubble.set_text(text)
|
||||
if bubble.has_method("set_text"):
|
||||
bubble.call("set_text", text)
|
||||
|
||||
65
scenes/characters/crayfish_npc.tscn
Normal file
65
scenes/characters/crayfish_npc.tscn
Normal 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")
|
||||
}
|
||||
Reference in New Issue
Block a user