feat: 增加NPC范鲸晶
- player场景增加RayCast2D - 增加npc场景 - 增加NPC对话气泡
This commit is contained in:
BIN
assets/characters/npc_286_241.png
Normal file
BIN
assets/characters/npc_286_241.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
40
assets/characters/npc_286_241.png.import
Normal file
40
assets/characters/npc_286_241.png.import
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://brko2ik6t6ib5"
|
||||||
|
path="res://.godot/imported/npc_286_241.png-dfe6daef11d0f27f7902e69d6057828f.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/characters/npc_286_241.png"
|
||||||
|
dest_files=["res://.godot/imported/npc_286_241.png-dfe6daef11d0f27f7902e69d6057828f.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=36 format=4 uid="uid://5cc0c6cpnhe8"]
|
[gd_scene load_steps=37 format=4 uid="uid://5cc0c6cpnhe8"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://c5ml4722ptwp2" path="res://scenes/Maps/BaseLevel.gd" id="1_m4als"]
|
[ext_resource type="Script" uid="uid://c5ml4722ptwp2" 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"]
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
[ext_resource type="Texture2D" uid="uid://5r75q24ww18f" path="res://assets/sprites/environment/river2_512_512.png" id="6_ff6l1"]
|
[ext_resource type="Texture2D" uid="uid://5r75q24ww18f" path="res://assets/sprites/environment/river2_512_512.png" id="6_ff6l1"]
|
||||||
[ext_resource type="Texture2D" uid="uid://d3w3fncsm32oi" path="res://assets/sprites/environment/deck_384_167.png" id="13_tct6u"]
|
[ext_resource type="Texture2D" uid="uid://d3w3fncsm32oi" path="res://assets/sprites/environment/deck_384_167.png" id="13_tct6u"]
|
||||||
[ext_resource type="Texture2D" uid="uid://j0twhfkpj15i" path="res://assets/sprites/environment/deck_512_164.png" id="14_m4als"]
|
[ext_resource type="Texture2D" uid="uid://j0twhfkpj15i" path="res://assets/sprites/environment/deck_512_164.png" id="14_m4als"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://npc2282a2new" path="res://scenes/characters/npc.tscn" id="15_0xqio"]
|
||||||
[ext_resource type="Texture2D" uid="uid://blre1srim52hs" path="res://assets/sprites/environment/deck_512_282.png" id="15_2rqka"]
|
[ext_resource type="Texture2D" uid="uid://blre1srim52hs" path="res://assets/sprites/environment/deck_512_282.png" id="15_2rqka"]
|
||||||
[ext_resource type="PackedScene" uid="uid://dscbaqkb1klwl" path="res://scenes/Maps/community.tscn" id="16_0xqio"]
|
[ext_resource type="PackedScene" uid="uid://dscbaqkb1klwl" path="res://scenes/Maps/community.tscn" id="16_0xqio"]
|
||||||
[ext_resource type="PackedScene" uid="uid://vq5qgk3k6t7e" path="res://scenes/Maps/fountain.tscn" id="16_2rqka"]
|
[ext_resource type="PackedScene" uid="uid://vq5qgk3k6t7e" path="res://scenes/Maps/fountain.tscn" id="16_2rqka"]
|
||||||
@@ -1088,6 +1089,9 @@ position = Vector2(0, 320)
|
|||||||
[node name="DataWhaleHome" parent="." instance=ExtResource("16_m4als")]
|
[node name="DataWhaleHome" parent="." instance=ExtResource("16_m4als")]
|
||||||
position = Vector2(8, -128)
|
position = Vector2(8, -128)
|
||||||
|
|
||||||
|
[node name="NPC" parent="." instance=ExtResource("15_0xqio")]
|
||||||
|
position = Vector2(-88, -88)
|
||||||
|
|
||||||
[node name="DefaultSpawn" type="Marker2D" parent="."]
|
[node name="DefaultSpawn" type="Marker2D" parent="."]
|
||||||
position = Vector2(647, 500)
|
position = Vector2(647, 500)
|
||||||
|
|
||||||
|
|||||||
29
scenes/characters/NPCController.gd
Normal file
29
scenes/characters/NPCController.gd
Normal 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)
|
||||||
1
scenes/characters/NPCController.gd.uid
Normal file
1
scenes/characters/NPCController.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dy3uf1rlu4h1u
|
||||||
@@ -9,6 +9,7 @@ const MOVE_SPEED = 200.0
|
|||||||
# 节点引用
|
# 节点引用
|
||||||
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
@onready var animation_player: AnimationPlayer = $AnimationPlayer
|
||||||
@onready var sprite: Sprite2D = $Sprite2D
|
@onready var sprite: Sprite2D = $Sprite2D
|
||||||
|
@onready var ray_cast: RayCast2D = $RayCast2D
|
||||||
|
|
||||||
var last_direction := "down"
|
var last_direction := "down"
|
||||||
|
|
||||||
@@ -20,6 +21,11 @@ func _ready() -> void:
|
|||||||
if animation_player.has_animation("idle"):
|
if animation_player.has_animation("idle"):
|
||||||
animation_player.play("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:
|
func _check_spawn_position() -> void:
|
||||||
var spawn_pos = SceneManager.get_next_scene_position()
|
var spawn_pos = SceneManager.get_next_scene_position()
|
||||||
if spawn_pos != null:
|
if spawn_pos != null:
|
||||||
@@ -27,6 +33,14 @@ func _check_spawn_position() -> void:
|
|||||||
|
|
||||||
func _physics_process(delta: float) -> void:
|
func _physics_process(delta: float) -> void:
|
||||||
_handle_movement(delta)
|
_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:
|
func _handle_movement(_delta: float) -> void:
|
||||||
# 获取移动向量 (参考 docs/02-开发规范/输入映射配置.md)
|
# 获取移动向量 (参考 docs/02-开发规范/输入映射配置.md)
|
||||||
@@ -59,13 +73,17 @@ func _update_animation_state(direction: Vector2) -> void:
|
|||||||
if abs(direction.x) > abs(direction.y):
|
if abs(direction.x) > abs(direction.y):
|
||||||
if direction.x > 0:
|
if direction.x > 0:
|
||||||
last_direction = "right"
|
last_direction = "right"
|
||||||
|
ray_cast.target_position = Vector2(60, 0)
|
||||||
else:
|
else:
|
||||||
last_direction = "left"
|
last_direction = "left"
|
||||||
|
ray_cast.target_position = Vector2(-60, 0)
|
||||||
else:
|
else:
|
||||||
if direction.y > 0:
|
if direction.y > 0:
|
||||||
last_direction = "down"
|
last_direction = "down"
|
||||||
|
ray_cast.target_position = Vector2(0, 60)
|
||||||
else:
|
else:
|
||||||
last_direction = "up"
|
last_direction = "up"
|
||||||
|
ray_cast.target_position = Vector2(0, -60)
|
||||||
|
|
||||||
animation_player.play("walk_" + last_direction)
|
animation_player.play("walk_" + last_direction)
|
||||||
|
|
||||||
|
|||||||
64
scenes/characters/npc.tscn
Normal file
64
scenes/characters/npc.tscn
Normal 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")
|
||||||
|
}
|
||||||
@@ -175,3 +175,5 @@ libraries = {
|
|||||||
|
|
||||||
[node name="Camera2D" type="Camera2D" parent="."]
|
[node name="Camera2D" type="Camera2D" parent="."]
|
||||||
zoom = Vector2(2, 2)
|
zoom = Vector2(2, 2)
|
||||||
|
|
||||||
|
[node name="RayCast2D" type="RayCast2D" parent="."]
|
||||||
|
|||||||
9
scenes/ui/ChatBubble.gd
Normal file
9
scenes/ui/ChatBubble.gd
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
extends Control
|
||||||
|
|
||||||
|
@onready var label = $PanelContainer/Label
|
||||||
|
|
||||||
|
func set_text(text):
|
||||||
|
label.text = text
|
||||||
|
# Destroy after 5 seconds
|
||||||
|
await get_tree().create_timer(5.0).timeout
|
||||||
|
queue_free()
|
||||||
1
scenes/ui/ChatBubble.gd.uid
Normal file
1
scenes/ui/ChatBubble.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b4aorojcbwkmb
|
||||||
42
scenes/ui/ChatBubble.tscn
Normal file
42
scenes/ui/ChatBubble.tscn
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
[gd_scene load_steps=3 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://b4aorojcbwkmb" path="res://scenes/ui/ChatBubble.gd" id="1_script"]
|
||||||
|
|
||||||
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bubble_modern"]
|
||||||
|
bg_color = Color(1, 1, 1, 0.9)
|
||||||
|
corner_radius_top_left = 5
|
||||||
|
corner_radius_top_right = 5
|
||||||
|
corner_radius_bottom_right = 5
|
||||||
|
corner_radius_bottom_left = 5
|
||||||
|
shadow_color = Color(0, 0, 0, 0.2)
|
||||||
|
shadow_size = 2
|
||||||
|
|
||||||
|
[node name="ChatBubble" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 0
|
||||||
|
script = ExtResource("1_script")
|
||||||
|
|
||||||
|
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 7
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_left = -75.0
|
||||||
|
offset_top = -60.0
|
||||||
|
offset_right = 75.0
|
||||||
|
offset_bottom = -20.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
theme_override_styles/panel = SubResource("StyleBoxFlat_bubble_modern")
|
||||||
|
|
||||||
|
[node name="Label" type="Label" parent="PanelContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_colors/font_color = Color(0.1, 0.1, 0.1, 1)
|
||||||
|
theme_override_font_sizes/font_size = 8
|
||||||
|
text = "..."
|
||||||
|
horizontal_alignment = 1
|
||||||
|
vertical_alignment = 1
|
||||||
|
autowrap_mode = 3
|
||||||
|
custom_minimum_size = Vector2(150, 0)
|
||||||
Reference in New Issue
Block a user