mirror of
https://github.com/Relintai/godot-steering-ai-framework.git
synced 2024-11-14 04:57:19 +01:00
Implement Face, Evade Pursue, Arrive, Flee, Seek
The MVP document goes over the main behaviors that need to be implemented prior to having a product.
This commit is contained in:
parent
4bc8bb372f
commit
c00b1242c8
@ -1,5 +1,7 @@
|
|||||||
[gd_resource type="Environment" load_steps=2 format=2]
|
[gd_resource type="Environment" load_steps=2 format=2]
|
||||||
|
|
||||||
[sub_resource type="ProceduralSky" id=1]
|
[sub_resource type="ProceduralSky" id=1]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
background_mode = 2
|
background_mode = 2
|
||||||
background_sky = SubResource( 1 )
|
background_sky = SubResource( 1 )
|
||||||
|
@ -8,9 +8,74 @@
|
|||||||
|
|
||||||
config_version=4
|
config_version=4
|
||||||
|
|
||||||
_global_script_classes=[ ]
|
_global_script_classes=[ {
|
||||||
|
"base": "Reference",
|
||||||
|
"class": "AgentLocation",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/AgentLocation.gd"
|
||||||
|
}, {
|
||||||
|
"base": "SteeringBehavior",
|
||||||
|
"class": "Arrive",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/Behaviors/Arrive.gd"
|
||||||
|
}, {
|
||||||
|
"base": "Pursue",
|
||||||
|
"class": "Evade",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/Behaviors/Evade.gd"
|
||||||
|
}, {
|
||||||
|
"base": "MatchOrientation",
|
||||||
|
"class": "Face",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/Behaviors/Face.gd"
|
||||||
|
}, {
|
||||||
|
"base": "Seek",
|
||||||
|
"class": "Flee",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/Behaviors/Flee.gd"
|
||||||
|
}, {
|
||||||
|
"base": "SteeringBehavior",
|
||||||
|
"class": "MatchOrientation",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/Behaviors/MatchOrientation.gd"
|
||||||
|
}, {
|
||||||
|
"base": "SteeringBehavior",
|
||||||
|
"class": "Pursue",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/Behaviors/Pursue.gd"
|
||||||
|
}, {
|
||||||
|
"base": "SteeringBehavior",
|
||||||
|
"class": "Seek",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/Behaviors/Seek.gd"
|
||||||
|
}, {
|
||||||
|
"base": "AgentLocation",
|
||||||
|
"class": "SteeringAgent",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/SteeringAgent.gd"
|
||||||
|
}, {
|
||||||
|
"base": "Reference",
|
||||||
|
"class": "SteeringBehavior",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/SteeringBehavior.gd"
|
||||||
|
}, {
|
||||||
|
"base": "Reference",
|
||||||
|
"class": "TargetAcceleration",
|
||||||
|
"language": "GDScript",
|
||||||
|
"path": "res://src/Steering/TargetAcceleration.gd"
|
||||||
|
} ]
|
||||||
_global_script_class_icons={
|
_global_script_class_icons={
|
||||||
|
"AgentLocation": "",
|
||||||
|
"Arrive": "",
|
||||||
|
"Evade": "",
|
||||||
|
"Face": "",
|
||||||
|
"Flee": "",
|
||||||
|
"MatchOrientation": "",
|
||||||
|
"Pursue": "",
|
||||||
|
"Seek": "",
|
||||||
|
"SteeringAgent": "",
|
||||||
|
"SteeringBehavior": "",
|
||||||
|
"TargetAcceleration": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
[application]
|
[application]
|
||||||
|
3
root.tscn
Normal file
3
root.tscn
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[gd_scene format=2]
|
||||||
|
|
||||||
|
[node name="Node2D" type="Node2D"]
|
6
src/Steering/AgentLocation.gd
Normal file
6
src/Steering/AgentLocation.gd
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
extends Reference
|
||||||
|
class_name AgentLocation
|
||||||
|
|
||||||
|
|
||||||
|
var position: = Vector3.ZERO
|
||||||
|
var orientation: = 0.0
|
46
src/Steering/Behaviors/Arrive.gd
Normal file
46
src/Steering/Behaviors/Arrive.gd
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
extends SteeringBehavior
|
||||||
|
class_name Arrive
|
||||||
|
|
||||||
|
|
||||||
|
var target: AgentLocation
|
||||||
|
var arrival_tolerance: float
|
||||||
|
var deceleration_radius: float
|
||||||
|
var time_to_target: = 0.1
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent, target: AgentLocation).(agent) -> void:
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
|
||||||
|
func _arrive(acceleration: TargetAcceleration, target_position: Vector3) -> TargetAcceleration:
|
||||||
|
var to_target: = target_position - agent.position
|
||||||
|
var distance: = to_target.length()
|
||||||
|
|
||||||
|
if distance <= arrival_tolerance:
|
||||||
|
return acceleration.set_zero()
|
||||||
|
|
||||||
|
var target_speed: = agent.max_linear_speed
|
||||||
|
|
||||||
|
if distance <= deceleration_radius:
|
||||||
|
target_speed *= distance / deceleration_radius
|
||||||
|
|
||||||
|
var target_velocity: = to_target * target_speed/distance
|
||||||
|
|
||||||
|
target_velocity = (target_velocity - agent.linear_velocity) * 1.0 / time_to_target
|
||||||
|
|
||||||
|
acceleration.linear = limit_length(target_velocity, agent.max_linear_acceleration)
|
||||||
|
acceleration.angular = 0
|
||||||
|
|
||||||
|
return acceleration
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_internal_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
return _arrive(acceleration, target.position)
|
||||||
|
|
||||||
|
|
||||||
|
static func limit_length(vector: Vector3, limit: float) -> Vector3:
|
||||||
|
var len2: = vector.length_squared()
|
||||||
|
var limit2: = limit * limit
|
||||||
|
if len2 > limit2:
|
||||||
|
vector *= sqrt(limit2 / len2)
|
||||||
|
return vector
|
10
src/Steering/Behaviors/Evade.gd
Normal file
10
src/Steering/Behaviors/Evade.gd
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
extends Pursue
|
||||||
|
class_name Evade
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent, target: SteeringAgent, max_predict_time: = 1.0).(agent, target, max_predict_time):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func get_max_linear_acceleration() -> float:
|
||||||
|
return -agent.max_linear_acceleration
|
22
src/Steering/Behaviors/Face.gd
Normal file
22
src/Steering/Behaviors/Face.gd
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
extends MatchOrientation
|
||||||
|
class_name Face
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent, target: AgentLocation).(agent, target) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _face(acceleration: TargetAcceleration, target_position: Vector3) -> TargetAcceleration:
|
||||||
|
var to_target: = target_position - agent.position
|
||||||
|
|
||||||
|
var distance_squared: = to_target.length_squared()
|
||||||
|
if distance_squared < agent.zero_linear_speed_threshold:
|
||||||
|
return acceleration.set_zero()
|
||||||
|
|
||||||
|
var orientation = Vector3.UP.angle_to(to_target)
|
||||||
|
|
||||||
|
return _match_orientation(acceleration, orientation)
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_internal_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
return _face(acceleration, target.position)
|
13
src/Steering/Behaviors/Flee.gd
Normal file
13
src/Steering/Behaviors/Flee.gd
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
extends Seek
|
||||||
|
class_name Flee
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent, target: AgentLocation).(agent, target) -> void:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_internal_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
acceleration.linear = (agent.position - target.position).normalized() * agent.max_linear_acceleration
|
||||||
|
acceleration.angular = 0
|
||||||
|
|
||||||
|
return acceleration
|
42
src/Steering/Behaviors/MatchOrientation.gd
Normal file
42
src/Steering/Behaviors/MatchOrientation.gd
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
extends SteeringBehavior
|
||||||
|
class_name MatchOrientation
|
||||||
|
|
||||||
|
|
||||||
|
var target: AgentLocation
|
||||||
|
var alignment_tolerance: float
|
||||||
|
var deceleration_radius: float
|
||||||
|
var time_to_target: float = 0.1
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent, target: AgentLocation).(agent) -> void:
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
|
||||||
|
func _match_orientation(acceleration: TargetAcceleration, target_orientation: float) -> TargetAcceleration:
|
||||||
|
var rotation: = wrapf(target_orientation - agent.orientation, -PI, PI)
|
||||||
|
|
||||||
|
var rotation_size: = -rotation if rotation < 0 else rotation
|
||||||
|
|
||||||
|
if rotation_size <= alignment_tolerance:
|
||||||
|
return acceleration.set_zero()
|
||||||
|
|
||||||
|
var target_rotation: = agent.max_angular_speed
|
||||||
|
|
||||||
|
if rotation_size <= deceleration_radius:
|
||||||
|
target_rotation *= rotation_size / deceleration_radius
|
||||||
|
|
||||||
|
target_rotation *= rotation / rotation_size
|
||||||
|
|
||||||
|
acceleration.angular = (target_rotation - agent.angular_velocity) / time_to_target
|
||||||
|
|
||||||
|
var limited_acceleration: = -acceleration.angular if acceleration.angular < 0 else acceleration.angular
|
||||||
|
if limited_acceleration > agent.max_angular_acceleration:
|
||||||
|
acceleration.angular *= agent.max_angular_acceleration / limited_acceleration
|
||||||
|
|
||||||
|
acceleration.linear = Vector3.ZERO
|
||||||
|
|
||||||
|
return acceleration
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_internal_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
return _match_orientation(acceleration, target.orientation)
|
35
src/Steering/Behaviors/Pursue.gd
Normal file
35
src/Steering/Behaviors/Pursue.gd
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
extends SteeringBehavior
|
||||||
|
class_name Pursue
|
||||||
|
|
||||||
|
|
||||||
|
var target: SteeringAgent
|
||||||
|
var max_predict_time: float
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent, target: SteeringAgent, max_predict_time: = 1.0).(agent) -> void:
|
||||||
|
self.target = target
|
||||||
|
self.max_predict_time = max_predict_time
|
||||||
|
|
||||||
|
|
||||||
|
func get_max_linear_acceleration() -> float:
|
||||||
|
return agent.max_linear_acceleration
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_internal_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
var target_position: = target.position
|
||||||
|
var distance_squared: = (target_position - agent.position).length_squared()
|
||||||
|
|
||||||
|
var speed_squared: = agent.linear_velocity.length_squared()
|
||||||
|
var predict_time: = max_predict_time
|
||||||
|
|
||||||
|
if speed_squared > 0:
|
||||||
|
var predict_time_squared: = distance_squared / speed_squared
|
||||||
|
if predict_time_squared < max_predict_time * max_predict_time:
|
||||||
|
predict_time = sqrt(predict_time_squared)
|
||||||
|
|
||||||
|
acceleration.linear = ((target_position + (target.linear_velocity * predict_time))-agent.position).normalized()
|
||||||
|
acceleration.linear *= agent.max_linear_acceleration
|
||||||
|
|
||||||
|
acceleration.angular = 0
|
||||||
|
|
||||||
|
return acceleration
|
16
src/Steering/Behaviors/Seek.gd
Normal file
16
src/Steering/Behaviors/Seek.gd
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
extends SteeringBehavior
|
||||||
|
class_name Seek
|
||||||
|
|
||||||
|
|
||||||
|
var target: AgentLocation
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent, target: AgentLocation).(agent) -> void:
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_internal_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
acceleration.linear = (target.position - agent.position).normalized() * agent.max_linear_acceleration
|
||||||
|
acceleration.angular = 0
|
||||||
|
|
||||||
|
return acceleration
|
12
src/Steering/SteeringAgent.gd
Normal file
12
src/Steering/SteeringAgent.gd
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
extends AgentLocation
|
||||||
|
class_name SteeringAgent
|
||||||
|
|
||||||
|
|
||||||
|
var zero_linear_speed_threshold: = 0.01
|
||||||
|
var max_linear_speed: = 0.0
|
||||||
|
var max_linear_acceleration: = 0.0
|
||||||
|
var max_angular_speed: = 0.0
|
||||||
|
var max_angular_acceleration: = 0.0
|
||||||
|
var linear_velocity: = Vector3.ZERO
|
||||||
|
var angular_velocity: = 0.0
|
||||||
|
var bounding_radius: = 0.0
|
18
src/Steering/SteeringBehavior.gd
Normal file
18
src/Steering/SteeringBehavior.gd
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
extends Reference
|
||||||
|
class_name SteeringBehavior
|
||||||
|
|
||||||
|
|
||||||
|
var enabled: = true
|
||||||
|
var agent: SteeringAgent
|
||||||
|
|
||||||
|
|
||||||
|
func _init(agent: SteeringAgent) -> void:
|
||||||
|
self.agent = agent
|
||||||
|
|
||||||
|
|
||||||
|
func calculate_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
return _calculate_internal_steering(acceleration) if enabled else acceleration.zero()
|
||||||
|
|
||||||
|
|
||||||
|
func _calculate_internal_steering(acceleration: TargetAcceleration) -> TargetAcceleration:
|
||||||
|
return acceleration.set_zero()
|
14
src/Steering/TargetAcceleration.gd
Normal file
14
src/Steering/TargetAcceleration.gd
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
extends Reference
|
||||||
|
class_name TargetAcceleration
|
||||||
|
|
||||||
|
|
||||||
|
var linear: = Vector3.ZERO
|
||||||
|
var angular: = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
func set_zero() -> TargetAcceleration:
|
||||||
|
linear.x = 0.0
|
||||||
|
linear.y = 0.0
|
||||||
|
linear.z = 0.0
|
||||||
|
angular = 0.0
|
||||||
|
return self
|
Loading…
Reference in New Issue
Block a user