├── .gitignore ├── Demos ├── Arrive │ ├── ArriveDemo.cs │ └── ArriveDemo.tscn ├── Arrive3d │ ├── Arrive3dDemo.cs │ ├── Arrive3dDemo.tscn │ └── Arriver.cs ├── AvoidCollisions │ ├── AvoidCollisionsDemo.tscn │ ├── Avoider.cs │ ├── Avoider.tscn │ └── Spawner.cs ├── Face │ ├── FaceDemo.tscn │ ├── TargetMouseMove.cs │ ├── Turret.cs │ └── TurretOutlook.cs ├── FollowPath │ ├── FollowPathDemo.cs │ ├── FollowPathDemo.tscn │ └── PathDrawer.cs ├── GroupBehaviors │ ├── GroupBehaviorsDemo.tscn │ ├── GrpSpawner.cs │ ├── Member.cs │ ├── Member.tscn │ └── MemberOutlook.cs ├── Main │ ├── DemoMain.cs │ └── DemoMain.tscn ├── PursueSeek │ ├── PursueSeekDemo.tscn │ ├── TrianglePlayer.cs │ ├── TrianglePursuer.cs │ └── TriangleSeeker.cs ├── QuickStart │ ├── Bullet.cs │ ├── Bullet.tscn │ ├── QuickAgent.cs │ ├── QuickPlayer.cs │ └── QuickStartDemo.tscn ├── SeekFlee │ ├── PlayerWithAgent.cs │ ├── SeekFleeDemo.tscn │ ├── Seeker.cs │ ├── Seeker.tscn │ └── SeekerSpawner.cs └── Utils │ ├── Background.tscn │ ├── Boundaries.tscn │ ├── BoundaryManager.cs │ ├── BoundaryManager.tscn │ ├── DrawBoundaries.cs │ ├── Player.tscn │ ├── PlayerOutlook.cs │ ├── SceneDesc.cs │ ├── SceneDesc.tscn │ ├── TriangleBoat.cs │ ├── TriangleBoat.tscn │ ├── TriangleBoatOutlook.cs │ └── default_env.tres ├── GSAI ├── Agents │ ├── GSAIKinematicBody2DAgent.cs │ ├── GSAIKinematicBody3DAgent.cs │ ├── GSAIRigidBody2DAgent.cs │ ├── GSAIRigidBody3DAgent.cs │ └── GSAISpecializedAgent.cs ├── Behaviors │ ├── GSAIArrive.cs │ ├── GSAIAvoidCollisions.cs │ ├── GSAIBlend.cs │ ├── GSAICohesion.cs │ ├── GSAIEvade.cs │ ├── GSAIFace.cs │ ├── GSAIFlee.cs │ ├── GSAIFollowPath.cs │ ├── GSAILookWhereYouGo.cs │ ├── GSAIMatchOrientation.cs │ ├── GSAIPriority.cs │ ├── GSAIPursue.cs │ ├── GSAISeek.cs │ └── GSAISeparation.cs ├── GSAIAgentLocation.cs ├── GSAIGroupBehavior.cs ├── GSAIPath.cs ├── GSAISteeringAgent.cs ├── GSAISteeringBehavior.cs ├── GSAITargetAcceleration.cs ├── GSAIUtils.cs └── Proximities │ ├── GSAIInfiniteProximity.cs │ ├── GSAIProximity.cs │ └── GSAIRadiusProximity.cs ├── GodotSteeringAI.csproj ├── GodotSteeringAI.sln ├── LICENSE ├── README.md ├── assets ├── background.png ├── background.png.import ├── icon.ico └── theme │ ├── button │ ├── disabled.stylebox │ ├── focused.stylebox │ ├── hover.stylebox │ ├── normal.stylebox │ └── pressed.stylebox │ ├── empty.stylebox │ ├── fonts │ ├── default_font.tres │ ├── default_font_bold.tres │ ├── default_font_code.tres │ ├── font_title.tres │ ├── montserrat │ │ ├── Montserrat-Black.ttf │ │ ├── Montserrat-Bold.ttf │ │ └── Montserrat-Medium.ttf │ └── source_code_pro │ │ └── SourceCodePro-Medium.otf │ ├── gdquest.theme │ ├── icons │ ├── chevron-right.svg │ ├── chevron-right.svg.import │ ├── chevron-up.svg │ └── chevron-up.svg.import │ ├── panel │ └── panel.stylebox │ ├── separator │ └── line.tres │ └── slider │ ├── grabber_area.stylebox │ └── slider.stylebox ├── export_presets.cfg ├── icon.png ├── icon.png.import └── project.godot /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | _build/ 3 | 4 | # Godot-specific ignores 5 | .import/ 6 | export.cfg 7 | 8 | # Mono-specific ignores 9 | .mono/ 10 | data_*/ 11 | -------------------------------------------------------------------------------- /Demos/Arrive/ArriveDemo.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using GodotSteeringAI; 4 | 5 | public class ArriveDemo : Node 6 | { 7 | [Export] private float linear_speed_max = 1600; 8 | [Export] private float linear_acceleration_max = 5000; 9 | [Export] private float arrival_tolerance = 15; 10 | [Export] private float deceleration_radius = 180; 11 | 12 | private KinematicBody2D arriver; 13 | private Node2D target_drawer; 14 | private float drag = 0.1f; 15 | 16 | private GSAIKinematicBody2DAgent gsai_agent; 17 | private GSAIAgentLocation gsai_target; 18 | private GSAIArrive gsai_arrive; 19 | private GSAITargetAcceleration gsai_accel; 20 | 21 | public override void _Ready() 22 | { 23 | arriver = GetNode("Arriver2d"); 24 | target_drawer = GetNode("TargetDrawer"); 25 | target_drawer.Connect("draw", this, nameof(_OnTargetDrawer_Draw)); 26 | target_drawer.GlobalPosition = arriver.GlobalPosition; 27 | gsai_agent = new GSAIKinematicBody2DAgent(arriver); 28 | gsai_target = new GSAIAgentLocation(); 29 | gsai_arrive = new GSAIArrive(gsai_agent, gsai_target); 30 | gsai_accel = new GSAITargetAcceleration(); 31 | gsai_agent.LinearSpeedMax = linear_speed_max; 32 | gsai_agent.LinearAccelerationMax = linear_acceleration_max; 33 | gsai_agent.LinearDragPercentage = drag; 34 | gsai_arrive.DecelerationRadius = deceleration_radius; 35 | gsai_arrive.ArrivalTolerance = arrival_tolerance; 36 | gsai_target.Position = gsai_agent.Position; 37 | } 38 | 39 | private void _OnTargetDrawer_Draw() 40 | { 41 | target_drawer.DrawCircle(Vector2.Zero, deceleration_radius, new Color(1.0f, 0.419f, 0.592f, 0.5f)); 42 | target_drawer.DrawCircle(Vector2.Zero, arrival_tolerance, new Color(0.278f, 0.231f, 0.47f, 0.3f)); 43 | } 44 | 45 | public override void _UnhandledInput(InputEvent evt) 46 | { 47 | if (evt is InputEventMouseButton btn) 48 | { 49 | if (btn.ButtonIndex == (int)ButtonList.Left && evt.IsPressed()) 50 | { 51 | target_drawer.GlobalPosition = btn.Position; 52 | gsai_target.Position = GSAIUtils.ToVector3(btn.Position); 53 | } 54 | } 55 | } 56 | 57 | public override void _PhysicsProcess(float delta) 58 | { 59 | gsai_arrive.CalculateSteering(gsai_accel); 60 | gsai_agent._ApplySteering(gsai_accel, delta); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Demos/Arrive/ArriveDemo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://Demos/Arrive/ArriveDemo.cs" type="Script" id=1] 4 | [ext_resource path="res://Demos/Utils/Background.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://Demos/Utils/Player.tscn" type="PackedScene" id=3] 6 | [ext_resource path="res://Demos/Utils/SceneDesc.tscn" type="PackedScene" id=4] 7 | 8 | [node name="ArriveDemo" type="Node"] 9 | script = ExtResource( 1 ) 10 | 11 | [node name="Background" parent="." instance=ExtResource( 2 )] 12 | 13 | [node name="TargetDrawer" type="Node2D" parent="."] 14 | 15 | [node name="Arriver2d" parent="." instance=ExtResource( 3 )] 16 | 17 | [node name="SceneDesc" parent="." instance=ExtResource( 4 )] 18 | Description = "Arrive Demo 19 | Mouse click to make the [color=lime]green \"Player\"[/color] move to the [color=fuchsia]purple target[/color]" 20 | -------------------------------------------------------------------------------- /Demos/Arrive3d/Arrive3dDemo.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | public class Arrive3dDemo : Node 5 | { 6 | private Camera camera; 7 | private RayCast ray; 8 | private Spatial mouseTarget; 9 | 10 | public override void _Ready() 11 | { 12 | camera = GetNode("Camera"); 13 | ray = camera.GetNode("RayCast"); 14 | mouseTarget = GetNode("MouseTarget"); 15 | } 16 | 17 | public override void _UnhandledInput(InputEvent evt) 18 | { 19 | if (evt is InputEventMouseMotion input) 20 | { 21 | ray.CastTo = camera.ProjectLocalRayNormal(input.Position) * 100000; 22 | ray.ForceRaycastUpdate(); 23 | if (ray.IsColliding()) 24 | { 25 | var point = ray.GetCollisionPoint(); 26 | var target = mouseTarget.Transform; 27 | target.origin = point; 28 | mouseTarget.Transform = target; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Demos/Arrive3d/Arrive3dDemo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=13 format=2] 2 | 3 | [ext_resource path="res://Demos/Arrive3d/Arrive3dDemo.cs" type="Script" id=1] 4 | [ext_resource path="res://Demos/Arrive3d/Arriver.cs" type="Script" id=2] 5 | [ext_resource path="res://Demos/Utils/SceneDesc.tscn" type="PackedScene" id=3] 6 | 7 | [sub_resource type="PlaneMesh" id=1] 8 | 9 | [sub_resource type="SpatialMaterial" id=2] 10 | albedo_color = Color( 0.0941176, 0.235294, 0.486275, 1 ) 11 | 12 | [sub_resource type="BoxShape" id=3] 13 | 14 | [sub_resource type="CapsuleShape" id=4] 15 | 16 | [sub_resource type="CapsuleMesh" id=5] 17 | 18 | [sub_resource type="SpatialMaterial" id=6] 19 | albedo_color = Color( 0.152941, 0.764706, 0.247059, 1 ) 20 | 21 | [sub_resource type="CubeMesh" id=7] 22 | 23 | [sub_resource type="CylinderMesh" id=8] 24 | 25 | [sub_resource type="SpatialMaterial" id=9] 26 | albedo_color = Color( 0.945098, 0.85098, 0.0745098, 1 ) 27 | 28 | [node name="Arrive3dDemo" type="Node"] 29 | script = ExtResource( 1 ) 30 | 31 | [node name="Ground" type="MeshInstance" parent="."] 32 | transform = Transform( 272.536, 0, 0, 0, 1, 0, 0, 0, 272.536, 0, 0, 0 ) 33 | mesh = SubResource( 1 ) 34 | material/0 = SubResource( 2 ) 35 | 36 | [node name="StaticBody" type="StaticBody" parent="Ground"] 37 | transform = Transform( 0.00366924, 0, 0, 0, 1, 0, 0, 0, 0.00366924, 0, 0, 0 ) 38 | 39 | [node name="CollisionShape" type="CollisionShape" parent="Ground/StaticBody"] 40 | transform = Transform( 270.816, 0, 0, 0, 0.1, 0, 0, 0, 270.816, 0, 0, 0 ) 41 | shape = SubResource( 3 ) 42 | 43 | [node name="Camera" type="Camera" parent="."] 44 | transform = Transform( 0.989771, 0.06892, -0.124912, 0.0376473, 0.718354, 0.694658, 0.137607, -0.692255, 0.708411, -7.5, 14, 25.5 ) 45 | current = true 46 | 47 | [node name="RayCast" type="RayCast" parent="Camera"] 48 | 49 | [node name="Arriver" type="KinematicBody" parent="."] 50 | collision_layer = 0 51 | collision_mask = 0 52 | script = ExtResource( 2 ) 53 | 54 | [node name="CollisionShape" type="CollisionShape" parent="Arriver"] 55 | transform = Transform( 1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 1.5, 0 ) 56 | shape = SubResource( 4 ) 57 | 58 | [node name="Capsule" type="MeshInstance" parent="Arriver"] 59 | transform = Transform( 1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 1.5, 0 ) 60 | mesh = SubResource( 5 ) 61 | material/0 = SubResource( 6 ) 62 | 63 | [node name="Nose" type="MeshInstance" parent="Arriver"] 64 | transform = Transform( 0.15, 0, 0, 0, 0.2, 0, 0, 0, 0.4, 0, 2, 1.25 ) 65 | mesh = SubResource( 7 ) 66 | material/0 = SubResource( 6 ) 67 | 68 | [node name="DirectionalLight" type="DirectionalLight" parent="."] 69 | transform = Transform( -0.536337, 0.521752, -0.663414, -0.836516, -0.224144, 0.5, 0.112176, 0.823125, 0.55667, 0, 100, 0 ) 70 | light_energy = 0.5 71 | shadow_enabled = true 72 | 73 | [node name="MouseTarget" type="Spatial" parent="."] 74 | 75 | [node name="MeshInstance" type="MeshInstance" parent="MouseTarget"] 76 | transform = Transform( 2, 0, 0, 0, 0.05, 0, 0, 0, 2, 0, 0, 0 ) 77 | mesh = SubResource( 8 ) 78 | material/0 = SubResource( 9 ) 79 | 80 | [node name="SceneDesc" parent="." instance=ExtResource( 3 )] 81 | Description = "3D Arrive Demo 82 | Move the mouse about the field to have the agent turn towards and smoothly arrive at the target marker." 83 | -------------------------------------------------------------------------------- /Demos/Arrive3d/Arriver.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using GodotSteeringAI; 4 | 5 | public class Arriver : KinematicBody 6 | { 7 | [Export] private float linear_speed_max = 50; 8 | [Export] private float linear_acceleration_max = 50; 9 | [Export] private float arrival_tolerance = 0.01f; 10 | [Export] private float deceleration_radius = 10; 11 | [Export] private float angular_speed_max = 30; 12 | [Export] private float angular_accel_max = 15; 13 | [Export] private float align_tolerance = 0.5f; 14 | [Export] private float angular_deceleration_radius = 60; 15 | 16 | private GSAIKinematicBody3DAgent agent; 17 | private GSAIAgentLocation target; 18 | private GSAITargetAcceleration accel; 19 | private GSAIBlend blend; 20 | private GSAIFace face; 21 | private GSAIArrive arrive; 22 | 23 | private Spatial target_node; 24 | 25 | public override void _Ready() 26 | { 27 | target_node = GetNode("../MouseTarget"); 28 | agent = new GSAIKinematicBody3DAgent(this); 29 | target = new GSAIAgentLocation(); 30 | accel = new GSAITargetAcceleration(); 31 | blend = new GSAIBlend(agent); 32 | face = new GSAIFace(agent, target, true); 33 | arrive = new GSAIArrive(agent, target); 34 | 35 | 36 | agent.LinearSpeedMax = linear_speed_max; 37 | agent.LinearAccelerationMax = linear_acceleration_max; 38 | agent.LinearDragPercentage = 0.05f; 39 | agent.AngularSpeedMax = angular_speed_max; 40 | agent.AngularAccelerationMax = angular_accel_max; 41 | agent.AngularDragPercentage = 0.1f; 42 | 43 | arrive.ArrivalTolerance = arrival_tolerance; 44 | arrive.DecelerationRadius = deceleration_radius; 45 | 46 | face.AlignmentTolerance = Mathf.Deg2Rad(align_tolerance); 47 | face.DecelerationRadius = Mathf.Deg2Rad(angular_deceleration_radius); 48 | 49 | target.Position = target_node.Transform.origin; 50 | blend.Add(arrive, 1); 51 | blend.Add(face, 1); 52 | } 53 | 54 | public override void _PhysicsProcess(float delta) 55 | { 56 | var pos = target_node.Transform.origin; 57 | pos.y = Transform.origin.y; 58 | target.Position = pos; 59 | blend.CalculateSteering(accel); 60 | agent._ApplySteering(accel, delta); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Demos/AvoidCollisions/AvoidCollisionsDemo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://Demos/Utils/Background.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://Demos/AvoidCollisions/Spawner.cs" type="Script" id=2] 5 | [ext_resource path="res://Demos/AvoidCollisions/Avoider.tscn" type="PackedScene" id=3] 6 | [ext_resource path="res://Demos/Utils/SceneDesc.tscn" type="PackedScene" id=4] 7 | 8 | [node name="AvoidCollisionsDemo" type="Node"] 9 | 10 | [node name="Background" parent="." instance=ExtResource( 1 )] 11 | 12 | [node name="Spawner" type="Node2D" parent="."] 13 | script = ExtResource( 2 ) 14 | linear_speed_max = 2700.0 15 | linear_acceleration_max = 2150.0 16 | proximity_radius = 150.0 17 | avoider_template = ExtResource( 3 ) 18 | inner_color = Color( 0.235294, 0.639216, 0.439216, 1 ) 19 | outer_color = Color( 0.560784, 0.870588, 0.364706, 1 ) 20 | 21 | [node name="SceneDesc" parent="." instance=ExtResource( 4 )] 22 | modulate = Color( 1, 1, 1, 0.564706 ) 23 | Description = "Avoid Collisions Demo 24 | Watch each agent try to keep traveling in a particular direction, but prioritize avoiding collisions with other agents." 25 | -------------------------------------------------------------------------------- /Demos/AvoidCollisions/Avoider.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using GodotSteeringAI; 4 | using System.Collections.Generic; 5 | 6 | public class Avoider : KinematicBody2D 7 | { 8 | private static Random random = new Random(); 9 | 10 | private static float GetRand(float min, float max) 11 | { 12 | var t = (float)random.NextDouble(); 13 | return t * (max - min) + min; 14 | } 15 | 16 | private bool draw_proximity; 17 | 18 | private float _boundary_right; 19 | private float _boundary_bottom; 20 | private float _radius; 21 | private GSAITargetAcceleration _accel = new GSAITargetAcceleration(); 22 | private Vector2 _direction; 23 | private float _drag = 0.1f; 24 | private Color _color = new Color(0.4f, 1.0f, 0.89f, 0.3f); 25 | 26 | public PlayerOutlook collision; 27 | public GSAIKinematicBody2DAgent agent; 28 | private GSAIRadiusProximity proximity; 29 | private GSAIAvoidCollisions avoid; 30 | private GSAIAgentLocation target; 31 | private GSAISeek seek; 32 | private GSAIPriority priority; 33 | 34 | public override void _Ready() 35 | { 36 | collision = GetNode("CollisionShape2D"); 37 | agent = new GSAIKinematicBody2DAgent(this); 38 | proximity = new GSAIRadiusProximity(agent, new List(), 140); 39 | avoid = new GSAIAvoidCollisions(agent, proximity); 40 | target = new GSAIAgentLocation(); 41 | seek = new GSAISeek(agent, target); 42 | priority = new GSAIPriority(agent, 0.0001f); 43 | } 44 | 45 | public override void _Draw() 46 | { 47 | if (draw_proximity) 48 | { 49 | DrawCircle(Vector2.Zero, proximity.Radius, _color); 50 | } 51 | } 52 | 53 | public override void _PhysicsProcess(float delta) 54 | { 55 | var pos = target.Position; 56 | pos.x = agent.Position.x + _direction.x * _radius; 57 | pos.y = agent.Position.y + _direction.y * _radius; 58 | target.Position = pos; 59 | 60 | priority.CalculateSteering(_accel); 61 | agent._ApplySteering(_accel, delta); 62 | } 63 | 64 | public void Setup( 65 | float linear_speed_max, float linear_accel_max, float proximity_radius, 66 | float boundary_right, float boundary_bottom, bool _draw_proximity) 67 | { 68 | _direction = new Vector2(GetRand(-1, 1), GetRand(-1, 1)).Normalized(); 69 | 70 | agent.LinearSpeedMax = linear_speed_max; 71 | agent.LinearAccelerationMax = linear_accel_max; 72 | 73 | proximity.Radius = proximity_radius; 74 | _boundary_bottom = boundary_bottom; 75 | _boundary_right = boundary_right; 76 | 77 | _radius = (collision.Shape as CircleShape2D).Radius; 78 | agent.BoundingRadius = _radius; 79 | 80 | agent.LinearDragPercentage = _drag; 81 | 82 | draw_proximity = _draw_proximity; 83 | 84 | priority.Add(avoid); 85 | priority.Add(seek); 86 | } 87 | 88 | public void SetProximityAgents(List agents) 89 | { 90 | proximity.Agents = agents; 91 | } 92 | 93 | public void SetRandomNonoverlappingPosition(IList others, float distance_from_boundary_min) 94 | { 95 | var tries_max = Math.Max(100, others.Count * others.Count); 96 | while (tries_max > 0) 97 | { 98 | tries_max--; 99 | 100 | var pos = GlobalPosition; 101 | pos.x = GetRand(distance_from_boundary_min, _boundary_right - distance_from_boundary_min); 102 | pos.y = GetRand(distance_from_boundary_min, _boundary_bottom - distance_from_boundary_min); 103 | GlobalPosition = pos; 104 | 105 | var done = true; 106 | foreach (var other in others) 107 | { 108 | if (other.GlobalPosition.DistanceTo(pos) <= _radius * 2 + distance_from_boundary_min) 109 | { 110 | done = false; break; 111 | } 112 | } 113 | if (done) break; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Demos/AvoidCollisions/Avoider.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://Demos/AvoidCollisions/Avoider.cs" type="Script" id=1] 4 | [ext_resource path="res://Demos/Utils/PlayerOutlook.cs" type="Script" id=2] 5 | 6 | [sub_resource type="CircleShape2D" id=1] 7 | radius = 24.975 8 | 9 | [node name="Avoider" type="KinematicBody2D"] 10 | script = ExtResource( 1 ) 11 | 12 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 13 | shape = SubResource( 1 ) 14 | script = ExtResource( 2 ) 15 | InnerColor = Color( 0.890196, 0.411765, 0.337255, 1 ) 16 | OuterColor = Color( 1, 0.709804, 0.439216, 1 ) 17 | -------------------------------------------------------------------------------- /Demos/AvoidCollisions/Spawner.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using System.Collections.Generic; 4 | using GodotSteeringAI; 5 | 6 | public class Spawner : Node2D 7 | { 8 | [Export] private float linear_speed_max = 350; 9 | [Export] private float linear_acceleration_max = 40; 10 | [Export] private float proximity_radius = 140; 11 | [Export] private bool draw_proximity = true; 12 | 13 | [Export] private PackedScene avoider_template; 14 | [Export] private Color inner_color = new Color(); 15 | [Export] private Color outer_color = new Color(); 16 | [Export] private int agent_count = 60; 17 | 18 | private Vector2 boundaries; 19 | 20 | private List avoiders; 21 | 22 | public override void _Ready() 23 | { 24 | boundaries = new Vector2( 25 | (float)(ProjectSettings.GetSetting("display/window/size/width") as int?), 26 | (float)(ProjectSettings.GetSetting("display/window/size/height") as int?)); 27 | 28 | avoiders = new List(); 29 | var avoider_agents = new List(); 30 | for (int i = 0; i < agent_count; i++) 31 | { 32 | var avoider = avoider_template.Instance() as Avoider; 33 | AddChild(avoider); 34 | avoider.Setup(linear_speed_max, linear_acceleration_max, proximity_radius, 35 | boundaries.x, boundaries.y, i == 0 && draw_proximity); 36 | avoider_agents.Add(avoider.agent); 37 | avoider.SetRandomNonoverlappingPosition(avoiders, 16); 38 | if (i == 0) 39 | { 40 | avoider.collision.InnerColor = inner_color; 41 | avoider.collision.OuterColor = outer_color; 42 | avoider.collision.Update(); 43 | } 44 | avoiders.Add(avoider); 45 | } 46 | foreach (var avoider in avoiders) 47 | { 48 | avoider.SetProximityAgents(avoider_agents); 49 | } 50 | } 51 | 52 | public override void _PhysicsProcess(float delta) 53 | { 54 | foreach (var avoider in avoiders) 55 | { 56 | avoider.GlobalPosition = avoider.GlobalPosition.PosMod(boundaries); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Demos/Face/FaceDemo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=9 format=2] 2 | 3 | [ext_resource path="res://Demos/Utils/Background.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://Demos/Face/TurretOutlook.cs" type="Script" id=2] 5 | [ext_resource path="res://Demos/Utils/PlayerOutlook.cs" type="Script" id=3] 6 | [ext_resource path="res://Demos/Face/TargetMouseMove.cs" type="Script" id=4] 7 | [ext_resource path="res://Demos/Face/Turret.cs" type="Script" id=5] 8 | [ext_resource path="res://Demos/Utils/SceneDesc.tscn" type="PackedScene" id=6] 9 | 10 | [sub_resource type="CircleShape2D" id=1] 11 | radius = 29.874 12 | 13 | [sub_resource type="CircleShape2D" id=2] 14 | radius = 38.6544 15 | 16 | [node name="FaceDemo" type="Node"] 17 | 18 | [node name="Background" parent="." instance=ExtResource( 1 )] 19 | 20 | [node name="Target" type="StaticBody2D" parent="."] 21 | position = Vector2( 350, 350 ) 22 | collision_layer = 0 23 | collision_mask = 0 24 | script = ExtResource( 4 ) 25 | 26 | [node name="Outlook" type="CollisionShape2D" parent="Target"] 27 | shape = SubResource( 1 ) 28 | disabled = true 29 | script = ExtResource( 3 ) 30 | 31 | [node name="Turret" type="KinematicBody2D" parent="."] 32 | position = Vector2( 960, 540 ) 33 | script = ExtResource( 5 ) 34 | __meta__ = { 35 | "_edit_group_": true 36 | } 37 | 38 | [node name="TurretOutlook" type="CollisionShape2D" parent="Turret"] 39 | shape = SubResource( 2 ) 40 | script = ExtResource( 2 ) 41 | 42 | [node name="SceneDesc" parent="." instance=ExtResource( 6 )] 43 | Description = "Face Demo 44 | Move the [color=lime]green player[/color] around with MOUSE and notice the [color=#ffb570]orange turret[/color] orient itself" 45 | -------------------------------------------------------------------------------- /Demos/Face/TargetMouseMove.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | public class TargetMouseMove : Node2D 5 | { 6 | public override void _UnhandledInput(InputEvent evt) 7 | { 8 | if (evt is InputEventMouseMotion mouse) 9 | { 10 | GlobalPosition = mouse.Position; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Demos/Face/Turret.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using GodotSteeringAI; 4 | 5 | public class Turret : KinematicBody2D 6 | { 7 | [Export] private float align_tolerance = 0.5f; 8 | [Export] private float deceleration_radius = 135; 9 | [Export] private float angular_accel_max = 15; 10 | [Export] private float angular_speed_max = 50; 11 | 12 | private GSAIFace face; 13 | private GSAIKinematicBody2DAgent agent; 14 | private GSAITargetAcceleration accel; 15 | private GSAIAgentLocation target; 16 | private Node2D target_node; 17 | private float angular_drag = 0.1f; 18 | 19 | public override void _Ready() 20 | { 21 | target_node = GetNode("../Target"); 22 | target = new GSAIAgentLocation(); 23 | target.Position = GSAIUtils.ToVector3(target_node.GlobalPosition); 24 | agent = new GSAIKinematicBody2DAgent(this); 25 | face = new GSAIFace(agent, target); 26 | accel = new GSAITargetAcceleration(); 27 | 28 | face.AlignmentTolerance = Mathf.Deg2Rad(align_tolerance); 29 | face.DecelerationRadius = Mathf.Deg2Rad(deceleration_radius); 30 | 31 | agent.AngularAccelerationMax = angular_accel_max; 32 | agent.AngularSpeedMax = angular_speed_max; 33 | agent.AngularDragPercentage = angular_drag; 34 | } 35 | 36 | public override void _PhysicsProcess(float delta) 37 | { 38 | target.Position = GSAIUtils.ToVector3(target_node.GlobalPosition); 39 | face.CalculateSteering(accel); 40 | agent._ApplySteering(accel, delta); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Demos/Face/TurretOutlook.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | public class TurretOutlook : CollisionShape2D 5 | { 6 | [Export] private Color inner_color = new Color(0.890196f, 0.411765f, 0.337255f); 7 | [Export] private Color outer_color = new Color(1, 0.709804f, 0.439216f); 8 | [Export] private float stroke = 8; 9 | 10 | public override void _Draw() 11 | { 12 | var rad = (Shape as CircleShape2D).Radius; 13 | var rect = new Rect2(new Vector2(-5, 0), new Vector2(10, -rad * 2)); 14 | DrawRect(rect, outer_color); 15 | DrawCircle(Vector2.Zero, rad + stroke, outer_color); 16 | DrawCircle(Vector2.Zero, rad, inner_color); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Demos/FollowPath/FollowPathDemo.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using GodotSteeringAI; 4 | using System.Collections.Generic; 5 | using Godot.Collections; 6 | 7 | public class FollowPathDemo : Node 8 | { 9 | [Export] private float linear_speed_max = 920; 10 | [Export] private float linear_acceleration_max = 3740; 11 | [Export] private float arrival_tolerance = 10; 12 | [Export] private float deceleration_radius = 200; 13 | [Export] private float predict_time = 0.3f; 14 | [Export] private float path_offset = 20; 15 | 16 | private KinematicBody2D pathFollower; 17 | public override void _Ready() 18 | { 19 | var drawer = GetNode("PathDrawer"); 20 | pathFollower = GetNode("PathFollower"); 21 | agent = new GSAIKinematicBody2DAgent(pathFollower); 22 | _accel = new GSAITargetAcceleration(); 23 | path = new GSAIPath(new List(new Vector3[] {Vector3.Zero, Vector3.Zero}), true); 24 | follow = new GSAIFollowPath(agent, path); 25 | 26 | agent.LinearAccelerationMax = linear_acceleration_max; 27 | agent.LinearSpeedMax = linear_speed_max; 28 | agent.LinearDragPercentage = _drag; 29 | 30 | follow.PathOffset = path_offset; 31 | follow.PredictionTime = predict_time; 32 | follow.DecelerationRadius = deceleration_radius; 33 | follow.ArrivalTolerance = arrival_tolerance; 34 | 35 | drawer.PathEstablishedSignal += _on_Drawer_path_established; 36 | } 37 | 38 | private bool _valid = false; 39 | private float _drag = 0.1f; 40 | private GSAITargetAcceleration _accel; 41 | private GSAIKinematicBody2DAgent agent; 42 | private GSAIFollowPath follow; 43 | private GSAIPath path; 44 | 45 | public override void _PhysicsProcess(float delta) 46 | { 47 | if (_valid) 48 | { 49 | follow.CalculateSteering(_accel); 50 | agent._ApplySteering(_accel, delta); 51 | } 52 | } 53 | 54 | private void _on_Drawer_path_established(List points) 55 | { 56 | var _waypoints = new Vector3[points.Count]; 57 | for (int i = 0; i < points.Count; i++) 58 | { 59 | _waypoints[i] = GSAIUtils.ToVector3(points[i]); 60 | } 61 | var waypoints = new List(_waypoints); 62 | path.CreatePath(waypoints); 63 | _valid = true; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Demos/FollowPath/FollowPathDemo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=2] 2 | 3 | [ext_resource path="res://Demos/Utils/Background.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://Demos/FollowPath/PathDrawer.cs" type="Script" id=2] 5 | [ext_resource path="res://Demos/Utils/Player.tscn" type="PackedScene" id=3] 6 | [ext_resource path="res://Demos/FollowPath/FollowPathDemo.cs" type="Script" id=4] 7 | [ext_resource path="res://Demos/Utils/SceneDesc.tscn" type="PackedScene" id=5] 8 | 9 | [node name="FollowPathDemo" type="Node"] 10 | script = ExtResource( 4 ) 11 | 12 | [node name="Background" parent="." instance=ExtResource( 1 )] 13 | 14 | [node name="PathDrawer" type="Node2D" parent="."] 15 | script = ExtResource( 2 ) 16 | 17 | [node name="PathFollower" parent="." instance=ExtResource( 3 )] 18 | 19 | [node name="SceneDesc" parent="." instance=ExtResource( 5 )] 20 | Description = "Follow Path Demo 21 | Use the mouse to draw a path on screen and watch the [color=lime]green \"Agent\"[/color] follow it to the end." 22 | -------------------------------------------------------------------------------- /Demos/FollowPath/PathDrawer.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | public class PathDrawer : Node2D 6 | { 7 | public delegate void PathEstablished(List points); 8 | public PathEstablished PathEstablishedSignal { get; set; } 9 | 10 | private List active_points = new List(256); 11 | private List tmp_points = new List(256); 12 | private bool is_drawing = false; 13 | private float distance_threshold = 100; 14 | 15 | public override void _UnhandledInput(InputEvent evt) 16 | { 17 | if (evt is InputEventMouseMotion mouseMove) 18 | { 19 | if (is_drawing) 20 | { 21 | active_points.Add(mouseMove.Position); 22 | Update(); 23 | } 24 | } 25 | else if (evt is InputEventMouseButton mouseBtn) 26 | { 27 | if (mouseBtn.Pressed) 28 | { 29 | if (mouseBtn.ButtonIndex == (int)ButtonList.Left) 30 | { 31 | is_drawing = true; 32 | active_points.Clear(); 33 | active_points.Add(mouseBtn.Position); 34 | Update(); 35 | } 36 | } 37 | else 38 | { 39 | is_drawing = false; 40 | if (active_points.Count >= 2) 41 | _Simplify(); 42 | } 43 | } 44 | } 45 | 46 | public override void _Draw() 47 | { 48 | if (is_drawing) 49 | { 50 | foreach (var point in active_points) 51 | { 52 | DrawCircle(point, 2, Color.ColorN("red")); 53 | } 54 | } 55 | else if (active_points.Count > 0) 56 | { 57 | DrawCircle(active_points[0], 5, Color.ColorN("red")); 58 | DrawCircle(active_points[active_points.Count - 1], 5, Color.ColorN("yellow")); 59 | DrawPolyline(active_points.ToArray(), Color.ColorN("skyblue"), 2); 60 | } 61 | } 62 | 63 | private void _Simplify() 64 | { 65 | var first = active_points[0]; 66 | var last = active_points[active_points.Count - 1]; 67 | var key = first; 68 | tmp_points.Clear(); 69 | tmp_points.Add(first); 70 | foreach (var point in active_points) 71 | { 72 | var distance = point.DistanceTo(key); 73 | if (distance > distance_threshold) 74 | { 75 | key = point; 76 | tmp_points.Add(key); 77 | } 78 | } 79 | var t = tmp_points; 80 | tmp_points = active_points; 81 | active_points = t; 82 | if (active_points[active_points.Count - 1] != last) 83 | active_points.Add(last); 84 | Update(); 85 | PathEstablishedSignal?.Invoke(active_points); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Demos/GroupBehaviors/GroupBehaviorsDemo.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://Demos/GroupBehaviors/GrpSpawner.cs" type="Script" id=1] 4 | [ext_resource path="res://Demos/Utils/Background.tscn" type="PackedScene" id=2] 5 | [ext_resource path="res://Demos/GroupBehaviors/Member.tscn" type="PackedScene" id=3] 6 | [ext_resource path="res://Demos/Utils/SceneDesc.tscn" type="PackedScene" id=4] 7 | 8 | [node name="GroupBehaviorsDemo" type="Node"] 9 | 10 | [node name="Background" parent="." instance=ExtResource( 2 )] 11 | 12 | [node name="GrpSpawner" type="Node2D" parent="."] 13 | position = Vector2( 960, 540 ) 14 | script = ExtResource( 1 ) 15 | linear_speed_max = 1800.0 16 | linear_accel_max = 1200.0 17 | proximity_radius = 250.0 18 | separation_decay_coefficient = 121500.0 19 | cohesion_strength = 0.2 20 | separation_strength = 6.6 21 | memberScene = ExtResource( 3 ) 22 | 23 | [node name="SceneDesc" parent="." instance=ExtResource( 4 )] 24 | modulate = Color( 1, 1, 1, 0.564706 ) 25 | margin_bottom = 175.0 26 | Description = "Group Behavior Demo 27 | Each of the \"Agents\" are both attempting to stay separated from each other but within reach of their nearest group's center of mass. Click on agent to see it's proximity." 28 | -------------------------------------------------------------------------------- /Demos/GroupBehaviors/GrpSpawner.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using GodotSteeringAI; 4 | using System.Collections.Generic; 5 | 6 | public class GrpSpawner : Node2D 7 | { 8 | [Export] private float linear_speed_max = 600; 9 | [Export] private float linear_accel_max = 40; 10 | [Export] private float proximity_radius = 140; 11 | [Export] private float separation_decay_coefficient = 2000; 12 | [Export] private float cohesion_strength = 0.1f; 13 | [Export] private float separation_strength = 1.5f; 14 | [Export] private bool show_proximity_radius = true; 15 | 16 | [Export] private PackedScene memberScene; 17 | 18 | private void follower_input_event(Viewport node, InputEvent input, int shape, Member follower) 19 | { 20 | if (input.IsActionPressed("click")) 21 | { 22 | foreach (Member other in GetChildren()) 23 | { 24 | if (other.draw_proximity) 25 | { 26 | other.draw_proximity = false; 27 | other.Update(); 28 | } 29 | } 30 | follower.draw_proximity = true; 31 | follower.Update(); 32 | MoveChild(follower, GetChildCount()); 33 | } 34 | } 35 | 36 | public override void _Ready() 37 | { 38 | var followers = new List(); 39 | 40 | for (int i = 0; i < 20; i++) 41 | { 42 | var follower = memberScene.Instance() as Member; 43 | AddChild(follower); 44 | follower.Position += new Vector2(Member.GetRand(-60, 60), Member.GetRand(-60, 60)); 45 | follower.Setup(linear_speed_max, linear_accel_max, proximity_radius, 46 | separation_decay_coefficient, cohesion_strength, separation_strength); 47 | followers.Add(follower.agent); 48 | if (i == 0 && show_proximity_radius) 49 | { 50 | follower.draw_proximity = true; 51 | follower.Update(); 52 | } 53 | follower.proximity.Agents = followers; 54 | follower.Connect("input_event", this, nameof(follower_input_event), 55 | new Godot.Collections.Array { follower }); 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Demos/GroupBehaviors/Member.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using GodotSteeringAI; 4 | using System.Collections.Generic; 5 | 6 | public class Member : KinematicBody2D 7 | { 8 | private static Random random = new Random(); 9 | 10 | public static float GetRand(float min, float max) 11 | { 12 | var t = (float)random.NextDouble(); 13 | return t * (max - min) + min; 14 | } 15 | 16 | private MemberOutlook outlook; 17 | 18 | private GSAISeparation separation; 19 | private GSAICohesion cohesion; 20 | public GSAIRadiusProximity proximity; 21 | public GSAIKinematicBody2DAgent agent; 22 | private GSAIBlend blend; 23 | private GSAITargetAcceleration acceleration; 24 | public bool draw_proximity = false; 25 | 26 | 27 | public override void _Draw() 28 | { 29 | if (draw_proximity) 30 | DrawCircle(Vector2.Zero, proximity.Radius, new Color(0.4f, 1, 0.89f, 0.3f)); 31 | } 32 | 33 | public override void _Ready() 34 | { 35 | outlook = GetNode("MemberOutlook"); 36 | } 37 | 38 | public void Setup(float linear_speed_max, float linear_accel_max, float proximity_radius, 39 | float separation_decay_coefficient, float cohesion_strength, float separation_strength) 40 | { 41 | outlook.SetShapeColor(new Color(GetRand(0.5f, 1), GetRand(0.25f, 1), GetRand(0, 1)), outlook.outer_color); 42 | 43 | acceleration = new GSAITargetAcceleration(); 44 | 45 | agent = new GSAIKinematicBody2DAgent(this); 46 | agent.LinearAccelerationMax = linear_accel_max; 47 | agent.LinearSpeedMax = linear_speed_max; 48 | agent.LinearDragPercentage = 0.1f; 49 | 50 | proximity = new GSAIRadiusProximity(agent, new List(), proximity_radius); 51 | separation = new GSAISeparation(agent, proximity); 52 | separation.DecayCoefficient = separation_decay_coefficient; 53 | cohesion = new GSAICohesion(agent, proximity); 54 | blend = new GSAIBlend(agent); 55 | blend.Add(separation, separation_strength); 56 | blend.Add(cohesion, cohesion_strength); 57 | } 58 | 59 | public override void _PhysicsProcess(float delta) 60 | { 61 | if (blend is null) 62 | return; 63 | blend.CalculateSteering(acceleration); 64 | agent._ApplySteering(acceleration, delta); 65 | } 66 | 67 | public void SetNeighbors(List neighbor) 68 | { 69 | proximity.Agents = neighbor; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Demos/GroupBehaviors/Member.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://Demos/GroupBehaviors/Member.cs" type="Script" id=1] 4 | [ext_resource path="res://Demos/GroupBehaviors/MemberOutlook.cs" type="Script" id=2] 5 | 6 | [sub_resource type="CircleShape2D" id=1] 7 | radius = 15.69 8 | 9 | [node name="Member" type="KinematicBody2D"] 10 | input_pickable = true 11 | script = ExtResource( 1 ) 12 | 13 | [node name="MemberOutlook" type="CollisionShape2D" parent="."] 14 | shape = SubResource( 1 ) 15 | script = ExtResource( 2 ) 16 | -------------------------------------------------------------------------------- /Demos/GroupBehaviors/MemberOutlook.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | public class MemberOutlook : CollisionShape2D 5 | { 6 | 7 | [Export] public Color inner_color = new Color(0, 0, 0); 8 | [Export] public Color outer_color = new Color(0.301961f, 0.65098f, 1); 9 | [Export] public float stroke = 5; 10 | 11 | public void SetShapeColor(Color inner, Color outer) 12 | { 13 | inner_color = inner; 14 | outer_color = outer; 15 | Update(); 16 | } 17 | 18 | public override void _Draw() 19 | { 20 | var shape = Shape as CircleShape2D; 21 | DrawCircle(Vector2.Zero, shape.Radius + stroke, outer_color); 22 | DrawCircle(Vector2.Zero, shape.Radius, inner_color); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Demos/Main/DemoMain.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | public class DemoMain : Node 6 | { 7 | private ItemList demoList; 8 | 9 | private List demoPaths; 10 | 11 | private List hidenList; 12 | private Button goBackBtn; 13 | 14 | private Button loadBtn; 15 | 16 | private Node2D demo_player; 17 | 18 | public override void _Ready() 19 | { 20 | demo_player = GetNode("DemoPlayer"); 21 | 22 | InitDemoList(); 23 | 24 | InitHideShowList(); 25 | } 26 | 27 | private void InitHideShowList() 28 | { 29 | goBackBtn = GetNode