133 lines
3.7 KiB
GDScript3
133 lines
3.7 KiB
GDScript3
|
extends RigidBody2D
|
||
|
|
||
|
@export_group("Movement Properties")
|
||
|
@export var speed := 100.0
|
||
|
@export var turn_speed := 25.0
|
||
|
|
||
|
@export_group("Vision Properties")
|
||
|
@export var ray : RayCast2D
|
||
|
@export_flags_2d_physics var player_collision_layer := 2
|
||
|
@export var max_view_distance := 120.0
|
||
|
@export_range(0.0, 360.0) var angle_cone_of_vision := 120.0
|
||
|
@export_range(0, 72) var sweeping_steps := 34.0
|
||
|
@export var debug_vision := false
|
||
|
|
||
|
@export_group("Behaviour Properties")
|
||
|
@export var follow_keep_distance := 30.0
|
||
|
|
||
|
enum STATES {
|
||
|
IDLE,
|
||
|
TARGETING
|
||
|
}
|
||
|
|
||
|
var state : STATES
|
||
|
var target_positons : Array
|
||
|
var target_distance : float
|
||
|
|
||
|
func _ready():
|
||
|
# Set Raycast into view Direction
|
||
|
ray.target_position = Vector2(max_view_distance, 0)
|
||
|
pass
|
||
|
|
||
|
func swipe_view() -> Array:
|
||
|
# Set Raycast to starting position
|
||
|
ray.rotation_degrees = -angle_cone_of_vision / 2
|
||
|
|
||
|
# Create Array to Store Angles of Hits
|
||
|
var hit_angles : Array
|
||
|
|
||
|
# Clear Player Positions Array
|
||
|
target_positons.clear()
|
||
|
# Reset Targets distance to Infinity
|
||
|
target_distance = INF
|
||
|
|
||
|
for step in sweeping_steps + 1:
|
||
|
# Check Ray
|
||
|
ray.force_raycast_update()
|
||
|
if ray.is_colliding():
|
||
|
# Get Collider
|
||
|
var collider = ray.get_collider()
|
||
|
var collider_class = collider.get_class()
|
||
|
var collision_layer : int
|
||
|
|
||
|
# Get Collision Layer
|
||
|
if collider_class == "TileMap":
|
||
|
collider = collider as TileMap
|
||
|
collision_layer = collider.tile_set.get_physics_layer_collision_layer(0)
|
||
|
else:
|
||
|
collision_layer = collider.get_collision_layer()
|
||
|
|
||
|
# Break if No relevant Collision
|
||
|
if collision_layer & player_collision_layer > 0:
|
||
|
# Checking Collision Layer
|
||
|
hit_angles.append(ray.rotation_degrees)
|
||
|
target_positons.append(to_local(ray.get_collision_point()))
|
||
|
|
||
|
# Check distance
|
||
|
target_distance = min(target_distance, ray.get_collision_point().distance_to(self.global_position))
|
||
|
|
||
|
# Rotate Ray
|
||
|
ray.rotation_degrees += angle_cone_of_vision / sweeping_steps
|
||
|
return hit_angles
|
||
|
|
||
|
func look_at_player(delta : float) -> float:
|
||
|
# Get All Angles at which the Player can be seen
|
||
|
var hit_angles = swipe_view()
|
||
|
|
||
|
# If The Player cant be seen, return and do nothing
|
||
|
if hit_angles.size() <= 0:
|
||
|
state = STATES.IDLE
|
||
|
return 0
|
||
|
state = STATES.TARGETING
|
||
|
|
||
|
# Calculate Average Angle Of Player
|
||
|
var average_angle : float
|
||
|
for angle in hit_angles:
|
||
|
average_angle += angle
|
||
|
average_angle /= hit_angles.size()
|
||
|
|
||
|
# Rotate towards Player, but limit it by rotation speed
|
||
|
self.rotation_degrees += clampf(average_angle, -2 * PI * delta * turn_speed, 2 * PI * delta * turn_speed)
|
||
|
return average_angle
|
||
|
|
||
|
func follow_player(angle : float, delta : float):
|
||
|
if(target_distance > follow_keep_distance):
|
||
|
self.apply_central_force(Vector2.RIGHT.rotated(self.rotation).rotated(deg_to_rad(angle)) * speed * delta * 60)
|
||
|
pass
|
||
|
|
||
|
|
||
|
func _physics_process(delta: float):
|
||
|
var angle := look_at_player(delta)
|
||
|
match state:
|
||
|
STATES.TARGETING:
|
||
|
follow_player(angle, delta)
|
||
|
STATES.IDLE:
|
||
|
# Do Idle Stuff
|
||
|
print("Dam, Nice a pause, time for a Coffee")
|
||
|
pass
|
||
|
|
||
|
func _process(delta: float):
|
||
|
if debug_vision:
|
||
|
queue_redraw()
|
||
|
pass
|
||
|
|
||
|
func _draw():
|
||
|
# Draw a Debug Arc in case that Debug Vision is enabled
|
||
|
if debug_vision:
|
||
|
|
||
|
# Draw All Ray Positions
|
||
|
var angle = -angle_cone_of_vision / 2
|
||
|
for step in sweeping_steps + 1:
|
||
|
self.draw_line(Vector2(), Vector2(max_view_distance,0).rotated(deg_to_rad(angle)), Color.DARK_RED, 0.5, false)
|
||
|
angle += angle_cone_of_vision / sweeping_steps
|
||
|
|
||
|
# Draw Ray Arc
|
||
|
self.draw_arc(Vector2(), max_view_distance, deg_to_rad(-angle_cone_of_vision / 2), deg_to_rad(angle_cone_of_vision / 2), sweeping_steps + 1, Color.RED, 0.5, false)
|
||
|
|
||
|
# Draw A Circle Around the Player if hes seen
|
||
|
for pos in target_positons:
|
||
|
self.draw_circle(pos, 1, Color.RED)
|
||
|
|
||
|
pass
|
||
|
|