In 2D RPG’s there are often tiles located on different layers, and invisible walls which prevent the characters from jumping off of a cliff or overhang. I would like characters to treat the cliff edge like a solid wall unless a certain key is pressed. I’ve simplified the edge-detection problem to a 2d demo(my game is 3d), but I can’t determine what movement algorithm would guarantee the same movement limitations as an invisible wall would. The problem I am facing is similar to Best way to prevent player from walking off ledge? - #37 by Swithun_Dodda, except I would like the characters to slide when they ‘hit’ the edge instead of stopping their movement alltogether.
Currently I use 3 raycasts to determine the edge positions. The rest I’ve tried to break down as a vector problem, assuming that I can find the edge’s normal, I should be able to slide the character along it. The character’s origin is point O, the intersection of the left raycast L, right raycast R, and center raycast C. The velocity is parallell to vector OC. The dark-blue line by CR + CL usually gives a reasonable estimation of which velocity the character should follow, but it still never works as smoothly as an invisible wall would.
As I said in that thread you reference - try using circular raycast array to estimate the edge/corner direction. I don’t think 3 raycasts would be enough.
Can you get intersected polygons from an area2d defining the space for raycasting? It could just be like a 2D cone.
Then you wouldnt need an array, sweep, shapecast or anything complicated. Just how do you then interpret the array of polygons …?
Perhaps you could grab the edges, then find nearest point in the list of all points (each edge is p_(i+1) - p_(i) ) then at some distance along the edge the nearest point on the extruded velocity line is the radius (or outer radius) of the boundary sphere of the player, (simple trigonometry) then, just find which of these edges has the closest point, then collide with that edge by moving along the edge - you could use the dot product to compute slide strength.
I would just use an area3d to activate a collider or disable it when the key is pressed. Set a boolean state in _physics_process(), then use signals …e.g.
func _on_body_entered_area(body):
if not (body is PlayerBody3D):
return
if the_bool_is_set:
set_flag_to_perform_action()
else:
activate_physics_collider()
Then in fact you might need another boolean that causes the process to ignore the key presses while the action is taking place.
In the code I posted above I forgot to rotate the target_vel relative to the character. After that addition, and some more maths-related tweaks it mostly works.
Here is the updated code for anyone who finds this post in the future:
func _process(delta: float) -> void:
queue_redraw()
dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
if !Input.is_action_pressed("strafe"):
# The raycasts are now placed in a Node2D container and rotated separately from the character
rayCont.rotation = dir.angle()
velocity = dir*SPEED
if too_close and dir != Vector2.ZERO and !stopped and !Input.is_action_pressed("ui_cancel"):
if wall_check():
# The angle between the character's velocity, and the wall surface
var angle_to = velocity.angle_to(v_cr)
# Allow movement away from the corner/wall
if angle_to < 0 or angle_to > PI:
pass
else:
velocity = velocity.project(v_cr.normalized())
elif corner_check():
# The angle between the character's velocity, and the wall surface
var angle_to = velocity.angle_to(v_cr - v_cl)
# Allow movement away from the corner/wall
if angle_to < 0 or angle_to > PI:
pass
else:
velocity = velocity.project((v_cr.normalized() - v_cl.normalized()).normalized())
move_and_slide()
too_close = is_too_close()
func wall_check():
var angle_diff = angle_difference(v_cr.angle(), v_cl.angle())
if abs(angle_diff) > 2.96706: #170°
return true
return false
func corner_check():
return v_cr.angle_to(v_cl) < PI - PI/6 # Returns true if the angle of the wall is less than 150.0