123 lines
4.2 KiB
GDScript3
123 lines
4.2 KiB
GDScript3
|
|
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
|