mirror of
https://github.com/Relintai/godot-steering-ai-framework.git
synced 2024-11-14 04:57:19 +01:00
Add and have most demos use specialized agents
The agents auto-update themselves and can calculate their velocities. This keeps the user from having to create an update_agent function. It can also save the user from having to keep track of and update velocities at all by using the provided `apply_steering` method. Closes #15, closes #16
This commit is contained in:
parent
57f3c4a24a
commit
85784791ec
@ -1,7 +1,7 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
|
||||
var agent := GSTSteeringAgent.new()
|
||||
var agent := GSTNode2DAgent.new(self)
|
||||
var target := GSTAgentLocation.new()
|
||||
var arrive := GSTArrive.new(agent, target)
|
||||
var _accel := GSTTargetAcceleration.new()
|
||||
@ -11,11 +11,8 @@ var _drag := 0.1
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_update_agent()
|
||||
arrive.calculate_steering(_accel)
|
||||
_velocity += Vector2(_accel.linear.x, _accel.linear.y)
|
||||
_velocity = _velocity.linear_interpolate(Vector2.ZERO, _drag).clamped(agent.linear_speed_max)
|
||||
_velocity = move_and_slide(_velocity)
|
||||
agent._apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func setup(
|
||||
@ -26,12 +23,7 @@ func setup(
|
||||
) -> void:
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
agent.linear_acceleration_max = linear_acceleration_max
|
||||
agent.position = Vector3(global_position.x, global_position.y, 0)
|
||||
agent.linear_drag_percentage = _drag
|
||||
arrive.deceleration_radius = deceleration_radius
|
||||
arrive.arrival_tolerance = arrival_tolerance
|
||||
target.position = agent.position
|
||||
|
||||
|
||||
func _update_agent() -> void:
|
||||
agent.position = Vector3(global_position.x, global_position.y, 0)
|
||||
agent.linear_velocity = Vector3(_velocity.x, _velocity.y, 0)
|
||||
|
@ -13,7 +13,7 @@ var _drag := 0.1
|
||||
var _color := Color(0.4, 1.0, 0.89, 0.3)
|
||||
|
||||
onready var collision := $CollisionShape2D
|
||||
onready var agent := GSTSteeringAgent.new()
|
||||
onready var agent := GSTNode2DAgent.new(self)
|
||||
onready var proximity := GSTRadiusProximity.new(agent, [], 140)
|
||||
onready var avoid := GSTAvoidCollisions.new(agent, proximity)
|
||||
onready var target := GSTAgentLocation.new()
|
||||
@ -27,12 +27,11 @@ func _draw() -> void:
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_update_agent()
|
||||
target.position.x = agent.position.x + _direction.x*_radius
|
||||
target.position.y = agent.position.y + _direction.y*_radius
|
||||
|
||||
priority.calculate_steering(_accel)
|
||||
_velocity += Vector2(_accel.linear.x, _accel.linear.y)
|
||||
_velocity = _velocity.linear_interpolate(Vector2.ZERO, _drag)
|
||||
_velocity = _velocity.clamped(agent.linear_speed_max)
|
||||
_velocity = move_and_slide(_velocity)
|
||||
agent._apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func setup(
|
||||
@ -46,15 +45,19 @@ func setup(
|
||||
) -> void:
|
||||
rng.randomize()
|
||||
_direction = Vector2(rng.randf_range(-1, 1), rng.randf_range(-1, 1)).normalized()
|
||||
_update_agent()
|
||||
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
agent.linear_acceleration_max = linear_accel_max
|
||||
|
||||
proximity.radius = proximity_radius
|
||||
_boundary_bottom = boundary_bottom
|
||||
_boundary_right = boundary_right
|
||||
|
||||
_radius = collision.shape.radius
|
||||
agent.bounding_radius = _radius
|
||||
|
||||
agent.linear_drag_percentage = _drag
|
||||
|
||||
self.draw_proximity = draw_proximity
|
||||
|
||||
priority.add(avoid)
|
||||
@ -84,12 +87,3 @@ func set_random_nonoverlapping_position(others: Array, distance_from_boundary_mi
|
||||
done = false
|
||||
if done:
|
||||
break
|
||||
|
||||
|
||||
func _update_agent() -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
agent.linear_velocity.x = _velocity.x
|
||||
agent.linear_velocity.y = _velocity.y
|
||||
target.position.x = agent.position.x + _direction.x*_radius
|
||||
target.position.y = agent.position.y + _direction.y*_radius
|
||||
|
@ -2,7 +2,7 @@ extends KinematicBody2D
|
||||
|
||||
|
||||
var face: GSTFace
|
||||
var agent := GSTSteeringAgent.new()
|
||||
var agent := GSTNode2DAgent.new(self)
|
||||
|
||||
var _accel := GSTTargetAcceleration.new()
|
||||
var _angular_drag := 0.1
|
||||
@ -20,14 +20,7 @@ func _ready() -> void:
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
face.calculate_steering(_accel)
|
||||
agent.angular_velocity = clamp(
|
||||
agent.angular_velocity + _accel.angular,
|
||||
-agent.angular_speed_max,
|
||||
agent.angular_speed_max
|
||||
)
|
||||
agent.angular_velocity = lerp(agent.angular_velocity, 0, _angular_drag)
|
||||
agent.orientation += agent.angular_velocity * delta
|
||||
rotation = agent.orientation
|
||||
agent._apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
@ -48,5 +41,4 @@ func setup(
|
||||
|
||||
agent.angular_acceleration_max = angular_accel_max
|
||||
agent.angular_speed_max = angular_speed_max
|
||||
agent.position = Vector3(global_position.x, global_position.y, 0)
|
||||
|
||||
agent.angular_drag_percentage = _angular_drag
|
||||
|
@ -6,7 +6,7 @@ var _accel := GSTTargetAcceleration.new()
|
||||
var _valid := false
|
||||
var _drag := 0.1
|
||||
|
||||
onready var agent := GSTSteeringAgent.new()
|
||||
onready var agent := GSTNode2DAgent.new(self)
|
||||
onready var path := GSTPath.new([
|
||||
Vector3(global_position.x, global_position.y, 0),
|
||||
Vector3(global_position.x, global_position.y, 0)
|
||||
@ -25,27 +25,18 @@ func setup(
|
||||
owner.drawer.connect("path_established", self, "_on_Drawer_path_established")
|
||||
follow.path_offset = path_offset
|
||||
follow.prediction_time = predict_time
|
||||
agent.linear_acceleration_max = accel_max
|
||||
agent.linear_speed_max = speed_max
|
||||
follow.deceleration_radius = decel_radius
|
||||
follow.arrival_tolerance = arrival_tolerance
|
||||
|
||||
agent.linear_acceleration_max = accel_max
|
||||
agent.linear_speed_max = speed_max
|
||||
agent.linear_drag_percentage = _drag
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if _valid:
|
||||
_update_agent()
|
||||
follow.calculate_steering(_accel)
|
||||
_velocity += Vector2(_accel.linear.x, _accel.linear.y)
|
||||
_velocity = _velocity.linear_interpolate(Vector2.ZERO, _drag)
|
||||
_velocity = _velocity.clamped(agent.linear_speed_max)
|
||||
_velocity = move_and_slide(_velocity)
|
||||
|
||||
|
||||
func _update_agent() -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
agent.linear_velocity.x = _velocity.x
|
||||
agent.linear_velocity.y = _velocity.y
|
||||
agent._apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func _on_Drawer_path_established(points: Array) -> void:
|
||||
|
@ -4,7 +4,7 @@ extends KinematicBody2D
|
||||
var separation: GSTSeparation
|
||||
var cohesion: GSTCohesion
|
||||
var proximity: GSTRadiusProximity
|
||||
var agent := GSTSteeringAgent.new()
|
||||
var agent := GSTNode2DAgent.new(self)
|
||||
var blend := GSTBlend.new(agent)
|
||||
var acceleration := GSTTargetAcceleration.new()
|
||||
var draw_proximity := false
|
||||
@ -28,6 +28,7 @@ func setup(
|
||||
|
||||
agent.linear_acceleration_max = linear_accel_max
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
agent.linear_drag_percentage = 0.1
|
||||
|
||||
proximity = GSTRadiusProximity.new(agent, [], proximity_radius)
|
||||
separation = GSTSeparation.new(agent, proximity)
|
||||
@ -43,14 +44,9 @@ func _draw() -> void:
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
if blend:
|
||||
blend.calculate_steering(acceleration)
|
||||
_velocity += Vector2(acceleration.linear.x, acceleration.linear.y)
|
||||
_velocity = _velocity.linear_interpolate(Vector2.ZERO, 0.1)
|
||||
_velocity = _velocity.clamped(agent.linear_speed_max)
|
||||
move_and_slide(_velocity)
|
||||
agent._apply_steering(acceleration, delta)
|
||||
|
||||
|
||||
func set_neighbors(neighbor: Array) -> void:
|
||||
|
@ -12,5 +12,6 @@ script = ExtResource( 1 )
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 3 )
|
||||
inner_color = Color( 0, 0, 0, 1 )
|
||||
outer_color = Color( 0.301961, 0.65098, 1, 1 )
|
||||
stroke = 4.0
|
||||
|
@ -4,71 +4,68 @@ extends KinematicBody2D
|
||||
|
||||
export var use_seek: bool = false
|
||||
|
||||
var _orient_behavior: GSTSteeringBehavior
|
||||
var _behavior: GSTSteeringBehavior
|
||||
var _blend: GSTBlend
|
||||
|
||||
var _linear_velocity := Vector2()
|
||||
var _linear_drag_coefficient := 0.025
|
||||
var _angular_velocity := 0.0
|
||||
var _angular_drag := 0.1
|
||||
var _direction_face := GSTAgentLocation.new()
|
||||
|
||||
onready var agent := GSTSteeringAgent.new()
|
||||
onready var agent := GSTNode2DAgent.new(self)
|
||||
onready var accel := GSTTargetAcceleration.new()
|
||||
onready var player_agent: GSTSteeringAgent = owner.find_node("Player", true, false).agent
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
agent.calculate_velocities = false
|
||||
set_physics_process(false)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_update_agent()
|
||||
|
||||
_behavior.calculate_steering(accel)
|
||||
|
||||
_direction_face.position = agent.position + accel.linear.normalized()
|
||||
|
||||
_orient_behavior.calculate_steering(accel)
|
||||
_angular_velocity += accel.angular
|
||||
_blend.calculate_steering(accel)
|
||||
|
||||
_angular_velocity = clamp(
|
||||
lerp(_angular_velocity, 0, _angular_drag),
|
||||
agent.angular_velocity = clamp(
|
||||
agent.angular_velocity + accel.angular,
|
||||
-agent.angular_speed_max,
|
||||
agent.angular_speed_max
|
||||
)
|
||||
agent.angular_velocity = lerp(agent.angular_velocity, 0, _angular_drag)
|
||||
|
||||
rotation += _angular_velocity * delta
|
||||
rotation += agent.angular_velocity * delta
|
||||
|
||||
_linear_velocity += GSTUtils.angle_to_vector2(rotation) * -agent.linear_acceleration_max
|
||||
_linear_velocity = _linear_velocity.clamped(agent.linear_speed_max)
|
||||
_linear_velocity = _linear_velocity.linear_interpolate(Vector2.ZERO, _linear_drag_coefficient)
|
||||
_linear_velocity = move_and_slide(_linear_velocity)
|
||||
var linear_velocity := (
|
||||
GSTUtils.to_vector2(agent.linear_velocity) +
|
||||
(GSTUtils.angle_to_vector2(rotation) * -agent.linear_acceleration_max)
|
||||
)
|
||||
linear_velocity = linear_velocity.clamped(agent.linear_speed_max)
|
||||
linear_velocity = linear_velocity.linear_interpolate(
|
||||
Vector2.ZERO,
|
||||
_linear_drag_coefficient
|
||||
)
|
||||
|
||||
linear_velocity = move_and_slide(linear_velocity)
|
||||
agent.linear_velocity = GSTUtils.to_vector3(linear_velocity)
|
||||
|
||||
|
||||
func setup(predict_time: float, linear_speed_max: float, linear_accel_max: float) -> void:
|
||||
var behavior: GSTSteeringBehavior
|
||||
if use_seek:
|
||||
_behavior = GSTSeek.new(agent, player_agent)
|
||||
behavior = GSTSeek.new(agent, player_agent)
|
||||
else:
|
||||
_behavior = GSTPursue.new(agent, player_agent, predict_time)
|
||||
behavior = GSTPursue.new(agent, player_agent, predict_time)
|
||||
|
||||
_orient_behavior = GSTFace.new(agent, _direction_face)
|
||||
_orient_behavior.alignment_tolerance = deg2rad(5)
|
||||
_orient_behavior.deceleration_radius = deg2rad(5)
|
||||
var orient_behavior := GSTFace.new(agent, _direction_face)
|
||||
orient_behavior.alignment_tolerance = deg2rad(5)
|
||||
orient_behavior.deceleration_radius = deg2rad(5)
|
||||
|
||||
_blend = GSTBlend.new(agent)
|
||||
_blend.add(behavior, 1)
|
||||
_blend.add(orient_behavior, 1)
|
||||
|
||||
agent.angular_acceleration_max = deg2rad(40)
|
||||
agent.angular_speed_max = deg2rad(90)
|
||||
agent.linear_acceleration_max = linear_accel_max
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
|
||||
_update_agent()
|
||||
set_physics_process(true)
|
||||
|
||||
|
||||
func _update_agent() -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
agent.orientation = rotation
|
||||
agent.linear_velocity.x = _linear_velocity.x
|
||||
agent.linear_velocity.y = _linear_velocity.y
|
||||
agent.angular_velocity = _angular_velocity
|
||||
|
@ -1,5 +1,4 @@
|
||||
extends KinematicBody2D
|
||||
# AI agent that uses the Seek behavior to hone in on the player's location as directly as possible.
|
||||
|
||||
|
||||
var player_agent: GSTAgentLocation
|
||||
@ -8,7 +7,7 @@ var start_speed: float
|
||||
var start_accel: float
|
||||
var use_seek := true
|
||||
|
||||
onready var agent := GSTSteeringAgent.new()
|
||||
onready var agent := GSTNode2DAgent.new(self)
|
||||
onready var accel := GSTTargetAcceleration.new()
|
||||
onready var seek := GSTSeek.new(agent, player_agent)
|
||||
onready var flee := GSTFlee.new(agent, player_agent)
|
||||
@ -23,18 +22,9 @@ func _physics_process(delta: float) -> void:
|
||||
if not player_agent:
|
||||
return
|
||||
|
||||
_update_agent()
|
||||
if use_seek:
|
||||
seek.calculate_steering(accel)
|
||||
else:
|
||||
flee.calculate_steering(accel)
|
||||
|
||||
velocity = (velocity + Vector2(accel.linear.x, accel.linear.y)).clamped(agent.linear_speed_max)
|
||||
velocity = move_and_slide(velocity)
|
||||
|
||||
|
||||
func _update_agent() -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
agent.linear_velocity.x = velocity.x
|
||||
agent.linear_velocity.y = velocity.y
|
||||
agent._apply_steering(accel, delta)
|
||||
|
@ -74,6 +74,16 @@ _global_script_classes=[ {
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Behaviors/GSTMatchOrientation.gd"
|
||||
}, {
|
||||
"base": "GSTNodeAgent",
|
||||
"class": "GSTNode2DAgent",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Agents/GSTNode2DAgent.gd"
|
||||
}, {
|
||||
"base": "GSTSteeringAgent",
|
||||
"class": "GSTNodeAgent",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Agents/GSTNodeAgent.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "GSTPath",
|
||||
"language": "GDScript",
|
||||
@ -109,6 +119,11 @@ _global_script_classes=[ {
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Behaviors/GSTSeparation.gd"
|
||||
}, {
|
||||
"base": "GSTNodeAgent",
|
||||
"class": "GSTSpatialAgent",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/Agents/GSTSpatialAgent.gd"
|
||||
}, {
|
||||
"base": "GSTAgentLocation",
|
||||
"class": "GSTSteeringAgent",
|
||||
"language": "GDScript",
|
||||
@ -143,6 +158,8 @@ _global_script_class_icons={
|
||||
"GSTInfiniteProximity": "",
|
||||
"GSTLookWhereYouGo": "",
|
||||
"GSTMatchOrientation": "",
|
||||
"GSTNode2DAgent": "",
|
||||
"GSTNodeAgent": "",
|
||||
"GSTPath": "",
|
||||
"GSTPriority": "",
|
||||
"GSTProximity": "",
|
||||
@ -150,6 +167,7 @@ _global_script_class_icons={
|
||||
"GSTRadiusProximity": "",
|
||||
"GSTSeek": "",
|
||||
"GSTSeparation": "",
|
||||
"GSTSpatialAgent": "",
|
||||
"GSTSteeringAgent": "",
|
||||
"GSTSteeringBehavior": "",
|
||||
"GSTTargetAcceleration": "",
|
||||
|
File diff suppressed because it is too large
Load Diff
165
project/src/Agents/GSTNode2DAgent.gd
Normal file
165
project/src/Agents/GSTNode2DAgent.gd
Normal file
@ -0,0 +1,165 @@
|
||||
# A specialized steering agent that updates itself every frame so the user does
|
||||
# not have to.
|
||||
extends GSTNodeAgent
|
||||
class_name GSTNode2DAgent
|
||||
|
||||
|
||||
# The Node2D to keep track of
|
||||
var body: Node2D setget _set_body
|
||||
|
||||
var _last_position: Vector2
|
||||
|
||||
|
||||
func _init(body: Node2D) -> void:
|
||||
self.body = body
|
||||
if body.is_inside_tree():
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
else:
|
||||
body.connect("ready", self, "_on_body_ready")
|
||||
|
||||
|
||||
# Moves the agent's `body` by target `acceleration`.
|
||||
# tags: virtual
|
||||
func _apply_steering(acceleration: GSTTargetAcceleration, delta: float) -> void:
|
||||
_applied_steering = true
|
||||
match _body_type:
|
||||
BodyType.RIGID:
|
||||
_apply_rigid_steering(acceleration)
|
||||
BodyType.KINEMATIC:
|
||||
match kinematic_movement_type:
|
||||
MovementType.COLLIDE:
|
||||
_apply_collide_steering(acceleration.linear, delta)
|
||||
MovementType.SLIDE:
|
||||
_apply_sliding_steering(acceleration.linear)
|
||||
MovementType.POSITION:
|
||||
_apply_normal_steering(acceleration.linear, delta)
|
||||
BodyType.NODE:
|
||||
_apply_normal_steering(acceleration.linear, delta)
|
||||
if not _body_type == BodyType.RIGID:
|
||||
_apply_orientation_steering(acceleration.angular, delta)
|
||||
|
||||
|
||||
func _apply_rigid_steering(accel: GSTTargetAcceleration) -> void:
|
||||
body.apply_central_impulse(GSTUtils.to_vector2(accel.linear))
|
||||
body.apply_torque_impulse(accel.angular)
|
||||
if calculate_velocities:
|
||||
linear_velocity = GSTUtils.to_vector3(body.linear_velocity)
|
||||
angular_velocity = body.angular_velocity
|
||||
|
||||
|
||||
func _apply_sliding_steering(accel: Vector3) -> void:
|
||||
var velocity := GSTUtils.to_vector2(linear_velocity + accel).clamped(linear_speed_max)
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(Vector2.ZERO, linear_drag_percentage)
|
||||
velocity = body.move_and_slide(velocity)
|
||||
if calculate_velocities:
|
||||
linear_velocity = GSTUtils.to_vector3(velocity)
|
||||
|
||||
|
||||
func _apply_collide_steering(accel: Vector3, delta: float) -> void:
|
||||
var velocity := GSTUtils.clampedv3(linear_velocity + accel, linear_speed_max)
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(
|
||||
Vector3.ZERO,
|
||||
linear_drag_percentage
|
||||
)
|
||||
body.move_and_collide(GSTUtils.to_vector2(velocity) * delta)
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_normal_steering(accel: Vector3, delta: float) -> void:
|
||||
var velocity := GSTUtils.clampedv3(linear_velocity + accel, linear_speed_max)
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(
|
||||
Vector3.ZERO,
|
||||
linear_drag_percentage
|
||||
)
|
||||
body.global_position += GSTUtils.to_vector2(velocity) * delta
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void:
|
||||
var velocity = angular_velocity + angular_acceleration
|
||||
if apply_angular_drag:
|
||||
velocity = lerp(velocity, 0, angular_drag_percentage)
|
||||
body.rotation += velocity * delta
|
||||
if calculate_velocities:
|
||||
angular_velocity = velocity
|
||||
|
||||
|
||||
func _set_use_physics(value: bool) -> void:
|
||||
if use_physics and not value:
|
||||
body.get_tree().disconnect("idle_frame", self, "_on_SceneTree_frame")
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
elif not use_physics and value:
|
||||
body.get_tree().disconnect("physics_frame", self, "_on_SceneTree_frame")
|
||||
body.get_tree().connect("idle_frame", self, "_on_SceneTree_frame")
|
||||
use_physics = value
|
||||
|
||||
|
||||
func _set_body(value: Node2D) -> void:
|
||||
body = value
|
||||
if body is RigidBody2D:
|
||||
_body_type = BodyType.RIGID
|
||||
elif body is KinematicBody2D:
|
||||
_body_type = BodyType.KINEMATIC
|
||||
else:
|
||||
_body_type = BodyType.NODE
|
||||
|
||||
if _body_type == BodyType.RIGID:
|
||||
linear_velocity = GSTUtils.to_vector3(body.linear_velocity)
|
||||
angular_velocity = body.angular_velocity
|
||||
else:
|
||||
_last_position = body.global_position
|
||||
_last_orientation = body.rotation
|
||||
|
||||
position = GSTUtils.to_vector3(_last_position)
|
||||
orientation = _last_orientation
|
||||
|
||||
|
||||
func _on_body_ready() -> void:
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
_set_body(body)
|
||||
|
||||
|
||||
func _on_SceneTree_frame() -> void:
|
||||
var current_position: Vector2 = body.global_position
|
||||
var current_orientation: float = body.rotation
|
||||
|
||||
position = GSTUtils.to_vector3(current_position)
|
||||
orientation = current_orientation
|
||||
|
||||
if calculate_velocities:
|
||||
if _applied_steering:
|
||||
_applied_steering = false
|
||||
else:
|
||||
match _body_type:
|
||||
BodyType.RIGID:
|
||||
linear_velocity = GSTUtils.to_vector3(body.linear_velocity)
|
||||
angular_velocity = body.angular_velocity
|
||||
_:
|
||||
linear_velocity = GSTUtils.clampedv3(
|
||||
GSTUtils.to_vector3(_last_position - current_position),
|
||||
linear_speed_max
|
||||
)
|
||||
if apply_linear_drag:
|
||||
linear_velocity = linear_velocity.linear_interpolate(
|
||||
Vector3.ZERO,
|
||||
linear_drag_percentage
|
||||
)
|
||||
angular_velocity = clamp(
|
||||
_last_orientation - current_orientation,
|
||||
-angular_speed_max,
|
||||
angular_speed_max
|
||||
)
|
||||
if apply_angular_drag:
|
||||
angular_velocity = lerp(
|
||||
angular_velocity,
|
||||
0,
|
||||
angular_drag_percentage
|
||||
)
|
||||
|
||||
_last_position = current_position
|
||||
_last_orientation = current_orientation
|
79
project/src/Agents/GSTNodeAgent.gd
Normal file
79
project/src/Agents/GSTNodeAgent.gd
Normal file
@ -0,0 +1,79 @@
|
||||
# A base class for a specialized steering agent that updates itself every frame
|
||||
# so the user does not have to.
|
||||
extends GSTSteeringAgent
|
||||
class_name GSTNodeAgent
|
||||
|
||||
|
||||
enum MovementType { SLIDE, COLLIDE, POSITION }
|
||||
|
||||
enum BodyType { NODE, KINEMATIC, RIGID }
|
||||
|
||||
|
||||
# If `true`, will update before `_physics_process` is called. If `false`, will
|
||||
# update before `_process` is called.
|
||||
#
|
||||
# `KinematicBody`, `KinematicBody2D`, `RigidBody`, and `RigidBody2D` should
|
||||
# always use `_physics_process`.
|
||||
var use_physics := true setget _set_use_physics
|
||||
|
||||
# If `true`, will calculate linear and angular velocities based on the previous
|
||||
# frame. When `false`, the user must keep those values updated.
|
||||
var calculate_velocities := true
|
||||
|
||||
# If `true` and velocities and `calculate_velocities` is true, will interpolate
|
||||
# the current linear velocity towards 0 by the `linear_drag_percentage` value.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var apply_linear_drag := true
|
||||
|
||||
# If `true` and velocities and `calculate_velocities` is true, will interpolate
|
||||
# the current angular velocity towards 0 by the `angular_drag_percentage` value.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var apply_angular_drag := true
|
||||
|
||||
# The percentage between the current linear velocity and 0 to interpolate by if
|
||||
# `calculate_velocities` and `apply_linear_drag` are true.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var linear_drag_percentage := 0.0
|
||||
|
||||
# The percentage between the current angular velocity and 0 to interpolate by if
|
||||
# `calculate_velocities` and `apply_angular_drag` are true.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var angular_drag_percentage := 0.0
|
||||
|
||||
# Determines how linear movement occurs if the body is a `KinematicBody` or
|
||||
# `KinematicBody2D`.
|
||||
#
|
||||
# SLIDE uses `move_and_slide`
|
||||
# COLLIDE uses `move_and_collide`
|
||||
# POSITION changes global position directly
|
||||
var kinematic_movement_type: int = MovementType.SLIDE
|
||||
|
||||
var _last_orientation: float
|
||||
var _body_type: int
|
||||
var _applied_steering := false
|
||||
|
||||
|
||||
# Moves the agent's body by target `acceleration`.
|
||||
# tags: virtual
|
||||
func _apply_steering(acceleration: GSTTargetAcceleration, delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _apply_sliding_steering(accel: Vector3) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _apply_collide_steering(accel: Vector3, delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _apply_normal_steering(accel: Vector3, delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _set_use_physics(value: bool) -> void:
|
||||
use_physics = value
|
166
project/src/Agents/GSTSpatialAgent.gd
Normal file
166
project/src/Agents/GSTSpatialAgent.gd
Normal file
@ -0,0 +1,166 @@
|
||||
# A specialized steering agent that updates itself every frame so the user does
|
||||
# not have to.
|
||||
extends GSTNodeAgent
|
||||
class_name GSTSpatialAgent
|
||||
|
||||
|
||||
# The Spatial to keep track of
|
||||
var body: Spatial setget _set_body
|
||||
|
||||
var _last_position: Vector3
|
||||
|
||||
|
||||
func _init(body: Spatial) -> void:
|
||||
self.body = body
|
||||
if body.is_inside_tree():
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
else:
|
||||
body.connect("ready", self, "_on_body_ready")
|
||||
|
||||
|
||||
# Moves the agent's `body` by target `acceleration`.
|
||||
# tags: virtual
|
||||
func _apply_steering(acceleration: GSTTargetAcceleration, delta: float) -> void:
|
||||
_applied_steering = true
|
||||
match _body_type:
|
||||
BodyType.RIGID:
|
||||
_apply_rigid_steering(acceleration)
|
||||
BodyType.KINEMATIC:
|
||||
match kinematic_movement_type:
|
||||
MovementType.COLLIDE:
|
||||
_apply_collide_steering(acceleration.linear, delta)
|
||||
MovementType.SLIDE:
|
||||
_apply_sliding_steering(acceleration.linear)
|
||||
MovementType.POSITION:
|
||||
_apply_normal_steering(acceleration.linear, delta)
|
||||
BodyType.NODE:
|
||||
_apply_normal_steering(acceleration.linear, delta)
|
||||
if not _body_type == BodyType.RIGID:
|
||||
_apply_orientation_steering(acceleration.angular, delta)
|
||||
|
||||
|
||||
func _apply_rigid_steering(accel: GSTTargetAcceleration) -> void:
|
||||
body.apply_central_impulse(GSTUtils.to_Vector3(accel.linear))
|
||||
body.apply_torque_impulse(accel.angular)
|
||||
if calculate_velocities:
|
||||
linear_velocity = body.linear_velocity
|
||||
angular_velocity = body.angular_velocity.y
|
||||
|
||||
|
||||
func _apply_sliding_steering(accel: Vector3) -> void:
|
||||
var velocity := GSTUtils.clampedv3(linear_velocity + accel, linear_speed_max)
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
|
||||
velocity = body.move_and_slide(velocity)
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_collide_steering(accel: Vector3, delta: float) -> void:
|
||||
var velocity := GSTUtils.clampedv3(linear_velocity + accel, linear_speed_max)
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(
|
||||
Vector3.ZERO,
|
||||
linear_drag_percentage
|
||||
)
|
||||
body.move_and_collide(velocity * delta)
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_normal_steering(accel: Vector3, delta: float) -> void:
|
||||
var velocity := GSTUtils.clampedv3(linear_velocity + accel, linear_speed_max)
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(
|
||||
Vector3.ZERO,
|
||||
linear_drag_percentage
|
||||
)
|
||||
body.global_position += velocity * delta
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void:
|
||||
var velocity = angular_velocity + angular_acceleration
|
||||
if apply_angular_drag:
|
||||
velocity = lerp(velocity, 0, angular_drag_percentage)
|
||||
body.rotation += velocity * delta
|
||||
if calculate_velocities:
|
||||
angular_velocity = velocity
|
||||
|
||||
|
||||
func _set_use_physics(value: bool) -> void:
|
||||
if use_physics and not value:
|
||||
body.get_tree().disconnect("idle_frame", self, "_on_SceneTree_frame")
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
elif not use_physics and value:
|
||||
body.get_tree().disconnect("physics_frame", self, "_on_SceneTree_frame")
|
||||
body.get_tree().connect("idle_frame", self, "_on_SceneTree_frame")
|
||||
use_physics = value
|
||||
|
||||
|
||||
func _set_body(value: Spatial) -> void:
|
||||
body = value
|
||||
if body is RigidBody:
|
||||
_body_type = BodyType.RIGID
|
||||
elif body is KinematicBody:
|
||||
_body_type = BodyType.KINEMATIC
|
||||
else:
|
||||
_body_type = BodyType.NODE
|
||||
|
||||
if _body_type == BodyType.RIGID:
|
||||
linear_velocity = body.linear_velocity
|
||||
angular_velocity = body.angular_velocity
|
||||
else:
|
||||
_last_position = body.global_position
|
||||
_last_orientation = body.rotation.y
|
||||
|
||||
position = _last_position
|
||||
orientation = _last_orientation
|
||||
|
||||
|
||||
func _on_body_ready() -> void:
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
body.disconnect("ready", self, "_on_body_ready")
|
||||
_set_body(body)
|
||||
|
||||
|
||||
func _on_SceneTree_frame() -> void:
|
||||
var current_position: Vector3 = body.global_position
|
||||
var current_orientation: float = body.rotation.y
|
||||
|
||||
position = current_position
|
||||
orientation = current_orientation
|
||||
|
||||
if calculate_velocities:
|
||||
if _applied_steering:
|
||||
_applied_steering = false
|
||||
else:
|
||||
match _body_type:
|
||||
BodyType.RIGID:
|
||||
linear_velocity = GSTUtils.to_vector3(body.linear_velocity)
|
||||
angular_velocity = body.angular_velocity
|
||||
_:
|
||||
linear_velocity = GSTUtils.clampedv3(
|
||||
_last_position - current_position,
|
||||
linear_speed_max
|
||||
)
|
||||
if apply_linear_drag:
|
||||
linear_velocity = linear_velocity.linear_interpolate(
|
||||
Vector3.ZERO,
|
||||
linear_drag_percentage
|
||||
)
|
||||
angular_velocity = clamp(
|
||||
_last_orientation - current_orientation,
|
||||
-angular_speed_max,
|
||||
angular_speed_max
|
||||
)
|
||||
if apply_angular_drag:
|
||||
angular_velocity = lerp(
|
||||
angular_velocity,
|
||||
0,
|
||||
angular_drag_percentage
|
||||
)
|
||||
|
||||
_last_position = current_position
|
||||
_last_orientation = current_orientation
|
@ -25,3 +25,13 @@ static func vector3_to_angle(vector: Vector3) -> float:
|
||||
# rotate around the Y axis.
|
||||
static func angle_to_vector2(angle: float) -> Vector2:
|
||||
return Vector2(sin(-angle), cos(angle))
|
||||
|
||||
|
||||
# Returns a vector2 with `vector`'s x and y components.
|
||||
static func to_vector2(vector: Vector3) -> Vector2:
|
||||
return Vector2(vector.x, vector.y)
|
||||
|
||||
|
||||
# Returns a vector3 with `vector`'s x and y components and 0 in z.
|
||||
static func to_vector3(vector: Vector2) -> Vector3:
|
||||
return Vector3(vector.x, vector.y, 0)
|
||||
|
Loading…
Reference in New Issue
Block a user