godot-plateformer/addons/reedcomponent/locomotion/wall_detector.gd

123 lines
4.2 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name WallDetector extends Node2D
@onready var up_r: RayCast2D = $UpRayCast2D
@onready var mid_r: RayCast2D = $MidRayCast2D
@onready var down_r: RayCast2D = $DownRayCast2D
var flip_h: bool = false:
set(value):
if flip_h == value:
return
else:
flip_h = value
flip(flip_h)
return
signal wall_state_changed(enter_wall: bool)
signal up_ray_state_changed(enter_wall: bool)
signal mid_ray_state_changed(enter_wall: bool)
signal down_ray_state_changed(enter_wall: bool)
var is_on_wall: bool = false : set = _change_wall_state
var up_ray_on_wall: bool = false:
set(value):
if up_ray_on_wall == value:
return
else:
up_ray_on_wall = value
up_ray_state_changed.emit(value)
var mid_ray_on_wall: bool = false:
set(value):
if mid_ray_on_wall == value:
return
else:
mid_ray_on_wall = value
mid_ray_state_changed.emit(value)
var down_ray_on_wall: bool = false:
set(value):
if down_ray_on_wall == value:
return
else:
down_ray_on_wall = value
down_ray_state_changed.emit(value)
func _physics_process(delta: float) -> void:
var n1 = up_r.get_collision_normal() as Vector2
var n2 = mid_r.get_collision_normal() as Vector2
var n3 = down_r.get_collision_normal() as Vector2
##更新其他的射线的OnWall状态
up_ray_on_wall = up_r.is_colliding() and is_nearly_vertical_plane(n1)
mid_ray_on_wall = mid_r.is_colliding() and is_nearly_vertical_plane(n2)
down_ray_on_wall = down_r.is_colliding() and is_nearly_vertical_plane(n3)
var all_collid = up_r.is_colliding() and mid_r.is_colliding() and down_r.is_colliding() as bool
var vertical_plane = is_nearly_vertical_plane(n1) and is_nearly_vertical_plane(n2) and is_nearly_vertical_plane(n3) as bool
is_on_wall = all_collid and vertical_plane
##修改wall state的函数如果需要改就在这里改
func _change_wall_state(inis_on_wall: bool) -> void:
if inis_on_wall: #如果在wall上的状态为真则检测现在的状态是不是在wall上如果不是则发送状态改变
if is_on_wall:
return
else:
wall_state_changed.emit(true)
is_on_wall = true
else: #如果在wall上的状态为false则检测现在的状态是不是不在wall上如果不是则发送状态改变
if not is_on_wall:
return
else:
wall_state_changed.emit(false)
is_on_wall = false
##修改ray state的函数如果需要改就在这里改
func _change_ray_state(inis_on_wall: bool,field_name: StringName,signal_name:StringName) -> void:
var current_state = get(field_name)
if inis_on_wall:
if current_state: return
else:
emit_signal(signal_name,true)
set(field_name,true)
else:
if not current_state: return
else:
emit_signal(signal_name,false)
set(field_name,false)
func flip(flip: bool) -> void:
var r = Vector2(-1,1) if flip else Vector2(1,1)
up_r.scale = r
mid_r.scale = r
down_r.scale = r
##Helper函数判断是否是几乎垂直的面
func is_nearly_vertical_plane(normal: Vector2) -> bool:
# 定义一个容忍度/阈值,例如 0.1 (表示角度偏离 90 度最多约 5.7 度)
# 90 度的法线夹角意味着点积为 0。
# 我们要找的是法线接近水平的情况,即法线与 Y 轴(垂直)的点积接近 0。
# 垂直向量 (Y 轴)
var world_up = Vector2.UP # (0, -1) 或 Vector2.DOWN (0, 1),取决于你的坐标系
# 为了代码健壮性,我们检查法线与垂直方向的点积的绝对值是否小于一个很小的阈值。
# 点积接近 0 => 向量(法线)和垂直向量接近正交(即法线接近水平)。
var dot_product_with_vertical = abs(normal.dot(world_up))
# 设定一个阈值 (epsilon),例如 0.1
# 如果点积的绝对值很小(接近 0说明法线与世界Y轴接近垂直即平面接近水平
# **更正:如果点积接近 0说明法线与Y轴垂直即平面是水平的。**
# 我们要找的是“几乎垂直的平面”,这意味着法线接近水平方向 (X 轴)
var world_right = Vector2.RIGHT # (1, 0)
var dot_product_with_horizontal = abs(normal.dot(world_right))
const VERTICAL_PLANE_THRESHOLD = 0.9 # 阈值设为接近 1
# 如果法线与水平向量的点积绝对值很大(接近 1则平面接近垂直
return dot_product_with_horizontal >= VERTICAL_PLANE_THRESHOLD