move collision handling to a separate subclass to reduce code repetition in trajectory logic
This commit is contained in:
@@ -73,61 +73,6 @@ static func process_movement(
|
||||
return new_velocity
|
||||
|
||||
|
||||
static func process_collision(
|
||||
new_velocity: Vector2,
|
||||
hit_slope: bool,
|
||||
hit_ceiling: bool,
|
||||
hit_wall: bool,
|
||||
floor_angle: float,
|
||||
wall_bounce_velocity_loss: float,
|
||||
) -> Vector2:
|
||||
if hit_slope:
|
||||
new_velocity = (
|
||||
new_velocity.slide(Vector2.UP.rotated(floor_angle))
|
||||
* wall_bounce_velocity_loss
|
||||
)
|
||||
if hit_ceiling:
|
||||
new_velocity.y = 0
|
||||
if hit_wall:
|
||||
new_velocity.x = -new_velocity.x
|
||||
if hit_ceiling or hit_wall:
|
||||
new_velocity.x *= wall_bounce_velocity_loss
|
||||
return new_velocity
|
||||
|
||||
|
||||
static func collide_shape(
|
||||
pos: Vector2,
|
||||
direction: Vector2,
|
||||
shape: Shape2D,
|
||||
space_state: PhysicsDirectSpaceState2D,
|
||||
max_results: int = 1
|
||||
) -> Array[Vector2]:
|
||||
var floor_query := PhysicsShapeQueryParameters2D.new()
|
||||
floor_query.collision_mask = 1
|
||||
floor_query.transform = Transform2D(0, pos)
|
||||
floor_query.motion = direction
|
||||
floor_query.shape = shape
|
||||
floor_query.margin = -0.08
|
||||
return space_state.collide_shape(floor_query, max_results)
|
||||
|
||||
|
||||
static func raycast_floor_angle(
|
||||
collisions: Array[Vector2], space_state: PhysicsDirectSpaceState2D
|
||||
) -> float:
|
||||
var floor_angle: float = 0
|
||||
for i in range(1, collisions.size(), 2):
|
||||
var from := collisions[i] + Vector2.UP
|
||||
var to := collisions[i] + Vector2.DOWN
|
||||
var query := PhysicsRayQueryParameters2D.create(from, to)
|
||||
var result := space_state.intersect_ray(query)
|
||||
if result["normal"] != Vector2.ZERO:
|
||||
var normal := result["normal"] as Vector2
|
||||
floor_angle = -normal.angle_to(Vector2.UP)
|
||||
if is_zero_approx(floor_angle):
|
||||
return floor_angle
|
||||
return floor_angle
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
instance = self
|
||||
Debugger.add_event("collided")
|
||||
@@ -176,7 +121,34 @@ func _physics_process(delta: float) -> void:
|
||||
# apply velocity
|
||||
move_and_collide(_velocity * delta)
|
||||
|
||||
_handle_collision()
|
||||
# handle collisions
|
||||
var handler := CollisionHandler.new()
|
||||
handler.is_on_floor = _is_on_floor
|
||||
handler.is_on_ceiling = _is_on_ceiling
|
||||
handler.is_on_wall = _is_on_wall
|
||||
handler.floor_angle = _floor_angle
|
||||
handler.jump_released = _jump_released
|
||||
handler.velocity = _velocity
|
||||
|
||||
var space_state := get_world_2d().direct_space_state
|
||||
handler.handle(
|
||||
space_state, _shape.global_position, _shape.shape, _wall_bounce_velocity_loss
|
||||
)
|
||||
|
||||
_is_on_floor = handler.is_on_floor
|
||||
_is_on_ceiling = handler.is_on_ceiling
|
||||
_is_on_wall = handler.is_on_wall
|
||||
_floor_angle = handler.floor_angle
|
||||
_jump_released = handler.jump_released
|
||||
_velocity = handler.velocity
|
||||
|
||||
if handler.hit_floor:
|
||||
_trajectory.visible = false
|
||||
collided.emit(SIDE_BOTTOM, _velocity)
|
||||
if handler.hit_ceiling:
|
||||
collided.emit(SIDE_TOP, _velocity)
|
||||
if handler.hit_wall:
|
||||
collided.emit(SIDE_LEFT if _velocity.x > 0 else SIDE_RIGHT, _velocity)
|
||||
|
||||
queue_redraw()
|
||||
|
||||
@@ -236,57 +208,107 @@ func _gather_input(delta: float) -> void:
|
||||
_is_on_wall = false
|
||||
|
||||
|
||||
func _handle_collision() -> void:
|
||||
var is_on_floor_prev := _is_on_floor
|
||||
var is_on_ceiling_prev := _is_on_ceiling
|
||||
var is_on_wall_prev := _is_on_wall
|
||||
class CollisionHandler:
|
||||
var is_on_floor: bool
|
||||
var is_on_ceiling: bool
|
||||
var is_on_wall: bool
|
||||
var floor_angle: float
|
||||
var jump_released: bool
|
||||
var velocity: Vector2
|
||||
|
||||
var space_state := get_world_2d().direct_space_state
|
||||
var floor_collisions := collide_shape(
|
||||
_shape.global_position, Vector2.DOWN, _shape.shape, space_state, 3
|
||||
)
|
||||
var ceiling_collisions := collide_shape(
|
||||
_shape.global_position, Vector2.UP, _shape.shape, space_state
|
||||
)
|
||||
var wall_collisions := collide_shape(
|
||||
_shape.global_position, Vector2.LEFT, _shape.shape, space_state
|
||||
)
|
||||
if not wall_collisions:
|
||||
wall_collisions = collide_shape(
|
||||
_shape.global_position, Vector2.RIGHT, _shape.shape, space_state
|
||||
var hit_floor: bool = false
|
||||
var hit_slope: bool = false
|
||||
var hit_ceiling: bool = false
|
||||
var hit_wall: bool = false
|
||||
|
||||
func handle(
|
||||
space_state: PhysicsDirectSpaceState2D,
|
||||
shape_pos: Vector2,
|
||||
shape: Shape2D,
|
||||
wall_bounce_velocity_loss: float
|
||||
) -> void:
|
||||
var is_on_floor_prev := is_on_floor
|
||||
var is_on_ceiling_prev := is_on_ceiling
|
||||
var is_on_wall_prev := is_on_wall
|
||||
|
||||
var floor_collisions := _collide_shape(
|
||||
shape_pos, Vector2.DOWN, shape, space_state, 3
|
||||
)
|
||||
var ceiling_collisions := _collide_shape(
|
||||
shape_pos, Vector2.UP, shape, space_state
|
||||
)
|
||||
var wall_collisions := _collide_shape(
|
||||
shape_pos, Vector2.LEFT, shape, space_state
|
||||
)
|
||||
if not wall_collisions:
|
||||
wall_collisions = _collide_shape(
|
||||
shape_pos, Vector2.RIGHT, shape, space_state
|
||||
)
|
||||
|
||||
is_on_floor = floor_collisions.size() > 0
|
||||
is_on_ceiling = ceiling_collisions.size() > 0
|
||||
is_on_wall = wall_collisions.size() > 0
|
||||
|
||||
var is_on_floor_changed := not is_on_floor_prev and is_on_floor
|
||||
if is_on_floor_changed:
|
||||
floor_angle = _get_floor_angle(floor_collisions, space_state)
|
||||
|
||||
hit_floor = is_on_floor_changed and is_zero_approx(floor_angle)
|
||||
hit_slope = is_on_floor_changed and not is_zero_approx(floor_angle)
|
||||
hit_ceiling = not is_on_ceiling_prev and is_on_ceiling
|
||||
hit_wall = (
|
||||
not is_on_wall_prev
|
||||
and is_on_wall
|
||||
and (not is_on_floor or jump_released)
|
||||
and not is_zero_approx(velocity.x)
|
||||
)
|
||||
|
||||
_is_on_floor = floor_collisions.size() > 0
|
||||
_is_on_ceiling = ceiling_collisions.size() > 0
|
||||
_is_on_wall = wall_collisions.size() > 0
|
||||
_process_collision(
|
||||
wall_bounce_velocity_loss,
|
||||
)
|
||||
|
||||
var is_on_floor_changed := not is_on_floor_prev and _is_on_floor
|
||||
if is_on_floor_changed:
|
||||
_floor_angle = raycast_floor_angle(floor_collisions, space_state)
|
||||
func _process_collision(
|
||||
wall_bounce_velocity_loss: float,
|
||||
) -> void:
|
||||
if hit_slope:
|
||||
velocity = (
|
||||
velocity.slide(Vector2.UP.rotated(floor_angle))
|
||||
* wall_bounce_velocity_loss
|
||||
)
|
||||
if hit_ceiling:
|
||||
velocity.y = 0
|
||||
if hit_wall:
|
||||
velocity.x = -velocity.x
|
||||
if hit_ceiling or hit_wall:
|
||||
velocity.x *= wall_bounce_velocity_loss
|
||||
|
||||
var hit_floor := is_on_floor_changed and is_zero_approx(_floor_angle)
|
||||
var hit_slope := is_on_floor_changed and not is_zero_approx(_floor_angle)
|
||||
var hit_ceiling := not is_on_ceiling_prev and _is_on_ceiling
|
||||
var hit_wall := (
|
||||
not is_on_wall_prev
|
||||
and _is_on_wall
|
||||
and (not _is_on_floor or _jump_released)
|
||||
and not is_zero_approx(_velocity.x)
|
||||
)
|
||||
func _collide_shape(
|
||||
pos: Vector2,
|
||||
direction: Vector2,
|
||||
shape: Shape2D,
|
||||
space_state: PhysicsDirectSpaceState2D,
|
||||
max_results: int = 1
|
||||
) -> Array[Vector2]:
|
||||
var floor_query := PhysicsShapeQueryParameters2D.new()
|
||||
floor_query.collision_mask = 1
|
||||
floor_query.transform = Transform2D(0, pos)
|
||||
floor_query.motion = direction
|
||||
floor_query.shape = shape
|
||||
floor_query.margin = -0.08
|
||||
return space_state.collide_shape(floor_query, max_results)
|
||||
|
||||
_velocity = process_collision(
|
||||
_velocity,
|
||||
hit_slope,
|
||||
hit_ceiling,
|
||||
hit_wall,
|
||||
_floor_angle,
|
||||
_wall_bounce_velocity_loss,
|
||||
)
|
||||
|
||||
if hit_floor:
|
||||
_trajectory.visible = false
|
||||
collided.emit(SIDE_BOTTOM, _velocity)
|
||||
if hit_ceiling:
|
||||
collided.emit(SIDE_TOP, _velocity)
|
||||
if hit_wall:
|
||||
collided.emit(SIDE_LEFT if _velocity.x > 0 else SIDE_RIGHT, _velocity)
|
||||
func _get_floor_angle(
|
||||
collisions: Array[Vector2], space_state: PhysicsDirectSpaceState2D
|
||||
) -> float:
|
||||
var angle: float = 0
|
||||
for i in range(1, collisions.size(), 2):
|
||||
var from := collisions[i] + Vector2.UP
|
||||
var to := collisions[i] + Vector2.DOWN
|
||||
var query := PhysicsRayQueryParameters2D.create(from, to)
|
||||
var result := space_state.intersect_ray(query)
|
||||
if result["normal"] != Vector2.ZERO:
|
||||
var normal := result["normal"] as Vector2
|
||||
angle = -normal.angle_to(Vector2.UP)
|
||||
if is_zero_approx(angle):
|
||||
return angle
|
||||
return angle
|
||||
|
||||
@@ -244,61 +244,32 @@ func _draw() -> void:
|
||||
var motion := (pos - pos_prev) * motion_proportion
|
||||
pos = pos_prev + motion
|
||||
|
||||
var is_on_floor_prev := is_on_floor
|
||||
var is_on_ceiling_prev := is_on_ceiling
|
||||
var is_on_wall_prev := is_on_wall
|
||||
|
||||
var shape_pos := to_global(pos) + Vector2.UP * (_player_size.y / 2.0)
|
||||
|
||||
var floor_collisions := Player.collide_shape(
|
||||
shape_pos, Vector2.DOWN, _player_shape, space_state, 3
|
||||
)
|
||||
var ceiling_collisions := Player.collide_shape(
|
||||
shape_pos, Vector2.UP, _player_shape, space_state
|
||||
)
|
||||
var wall_collisions := Player.collide_shape(
|
||||
shape_pos, Vector2.LEFT, _player_shape, space_state
|
||||
)
|
||||
if not wall_collisions:
|
||||
wall_collisions = Player.collide_shape(
|
||||
shape_pos, Vector2.RIGHT, _player_shape, space_state
|
||||
)
|
||||
var handler := Player.CollisionHandler.new()
|
||||
handler.is_on_floor = is_on_floor
|
||||
handler.is_on_ceiling = is_on_ceiling
|
||||
handler.is_on_wall = is_on_wall
|
||||
handler.floor_angle = floor_angle
|
||||
handler.jump_released = jump_released
|
||||
handler.velocity = velocity
|
||||
|
||||
is_on_floor = floor_collisions.size() > 0
|
||||
is_on_ceiling = ceiling_collisions.size() > 0
|
||||
is_on_wall = wall_collisions.size() > 0
|
||||
|
||||
var is_on_floor_changed := not is_on_floor_prev and is_on_floor
|
||||
if is_on_floor_changed:
|
||||
floor_angle = Player.raycast_floor_angle(floor_collisions, space_state)
|
||||
|
||||
var hit_floor := is_on_floor_changed and is_zero_approx(floor_angle)
|
||||
var hit_slope := is_on_floor_changed and not is_zero_approx(floor_angle)
|
||||
var hit_ceiling := not is_on_ceiling_prev and is_on_ceiling
|
||||
var hit_wall := (
|
||||
not is_on_wall_prev
|
||||
and is_on_wall
|
||||
and (not is_on_floor or jump_released)
|
||||
and not is_zero_approx(velocity.x)
|
||||
handler.handle(
|
||||
space_state, shape_pos, _player_shape, _wall_bounce_velocity_loss
|
||||
)
|
||||
|
||||
velocity = (
|
||||
Player
|
||||
. process_collision(
|
||||
velocity,
|
||||
hit_slope,
|
||||
hit_ceiling,
|
||||
hit_wall,
|
||||
floor_angle,
|
||||
_wall_bounce_velocity_loss,
|
||||
)
|
||||
)
|
||||
is_on_floor = handler.is_on_floor
|
||||
is_on_ceiling = handler.is_on_ceiling
|
||||
is_on_wall = handler.is_on_wall
|
||||
floor_angle = handler.floor_angle
|
||||
jump_released = handler.jump_released
|
||||
velocity = handler.velocity
|
||||
|
||||
if hit_floor or hit_slope:
|
||||
if handler.hit_floor or handler.hit_slope:
|
||||
_draw_collision_hit(pos, SIDE_BOTTOM, _hit_color)
|
||||
if hit_ceiling:
|
||||
if handler.hit_ceiling:
|
||||
_draw_collision_hit(pos, SIDE_TOP, _hit_color)
|
||||
if hit_wall:
|
||||
if handler.hit_wall:
|
||||
_draw_collision_hit(
|
||||
pos, SIDE_LEFT if velocity.x > 0 else SIDE_RIGHT, _hit_color
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user