This commit is contained in:
Valerie 2026-01-15 13:29:33 +08:00
commit 7d4dc68196
13 changed files with 174 additions and 34 deletions

View File

@ -1,11 +1,11 @@
extends Node2D extends Node2D
func _ready() -> void: func _ready() -> void:
#$L0_S0.switch_act_by_id(1) $L0_S0.switch_act_by_id(1)
#$L1_S1.switch_act_by_id(1) #$L1_S1.switch_act_by_id(1)
#$L1_S2.switch_act_by_id(1) #$L1_S2.switch_act_by_id(1)
#$L1_S3.switch_act_by_id(1) #$L1_S3.switch_act_by_id(1)
$L1_S4.switch_act_by_id(1) #$L1_S4.switch_act_by_id(1)
#$L1_S5.switch_act_by_id(1) #$L1_S5.switch_act_by_id(1)
#$L1_S6.switch_act_by_id(1) #$L1_S6.switch_act_by_id(1)
#$L1_S7.switch_act_by_id(1) #$L1_S7.switch_act_by_id(1)

View File

@ -152,6 +152,7 @@ release_distance = 20.0
[node name="Dash" type="LimboState" parent="PlayerHSM/Normal"] [node name="Dash" type="LimboState" parent="PlayerHSM/Normal"]
unique_name_in_owner = true unique_name_in_owner = true
script = ExtResource("12_8nsdm") script = ExtResource("12_8nsdm")
dash_time = 1.0
[node name="Dead" type="LimboState" parent="PlayerHSM"] [node name="Dead" type="LimboState" parent="PlayerHSM"]
unique_name_in_owner = true unique_name_in_owner = true

View File

@ -27,6 +27,7 @@ func _setup() -> void:
func _init_handler() -> void: func _init_handler() -> void:
self.add_event_handler(&"trigger_dash",_handler_trigger_dash) ##处理Dash输入 self.add_event_handler(&"trigger_dash",_handler_trigger_dash) ##处理Dash输入
self.add_event_handler(&"trigger_external_dash",_handler_trigger_external_dash) ##处理外部触发的Dash
self.add_event_handler(&"trigger_climb",_handler_trigger_climb) ##处理瞬时的climb self.add_event_handler(&"trigger_climb",_handler_trigger_climb) ##处理瞬时的climb
self.add_event_handler(&"trigger_grap_hook",_handler_grap_hook) ##处理grap hook 的輸入 self.add_event_handler(&"trigger_grap_hook",_handler_grap_hook) ##处理grap hook 的輸入
@ -72,6 +73,14 @@ func _handler_trigger_dash() -> bool:
self.dispatch(&"want_to_dash") self.dispatch(&"want_to_dash")
return true return true
##处理外部触发的Dash如道具触发
func _handler_trigger_external_dash() -> bool:
if get_root().blackboard.get_var(&"is_dashing",false):
return false
self.dispatch(&"want_to_dash")
return true
func _handler_trigger_climb() -> bool: func _handler_trigger_climb() -> bool:
if not agent.is_on_wall(): if not agent.is_on_wall():
return false return false

View File

@ -8,3 +8,4 @@ editor_description = "此类在检测到玩家时会发出signal可以与其
collision_layer = 0 collision_layer = 0
collision_mask = 2 collision_mask = 2
script = ExtResource("1_qrafk") script = ExtResource("1_qrafk")
debug_print = true

View File

@ -1,16 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://c3mievyfhx6ni"]
[ext_resource type="PackedScene" uid="uid://d20u8tfktepxg" path="res://_props/_prefabs/collection/collection_prefab.tscn" id="1_r0qyb"]
[ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="2_igeyo"]
[sub_resource type="CircleShape2D" id="CircleShape2D_r0qyb"]
radius = 23.0
[node name="Coin" instance=ExtResource("1_r0qyb")]
[node name="CollisionShape2D" type="CollisionShape2D" parent="." index="0"]
shape = SubResource("CircleShape2D_r0qyb")
[node name="Sprite2D" type="Sprite2D" parent="." index="1"]
scale = Vector2(0.33, 0.33)
texture = ExtResource("2_igeyo")

View File

@ -0,0 +1,64 @@
extends CharacterBody2D
@onready var player_collectable_volumn: PlayerTriggerVolumn = %PlayerCollectableVolumn
@onready var hook_attract_volumn: Area2D = %HookAttractVolumn
enum State {
IDLE,
PULLED_BY_HOOK,
COLLECTED
}
var _current_state: State = State.IDLE
## 拉回速度
@export var pull_speed: float = 800.0
## 拉回目标(玩家)
var _pull_target: Node2D
func _ready() -> void:
player_collectable_volumn.player_entered.connect(_on_player_collected)
##状态管理函数
func change_state(in_state: State) -> void:
if in_state == _current_state:
return
## 退出State的逻辑
match _current_state:
State.IDLE:
pass
State.PULLED_BY_HOOK:
pass
State.COLLECTED:
pass
_current_state = in_state
## 进入State的逻辑
match in_state:
State.IDLE:
pass
State.PULLED_BY_HOOK:
pass
State.COLLECTED:
queue_free()
## 钩爪击中时调用hit_pos 是击中点hook 是钩爪实例)
func on_hook_hit(hit_pos: Vector2, hook: Hook) -> void:
# 通过钩爪组件获取发射者(玩家)
_pull_target = hook._binded_hook_comp.owner as Node2D
if _pull_target:
change_state(State.PULLED_BY_HOOK)
func _physics_process(delta: float) -> void:
if _current_state == State.PULLED_BY_HOOK and _pull_target:
var direction := (_pull_target.global_position - global_position).normalized()
velocity = direction * pull_speed
move_and_slide()
##如果玩家进入收集区域,则切换为已收集状态。
func _on_player_collected(body:CharacterBody2D) -> void:
if body is Player:
body.hsm.dispatch(&"trigger_external_dash")
change_state(State.COLLECTED)

View File

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

View File

@ -0,0 +1,44 @@
[gd_scene load_steps=7 format=3 uid="uid://degt1t2y08udg"]
[ext_resource type="Script" uid="uid://c8a7gs0h2h6vn" path="res://_props/dark_material_ball/dark_material_ball.gd" id="1_8vsnl"]
[ext_resource type="Texture2D" uid="uid://c673bap4b12fx" path="res://icon.svg" id="2_ts6gp"]
[ext_resource type="PackedScene" uid="uid://bonrls3iuhdqb" path="res://_props/_prefabs/player/player_trigger_volumn.tscn" id="3_rtv51"]
[sub_resource type="CircleShape2D" id="CircleShape2D_62vs1"]
radius = 7.0
[sub_resource type="CircleShape2D" id="CircleShape2D_j8xbm"]
radius = 10.049875
[sub_resource type="CircleShape2D" id="CircleShape2D_8vsnl"]
radius = 34.0147
[node name="DarkMaterialBall" type="CharacterBody2D" groups=["GRAPABLE"]]
collision_layer = 16
collision_mask = 2
script = ExtResource("1_8vsnl")
[node name="Sprite2D" type="Sprite2D" parent="."]
unique_name_in_owner = true
scale = Vector2(0.135, 0.135)
texture = ExtResource("2_ts6gp")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
shape = SubResource("CircleShape2D_62vs1")
[node name="PlayerCollectableVolumn" parent="." instance=ExtResource("3_rtv51")]
unique_name_in_owner = true
debug_print = false
[node name="CollisionShape2D" type="CollisionShape2D" parent="PlayerCollectableVolumn"]
shape = SubResource("CircleShape2D_j8xbm")
debug_color = Color(0.19215687, 0.6431373, 0, 0)
[node name="HookAttractVolumn" type="Area2D" parent="."]
unique_name_in_owner = true
collision_layer = 16
collision_mask = 0
[node name="CollisionShape2D" type="CollisionShape2D" parent="HookAttractVolumn"]
shape = SubResource("CircleShape2D_8vsnl")
debug_color = Color(1, 0, 0.14509805, 0)

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,8 @@
[gd_resource type="TileSet" load_steps=3 format=3 uid="uid://cup1q1upvp18h"] [gd_resource type="TileSet" load_steps=7 format=3 uid="uid://cup1q1upvp18h"]
[ext_resource type="Texture2D" uid="uid://7psxuet3jk1p" path="res://_asset/ksw/basicTile.png" id="1_ln1fl"] [ext_resource type="Texture2D" uid="uid://7psxuet3jk1p" path="res://_asset/ksw/basicTile.png" id="1_ln1fl"]
[ext_resource type="Texture2D" uid="uid://dd622t4mw5vva" path="res://_asset/ksw/basicTile01.png" id="2_mucy5"]
[ext_resource type="Texture2D" uid="uid://cwet2kw1mngmf" path="res://_asset/ksw/basicTile02.png" id="3_u6jqb"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_ln1fl"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_ln1fl"]
texture = ExtResource("1_ln1fl") texture = ExtResource("1_ln1fl")
@ -74,6 +76,17 @@ texture_region_size = Vector2i(96, 96)
3:4/0 = 0 3:4/0 = 0
1:4/0 = 0 1:4/0 = 0
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_u6jqb"]
texture = ExtResource("2_mucy5")
texture_region_size = Vector2i(96, 96)
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_wtljp"]
texture = ExtResource("3_u6jqb")
texture_region_size = Vector2i(96, 96)
10:6/0 = 0
[resource] [resource]
tile_size = Vector2i(96, 96) tile_size = Vector2i(96, 96)
sources/0 = SubResource("TileSetAtlasSource_ln1fl") sources/0 = SubResource("TileSetAtlasSource_ln1fl")
sources/1 = SubResource("TileSetAtlasSource_u6jqb")
sources/2 = SubResource("TileSetAtlasSource_wtljp")

View File

@ -13,5 +13,5 @@ width = 8.0
[node name="RayCast2D" type="RayCast2D" parent="."] [node name="RayCast2D" type="RayCast2D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
target_position = Vector2(80, 0) target_position = Vector2(80, 0)
collision_mask = 36 collision_mask = 20
collide_with_areas = true collide_with_areas = true

View File

@ -19,8 +19,8 @@ var _tween: Tween
const GRAPABLE_GROUP = &"GRAPABLE" const GRAPABLE_GROUP = &"GRAPABLE"
signal stretching_finished(reach_limit: bool, anchor_node: Node2D) signal stretching_finished(reach_limit: bool, anchor_node: Node2D)
## 钩爪击中物体信号target 是被击中的物体hit_pos 是击中点世界坐标 ## 钩爪击中物体信号target 是被击中的物体hit_pos 是击中点世界坐标hook 是钩爪实例
signal hook_hit(target: Node2D, hit_pos: Vector2) signal hook_hit(target: Node2D, hit_pos: Vector2, hook: Hook)
## ================ ## ================
## Private Field ## Private Field
@ -73,6 +73,15 @@ func end_stretching(force_end: bool = false) -> bool:
func is_stretching() -> bool: func is_stretching() -> bool:
return _is_stretching return _is_stretching
## 获取当前飞行方向(可被外部复写)
func get_stretching_dir() -> Vector2:
return _stretching_dir.normalized()
## 设置飞行方向(供外部物体修改)
func set_stretching_dir(dir: Vector2) -> void:
_stretching_dir = dir.normalized()
_dir_id = _get_direction_id(_stretching_dir, 8)
# ================= # =================
# Update # Update
# ================= # =================
@ -93,8 +102,11 @@ func _update_stretching(delta: float) -> void:
var next_length := _current_length + stretching_speed * delta var next_length := _current_length + stretching_speed * delta
next_length = min(next_length, max_length) next_length = min(next_length, max_length)
# 使用 getter 获取当前方向(允许外部复写)
var current_dir := get_stretching_dir()
# 先用「下一幀長度」做 Ray # 先用「下一幀長度」做 Ray
ray.target_position = _stretching_dir * next_length ray.target_position = current_dir * next_length
ray.force_raycast_update() ray.force_raycast_update()
# ===== 命中檢測(最高優先)===== # ===== 命中檢測(最高優先)=====
@ -104,7 +116,7 @@ func _update_stretching(delta: float) -> void:
var hit_pos := ray.get_collision_point() var hit_pos := ray.get_collision_point()
_current_length = global_position.distance_to(hit_pos) _current_length = global_position.distance_to(hit_pos)
ray.target_position = _stretching_dir * _current_length ray.target_position = current_dir * _current_length
_handle_hit(collider as Node2D, hit_pos) _handle_hit(collider as Node2D, hit_pos)
return return
@ -128,17 +140,15 @@ func _handle_hit(target: Node2D, hit_pos: Vector2) -> void:
ray.target_position = to_local(hit_pos) ray.target_position = to_local(hit_pos)
# 如果 target 有 on_hook_hit 方法,调用它 # 如果 target 有 on_hook_hit 方法,调用它(传入钩爪实例)
if target.has_method(&"on_hook_hit"): if target.has_method(&"on_hook_hit"):
target.on_hook_hit(hit_pos) target.on_hook_hit(hit_pos, self)
if target.owner.has_method(&"on_hook_hit"):
target.owner.on_hook_hit(hit_pos)
var reach_max := is_equal_approx(_current_length, max_length) var reach_max := is_equal_approx(_current_length, max_length)
var anchor := _create_anchor_on_node(target, hit_pos) var anchor := _create_anchor_on_node(target, hit_pos)
stretching_finished.emit(reach_max, anchor) stretching_finished.emit(reach_max, anchor)
hook_hit.emit(target, hit_pos, self)
## 釋放鉤爪(清理 Anchor 與狀態) ## 釋放鉤爪(清理 Anchor 與狀態)
func _release_hook() -> void: func _release_hook() -> void:

View File

@ -37,7 +37,8 @@ enabled=PackedStringArray("res://addons/reedcamera/plugin.cfg", "res://addons/re
[file_customization] [file_customization]
folder_colors={ folder_colors={
"res://_shared/": "pink" "res://_shared/": "pink",
"res://_tileset/": "green"
} }
[global_group] [global_group]
@ -96,4 +97,3 @@ grap_hook={
2d_physics/layer_3="Environment" 2d_physics/layer_3="Environment"
2d_physics/layer_4="Damage" 2d_physics/layer_4="Damage"
2d_physics/layer_5="Collectable" 2d_physics/layer_5="Collectable"
2d_physics/layer_6="OneWayPlateform"