feat: 增加NPC范鲸晶

- player场景增加RayCast2D
- 增加npc场景
- 增加NPC对话气泡
This commit is contained in:
2026-01-11 00:10:45 +08:00
parent ed7d89e39d
commit 8a5a4a0005
11 changed files with 211 additions and 1 deletions

View File

@@ -0,0 +1,29 @@
extends CharacterBody2D
signal interaction_happened(text)
@export var npc_name: String = "NPC"
@export 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)
collision_mask = 3
func interact():
show_bubble(dialogue)
interaction_happened.emit(dialogue)
return null
func show_bubble(text):
var bubble = preload("res://scenes/ui/ChatBubble.tscn").instantiate()
add_child(bubble)
bubble.set_text(text)

View File

@@ -0,0 +1 @@
uid://dy3uf1rlu4h1u

View File

@@ -9,6 +9,7 @@ const MOVE_SPEED = 200.0
# 节点引用
@onready var animation_player: AnimationPlayer = $AnimationPlayer
@onready var sprite: Sprite2D = $Sprite2D
@onready var ray_cast: RayCast2D = $RayCast2D
var last_direction := "down"
@@ -19,6 +20,11 @@ func _ready() -> void:
# 播放初始动画
if animation_player.has_animation("idle"):
animation_player.play("idle")
# Initialize RayCast
ray_cast.add_exception(self) # Ignore local player
ray_cast.enabled = true
ray_cast.target_position = Vector2(0, 60)
func _check_spawn_position() -> void:
var spawn_pos = SceneManager.get_next_scene_position()
@@ -27,6 +33,14 @@ func _check_spawn_position() -> void:
func _physics_process(delta: float) -> void:
_handle_movement(delta)
_handle_interaction()
func _handle_interaction() -> void:
if Input.is_action_just_pressed("interact"):
if ray_cast.is_colliding():
var collider = ray_cast.get_collider()
if collider and collider.has_method("interact"):
collider.interact()
func _handle_movement(_delta: float) -> void:
# 获取移动向量 (参考 docs/02-开发规范/输入映射配置.md)
@@ -59,13 +73,17 @@ func _update_animation_state(direction: Vector2) -> void:
if abs(direction.x) > abs(direction.y):
if direction.x > 0:
last_direction = "right"
ray_cast.target_position = Vector2(60, 0)
else:
last_direction = "left"
ray_cast.target_position = Vector2(-60, 0)
else:
if direction.y > 0:
last_direction = "down"
ray_cast.target_position = Vector2(0, 60)
else:
last_direction = "up"
ray_cast.target_position = Vector2(0, -60)
animation_player.play("walk_" + last_direction)

View File

@@ -0,0 +1,64 @@
[gd_scene load_steps=7 format=3 uid="uid://npc2282a2new"]
[ext_resource type="Texture2D" uid="uid://brko2ik6t6ib5" path="res://assets/characters/npc_286_241.png" id="1_2r34a"]
[ext_resource type="Script" uid="uid://dy3uf1rlu4h1u" path="res://scenes/characters/NPCController.gd" id="1_script"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_npc"]
size = Vector2(48, 24)
[sub_resource type="Animation" id="Animation_2r34a"]
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="Animation_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="AnimationLibrary_npc"]
_data = {
&"RESET": SubResource("Animation_2r34a"),
&"idle": SubResource("Animation_idle")
}
[node name="NPC" type="CharacterBody2D"]
position = Vector2(-8, 0)
script = ExtResource("1_script")
[node name="Sprite2D" type="Sprite2D" parent="."]
texture = ExtResource("1_2r34a")
hframes = 4
vframes = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
light_mask = 5
visibility_layer = 5
shape = SubResource("RectangleShape2D_npc")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
&"": SubResource("AnimationLibrary_npc")
}

View File

@@ -175,3 +175,5 @@ libraries = {
[node name="Camera2D" type="Camera2D" parent="."]
zoom = Vector2(2, 2)
[node name="RayCast2D" type="RayCast2D" parent="."]