mirror of
https://github.com/Relintai/godot-steering-ai-framework.git
synced 2025-03-09 07:46:58 +01:00
Added the full project again into a different folder.
This commit is contained in:
parent
2a6d8ba550
commit
8ac2c60a4d
51
project_gdscript/Demos/Arrive/ArriveDemo.gd
Normal file
51
project_gdscript/Demos/Arrive/ArriveDemo.gd
Normal file
@ -0,0 +1,51 @@
|
||||
extends Node
|
||||
|
||||
export (float, 0, 3200, 100) var linear_speed_max := 800.0 setget set_linear_speed_max
|
||||
export (float, 0, 10000, 100) var linear_acceleration_max := 80.0 setget set_linear_acceleration_max
|
||||
export (float, 0, 100, 0.1) var arrival_tolerance := 25.0 setget set_arrival_tolerance
|
||||
export (float, 0, 500, 10) var deceleration_radius := 125.0 setget set_deceleration_radius
|
||||
|
||||
onready var arriver := $Arriver
|
||||
onready var target_drawer := $TargetDrawer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
arriver.setup(linear_speed_max, linear_acceleration_max, arrival_tolerance, deceleration_radius)
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.is_pressed():
|
||||
arriver.target.position = Vector3(event.position.x, event.position.y, 0)
|
||||
target_drawer.update()
|
||||
|
||||
|
||||
func set_arrival_tolerance(value: float) -> void:
|
||||
arrival_tolerance = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.arrive.arrival_tolerance = value
|
||||
|
||||
|
||||
func set_deceleration_radius(value: float) -> void:
|
||||
deceleration_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.arrive.deceleration_radius = value
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
linear_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.agent.linear_speed_max = value
|
||||
|
||||
|
||||
func set_linear_acceleration_max(value: float) -> void:
|
||||
linear_acceleration_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.agent.linear_acceleration_max = value
|
39
project_gdscript/Demos/Arrive/ArriveDemo.tscn
Normal file
39
project_gdscript/Demos/Arrive/ArriveDemo.tscn
Normal file
@ -0,0 +1,39 @@
|
||||
[gd_scene load_steps=8 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/Arrive/Arriver.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://Demos/Arrive/ArriveDemo.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://Demos/Arrive/TargetDrawer.gd" type="Script" id=5]
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=6]
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 23.2163
|
||||
|
||||
[node name="ArriveDemo" type="Node"]
|
||||
script = ExtResource( 3 )
|
||||
linear_speed_max = 1600.0
|
||||
linear_acceleration_max = 5000.0
|
||||
arrival_tolerance = 35.0
|
||||
deceleration_radius = 180.0
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 4 )]
|
||||
|
||||
[node name="TargetDrawer" type="Node2D" parent="."]
|
||||
script = ExtResource( 5 )
|
||||
|
||||
[node name="Arriver" type="KinematicBody2D" parent="."]
|
||||
show_behind_parent = true
|
||||
position = Vector2( 960, 540 )
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Arriver"]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 6 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
outer_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
stroke = 6.0
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 2 )]
|
||||
text_bbcode = "Arrive Demo
|
||||
Mouse click to make the [color=lime]green \"Player\"[/color] move to the [color=fuchsia]purple target[/color]"
|
32
project_gdscript/Demos/Arrive/Arriver.gd
Normal file
32
project_gdscript/Demos/Arrive/Arriver.gd
Normal file
@ -0,0 +1,32 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
var agent := GSAIKinematicBody2DAgent.new()
|
||||
var target := GSAIAgentLocation.new()
|
||||
var arrive := GSAIArrive.new()
|
||||
var _accel := GSAITargetAcceleration.new()
|
||||
|
||||
var _velocity := Vector2()
|
||||
var _drag := 0.1
|
||||
|
||||
func _init() -> void:
|
||||
agent.body = self
|
||||
arrive.agent = agent
|
||||
arrive.target = target
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
arrive.calculate_steering(_accel)
|
||||
agent.apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func setup(
|
||||
linear_speed_max: float,
|
||||
linear_acceleration_max: float,
|
||||
arrival_tolerance: float,
|
||||
deceleration_radius: float
|
||||
) -> void:
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
agent.linear_acceleration_max = linear_acceleration_max
|
||||
agent.linear_drag_percentage = _drag
|
||||
arrive.deceleration_radius = deceleration_radius
|
||||
arrive.arrival_tolerance = arrival_tolerance
|
||||
target.position = agent.position
|
17
project_gdscript/Demos/Arrive/TargetDrawer.gd
Normal file
17
project_gdscript/Demos/Arrive/TargetDrawer.gd
Normal file
@ -0,0 +1,17 @@
|
||||
extends Node2D
|
||||
|
||||
var deceleration_radius_color : Color = Color(1.0, 0.419, 0.592, 0.5)
|
||||
var arrival_tolerance_color : Color = Color(0.278, 0.231, 0.47, 0.3)
|
||||
|
||||
var arriver: Node2D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
yield(owner, "ready")
|
||||
arriver = owner.arriver
|
||||
|
||||
|
||||
func _draw():
|
||||
var target_position := GSAIUtils.to_vector2(arriver.target.position)
|
||||
draw_circle(target_position, owner.deceleration_radius, deceleration_radius_color)
|
||||
draw_circle(target_position, owner.arrival_tolerance, arrival_tolerance_color)
|
96
project_gdscript/Demos/Arrive3d/Arrive3dDemo.tscn
Normal file
96
project_gdscript/Demos/Arrive3d/Arrive3dDemo.tscn
Normal file
@ -0,0 +1,96 @@
|
||||
[gd_scene load_steps=14 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://Demos/Arrive3d/Camera.gd" type="Script" id=2]
|
||||
[ext_resource path="res://Demos/Arrive3d/Seek3dDemo.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/Arrive3d/Seeker.gd" type="Script" id=4]
|
||||
[ext_resource path="res://Demos/Arrive3d/SeekerMat.tres" type="Material" id=5]
|
||||
|
||||
[sub_resource type="CapsuleShape" id=1]
|
||||
|
||||
[sub_resource type="CapsuleMesh" id=2]
|
||||
|
||||
[sub_resource type="CubeMesh" id=3]
|
||||
material = ExtResource( 5 )
|
||||
size = Vector3( 0.5, 0.5, 1 )
|
||||
|
||||
[sub_resource type="CylinderMesh" id=4]
|
||||
top_radius = 2.0
|
||||
bottom_radius = 2.0
|
||||
height = 0.1
|
||||
|
||||
[sub_resource type="SpatialMaterial" id=5]
|
||||
albedo_color = Color( 0.945098, 0.85098, 0.0745098, 1 )
|
||||
|
||||
[sub_resource type="BoxShape" id=6]
|
||||
extents = Vector3( 1000, 0.1, 1000 )
|
||||
|
||||
[sub_resource type="PlaneMesh" id=7]
|
||||
size = Vector2( 250, 250 )
|
||||
|
||||
[sub_resource type="SpatialMaterial" id=8]
|
||||
albedo_color = Color( 0.0941176, 0.235294, 0.486275, 1 )
|
||||
|
||||
[node name="Arrive3dDemo" type="Node"]
|
||||
script = ExtResource( 3 )
|
||||
linear_speed_max = 50.0
|
||||
linear_acceleration_max = 53.2
|
||||
deceleration_radius = 10.8
|
||||
angular_speed_max = 550
|
||||
angular_accel_max = 910
|
||||
|
||||
[node name="Arriver" type="KinematicBody" parent="."]
|
||||
script = ExtResource( 4 )
|
||||
|
||||
[node name="CollisionShape" type="CollisionShape" parent="Arriver"]
|
||||
transform = Transform( 1, 0, 0, 0, -1.62921e-07, 1, 0, -1, -1.62921e-07, 0, 1.5, 0 )
|
||||
shape = SubResource( 1 )
|
||||
|
||||
[node name="Capsule" type="MeshInstance" parent="Arriver"]
|
||||
transform = Transform( 1, 0, 0, 0, -1.62921e-07, 1, 0, -1, -1.62921e-07, 0, 1.5, 0 )
|
||||
mesh = SubResource( 2 )
|
||||
material/0 = ExtResource( 5 )
|
||||
|
||||
[node name="Nose" type="MeshInstance" parent="Arriver"]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 1.25 )
|
||||
mesh = SubResource( 3 )
|
||||
material/0 = null
|
||||
|
||||
[node name="Camera" type="Camera" parent="."]
|
||||
transform = Transform( 0.989952, 0.0720094, -0.121693, 0.0339305, 0.714503, 0.69881, 0.137271, -0.695917, 0.70488, -7.68317, 14.1265, 25.616 )
|
||||
current = true
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="RayCast" type="RayCast" parent="Camera"]
|
||||
enabled = true
|
||||
cast_to = Vector3( -627, 200, -777 )
|
||||
collision_mask = 2
|
||||
|
||||
[node name="MouseTarget" type="Spatial" parent="."]
|
||||
transform = Transform( 1, 0, 7.45058e-09, 0, 1, 0, 7.45058e-09, 0, 1, -4.76837e-07, 9.53674e-07, 1.90735e-06 )
|
||||
|
||||
[node name="MeshInstance" type="MeshInstance" parent="MouseTarget"]
|
||||
mesh = SubResource( 4 )
|
||||
material/0 = SubResource( 5 )
|
||||
|
||||
[node name="StaticBody" type="StaticBody" parent="."]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.1, 0 )
|
||||
collision_layer = 2
|
||||
collision_mask = 2
|
||||
|
||||
[node name="CollisionShape" type="CollisionShape" parent="StaticBody"]
|
||||
shape = SubResource( 6 )
|
||||
|
||||
[node name="Ground" type="MeshInstance" parent="."]
|
||||
mesh = SubResource( 7 )
|
||||
material/0 = SubResource( 8 )
|
||||
|
||||
[node name="DirectionalLight" type="DirectionalLight" parent="."]
|
||||
transform = Transform( -0.588165, 0.462179, -0.663666, -0.804031, -0.245728, 0.541436, 0.087159, 0.852061, 0.516134, -17.6076, 12.1748, 0 )
|
||||
light_energy = 0.5
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 1 )]
|
||||
mouse_filter = 2
|
||||
text_bbcode = "3D Arrive Demo
|
||||
Move the mouse about the field to have the agent turn towards and smoothly arrive at the target marker."
|
24
project_gdscript/Demos/Arrive3d/Camera.gd
Normal file
24
project_gdscript/Demos/Arrive3d/Camera.gd
Normal file
@ -0,0 +1,24 @@
|
||||
extends Camera
|
||||
|
||||
var target: Spatial
|
||||
|
||||
onready var ray := $RayCast
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion:
|
||||
_set_target_position(event.position)
|
||||
|
||||
|
||||
func setup(_target: Spatial) -> void:
|
||||
self.target = _target
|
||||
_set_target_position(get_viewport().get_mouse_position())
|
||||
|
||||
|
||||
func _set_target_position(position: Vector2) -> void:
|
||||
var to = project_local_ray_normal(position) * 10000
|
||||
ray.cast_to = to
|
||||
ray.force_raycast_update()
|
||||
if ray.is_colliding():
|
||||
var point = ray.get_collision_point()
|
||||
target.transform.origin = point
|
92
project_gdscript/Demos/Arrive3d/Seek3dDemo.gd
Normal file
92
project_gdscript/Demos/Arrive3d/Seek3dDemo.gd
Normal file
@ -0,0 +1,92 @@
|
||||
extends Node
|
||||
|
||||
export (float, 0, 100, 5) var linear_speed_max := 10.0 setget set_linear_speed_max
|
||||
export (float, 0, 100, 0.1) var linear_acceleration_max := 1.0 setget set_linear_acceleration_max
|
||||
export (float, 0, 50, 0.1) var arrival_tolerance := 0.5 setget set_arrival_tolerance
|
||||
export (float, 0, 50, 0.1) var deceleration_radius := 5.0 setget set_deceleration_radius
|
||||
export (int, 0, 1080, 10) var angular_speed_max := 270 setget set_angular_speed_max
|
||||
export (int, 0, 2048, 10) var angular_accel_max := 45 setget set_angular_accel_max
|
||||
export (int, 0, 178, 2) var align_tolerance := 5 setget set_align_tolerance
|
||||
export (int, 0, 180, 2) var angular_deceleration_radius := 45 setget set_angular_deceleration_radius
|
||||
|
||||
onready var target := $MouseTarget
|
||||
onready var arriver := $Arriver
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
arriver.setup(
|
||||
deg2rad(align_tolerance),
|
||||
deg2rad(angular_deceleration_radius),
|
||||
deg2rad(angular_accel_max),
|
||||
deg2rad(angular_speed_max),
|
||||
deceleration_radius,
|
||||
arrival_tolerance,
|
||||
linear_acceleration_max,
|
||||
linear_speed_max,
|
||||
target
|
||||
)
|
||||
$Camera.setup(target)
|
||||
|
||||
|
||||
func set_align_tolerance(value: int) -> void:
|
||||
align_tolerance = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.face.alignment_tolerance = deg2rad(value)
|
||||
|
||||
|
||||
func set_angular_deceleration_radius(value: int) -> void:
|
||||
deceleration_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.face.deceleration_radius = deg2rad(value)
|
||||
|
||||
|
||||
func set_angular_accel_max(value: int) -> void:
|
||||
angular_accel_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.agent.angular_acceleration_max = deg2rad(value)
|
||||
|
||||
|
||||
func set_angular_speed_max(value: int) -> void:
|
||||
angular_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.agent.angular_speed_max = deg2rad(value)
|
||||
|
||||
|
||||
func set_arrival_tolerance(value: float) -> void:
|
||||
arrival_tolerance = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.arrive.arrival_tolerance = value
|
||||
|
||||
|
||||
func set_deceleration_radius(value: float) -> void:
|
||||
deceleration_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.arrive.deceleration_radius = value
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
linear_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.agent.linear_speed_max = value
|
||||
|
||||
|
||||
func set_linear_acceleration_max(value: float) -> void:
|
||||
linear_acceleration_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
arriver.agent.linear_acceleration_max = value
|
64
project_gdscript/Demos/Arrive3d/Seeker.gd
Normal file
64
project_gdscript/Demos/Arrive3d/Seeker.gd
Normal file
@ -0,0 +1,64 @@
|
||||
extends KinematicBody
|
||||
|
||||
var target_node: Spatial
|
||||
|
||||
var agent : GSAIKinematicBody3DAgent = null
|
||||
var target : GSAIAgentLocation = null
|
||||
var accel : GSAITargetAcceleration = null
|
||||
var blend : GSAIBlend = null
|
||||
var face : GSAIFace = null
|
||||
var arrive : GSAIArrive = null
|
||||
|
||||
func _init() -> void:
|
||||
agent = GSAIKinematicBody3DAgent.new()
|
||||
agent.body = self
|
||||
|
||||
target = GSAIAgentLocation.new()
|
||||
accel = GSAITargetAcceleration.new()
|
||||
blend = GSAIBlend.new()
|
||||
blend.agent = agent
|
||||
|
||||
face = GSAIFace.new()
|
||||
face.agent = agent
|
||||
face.target = target
|
||||
face.use_z = true
|
||||
|
||||
arrive = GSAIArrive.new()
|
||||
arrive.agent = agent
|
||||
arrive.target = target
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
target.position = target_node.transform.origin
|
||||
target.position.y = transform.origin.y
|
||||
blend.calculate_steering(accel)
|
||||
agent.apply_steering(accel, delta)
|
||||
|
||||
|
||||
func setup(
|
||||
align_tolerance: float,
|
||||
angular_deceleration_radius: float,
|
||||
angular_accel_max: float,
|
||||
angular_speed_max: float,
|
||||
deceleration_radius: float,
|
||||
arrival_tolerance: float,
|
||||
linear_acceleration_max: float,
|
||||
linear_speed_max: float,
|
||||
_target: Spatial
|
||||
) -> void:
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
agent.linear_acceleration_max = linear_acceleration_max
|
||||
agent.linear_drag_percentage = 0.05
|
||||
agent.angular_acceleration_max = angular_accel_max
|
||||
agent.angular_speed_max = angular_speed_max
|
||||
agent.angular_drag_percentage = 0.1
|
||||
|
||||
arrive.arrival_tolerance = arrival_tolerance
|
||||
arrive.deceleration_radius = deceleration_radius
|
||||
|
||||
face.alignment_tolerance = align_tolerance
|
||||
face.deceleration_radius = angular_deceleration_radius
|
||||
|
||||
target_node = _target
|
||||
self.target.position = target_node.transform.origin
|
||||
blend.add_behavior(arrive, 1)
|
||||
blend.add_behavior(face, 1)
|
4
project_gdscript/Demos/Arrive3d/SeekerMat.tres
Normal file
4
project_gdscript/Demos/Arrive3d/SeekerMat.tres
Normal file
@ -0,0 +1,4 @@
|
||||
[gd_resource type="SpatialMaterial" format=2]
|
||||
|
||||
[resource]
|
||||
albedo_color = Color( 0.152941, 0.764706, 0.247059, 1 )
|
@ -0,0 +1,40 @@
|
||||
extends Node
|
||||
|
||||
export (float, 0, 1000, 40) var linear_speed_max := 350.0 setget set_linear_speed_max
|
||||
export (float, 0, 4000, 2) var linear_acceleration_max := 40.0 setget set_linear_accel_max
|
||||
export (float, 0, 500, 10) var proximity_radius := 140.0 setget set_proximity_radius
|
||||
export var draw_proximity := true setget set_draw_proximity
|
||||
|
||||
onready var spawner := $Spawner
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
linear_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_linear_speed_max(value)
|
||||
|
||||
|
||||
func set_linear_accel_max(value: float) -> void:
|
||||
linear_acceleration_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_linear_accel_max(value)
|
||||
|
||||
|
||||
func set_proximity_radius(value: float) -> void:
|
||||
proximity_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_proximity_radius(value)
|
||||
|
||||
|
||||
func set_draw_proximity(value: bool) -> void:
|
||||
draw_proximity = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_draw_proximity(value)
|
@ -0,0 +1,26 @@
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/AvoidCollisions/Spawner.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/AvoidCollisions/AvoidCollisionsDemo.gd" type="Script" id=2]
|
||||
[ext_resource path="res://Demos/AvoidCollisions/Avoider.tscn" type="PackedScene" id=3]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=5]
|
||||
|
||||
[node name="AvoidCollisionsDemo" type="Node"]
|
||||
script = ExtResource( 2 )
|
||||
linear_speed_max = 520.0
|
||||
linear_acceleration_max = 2250.0
|
||||
proximity_radius = 100.0
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 5 )]
|
||||
|
||||
[node name="Spawner" type="Node2D" parent="."]
|
||||
script = ExtResource( 1 )
|
||||
avoider_template = ExtResource( 3 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
outer_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
agent_count = 80
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 4 )]
|
||||
text_bbcode = "Avoid Collisions Demo
|
||||
Watch each agent try to keep traveling in a particular direction, but prioritize avoiding collisions with other agents."
|
112
project_gdscript/Demos/AvoidCollisions/Avoider.gd
Normal file
112
project_gdscript/Demos/AvoidCollisions/Avoider.gd
Normal file
@ -0,0 +1,112 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
var draw_proximity: bool
|
||||
|
||||
var _boundary_right: float
|
||||
var _boundary_bottom: float
|
||||
var _radius: float
|
||||
var _accel := GSAITargetAcceleration.new()
|
||||
var _velocity := Vector2.ZERO
|
||||
var _direction := Vector2()
|
||||
var _drag := 0.1
|
||||
var _color := Color(0.4, 1.0, 0.89, 0.3)
|
||||
|
||||
onready var collision := $CollisionShape2D
|
||||
var agent :GSAIKinematicBody2DAgent= null
|
||||
var proximity :GSAIRadiusProximity= null
|
||||
var avoid :GSAIAvoidCollisions= null
|
||||
var target :GSAIAgentLocation= null
|
||||
var seek :GSAISeek= null
|
||||
var priority :GSAIPriority= null
|
||||
|
||||
func _ready() -> void:
|
||||
agent = GSAIKinematicBody2DAgent.new()
|
||||
agent.body = self
|
||||
|
||||
proximity = GSAIRadiusProximity.new()
|
||||
proximity.agent = agent
|
||||
proximity.radius = 140
|
||||
|
||||
avoid = GSAIAvoidCollisions.new()
|
||||
avoid.agent = agent
|
||||
avoid.proximity = proximity
|
||||
|
||||
target = GSAIAgentLocation.new()
|
||||
|
||||
seek = GSAISeek.new()
|
||||
seek.agent = agent
|
||||
seek.target = target
|
||||
|
||||
priority = GSAIPriority.new()
|
||||
priority.agent = agent
|
||||
priority.zero_threshold = 0.0001
|
||||
|
||||
func _draw() -> void:
|
||||
if draw_proximity:
|
||||
draw_circle(Vector2.ZERO, proximity.radius, _color)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
target.position.x = agent.position.x + _direction.x * _radius
|
||||
target.position.y = agent.position.y + _direction.y * _radius
|
||||
|
||||
priority.calculate_steering(_accel)
|
||||
agent.apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func setup(
|
||||
linear_speed_max: float,
|
||||
linear_accel_max: float,
|
||||
proximity_radius: float,
|
||||
boundary_right: float,
|
||||
boundary_bottom: float,
|
||||
_draw_proximity: bool,
|
||||
rng: RandomNumberGenerator
|
||||
) -> void:
|
||||
rng.randomize()
|
||||
_direction = Vector2(rng.randf_range(-1, 1), rng.randf_range(-1, 1)).normalized()
|
||||
|
||||
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_behavior(avoid)
|
||||
priority.add_behavior(seek)
|
||||
|
||||
|
||||
func set_proximity_agents(agents: Array) -> void:
|
||||
proximity.agents = agents
|
||||
|
||||
|
||||
func set_random_nonoverlapping_position(others: Array, distance_from_boundary_min: float) -> void:
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.randomize()
|
||||
var tries_max := max(100, others.size() * others.size())
|
||||
while tries_max > 0:
|
||||
tries_max -= 1
|
||||
global_position.x = rng.randf_range(
|
||||
distance_from_boundary_min, _boundary_right - distance_from_boundary_min
|
||||
)
|
||||
global_position.y = rng.randf_range(
|
||||
distance_from_boundary_min, _boundary_bottom - distance_from_boundary_min
|
||||
)
|
||||
var done := true
|
||||
for i in range(others.size()):
|
||||
var other: Node2D = others[i]
|
||||
if (
|
||||
other.global_position.distance_to(position)
|
||||
<= _radius * 2 + distance_from_boundary_min
|
||||
):
|
||||
done = false
|
||||
if done:
|
||||
break
|
17
project_gdscript/Demos/AvoidCollisions/Avoider.tscn
Normal file
17
project_gdscript/Demos/AvoidCollisions/Avoider.tscn
Normal file
@ -0,0 +1,17 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/AvoidCollisions/Avoider.gd" type="Script" id=2]
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 21.3503
|
||||
|
||||
[node name="Avoider" type="KinematicBody2D"]
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 1 )
|
||||
inner_color = Color( 0.890196, 0.411765, 0.337255, 1 )
|
||||
outer_color = Color( 1, 0.709804, 0.439216, 1 )
|
||||
stroke = 5.0
|
66
project_gdscript/Demos/AvoidCollisions/Spawner.gd
Normal file
66
project_gdscript/Demos/AvoidCollisions/Spawner.gd
Normal file
@ -0,0 +1,66 @@
|
||||
extends Node2D
|
||||
|
||||
export var avoider_template: PackedScene
|
||||
export var inner_color := Color()
|
||||
export var outer_color := Color()
|
||||
export var agent_count := 60
|
||||
|
||||
var boundaries: Vector2
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
boundaries = Vector2(
|
||||
ProjectSettings["display/window/size/width"], ProjectSettings["display/window/size/height"]
|
||||
)
|
||||
var rng := RandomNumberGenerator.new()
|
||||
var avoiders := []
|
||||
var avoider_agents := []
|
||||
for i in range(agent_count):
|
||||
var avoider := avoider_template.instance()
|
||||
add_child(avoider)
|
||||
avoider.setup(
|
||||
owner.linear_speed_max,
|
||||
owner.linear_acceleration_max,
|
||||
owner.proximity_radius,
|
||||
boundaries.x,
|
||||
boundaries.y,
|
||||
true if i == 0 and owner.draw_proximity else false,
|
||||
rng
|
||||
)
|
||||
avoider_agents.append(avoider.agent)
|
||||
avoider.set_random_nonoverlapping_position(avoiders, 16)
|
||||
if i == 0:
|
||||
avoider.collision.inner_color = inner_color
|
||||
avoider.collision.outer_color = outer_color
|
||||
avoiders.append(avoider)
|
||||
if i % 10 == 0:
|
||||
yield(get_tree(), "idle_frame")
|
||||
for child in get_children():
|
||||
child.set_proximity_agents(avoider_agents)
|
||||
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
for child in get_children():
|
||||
child.global_position = child.global_position.posmodv(boundaries)
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.agent.linear_speed_max = value
|
||||
|
||||
|
||||
func set_linear_accel_max(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.agent.linear_acceleration_max = value
|
||||
|
||||
|
||||
func set_proximity_radius(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.proximity.radius = value
|
||||
get_child(0).update()
|
||||
|
||||
|
||||
func set_draw_proximity(value: bool) -> void:
|
||||
var child := get_child(0)
|
||||
child.draw_proximity = value
|
||||
child.update()
|
28
project_gdscript/Demos/DemoPickerUI.gd
Normal file
28
project_gdscript/Demos/DemoPickerUI.gd
Normal file
@ -0,0 +1,28 @@
|
||||
class_name DemoPickerUI
|
||||
extends Control
|
||||
|
||||
# warning-ignore:unused_signal
|
||||
signal demo_requested
|
||||
|
||||
var demo_path := "" setget set_demo_path
|
||||
|
||||
onready var list: ItemList = $VBoxContainer/ItemList
|
||||
onready var button: Button = $VBoxContainer/Button
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
list.connect("demo_selected", self, "set_demo_path")
|
||||
# warning-ignore:return_value_discarded
|
||||
list.connect("item_activated", self, "_on_ItemList_item_activated")
|
||||
# warning-ignore:return_value_discarded
|
||||
button.connect("pressed", self, "emit_signal", ["demo_requested"])
|
||||
demo_path = list.file_paths[0]
|
||||
|
||||
|
||||
func set_demo_path(value: String) -> void:
|
||||
demo_path = value
|
||||
|
||||
|
||||
func _on_ItemList_item_activated(_index: int) -> void:
|
||||
emit_signal("demo_requested")
|
16
project_gdscript/Demos/DemoPlayer.gd
Normal file
16
project_gdscript/Demos/DemoPlayer.gd
Normal file
@ -0,0 +1,16 @@
|
||||
extends Node2D
|
||||
|
||||
|
||||
func load_demo(scene_path: String) -> void:
|
||||
if not scene_path:
|
||||
return
|
||||
|
||||
var demo = load(scene_path)
|
||||
if demo:
|
||||
add_child(demo.instance())
|
||||
|
||||
|
||||
func unload() -> void:
|
||||
for node in get_children():
|
||||
call_deferred("remove_child", node)
|
||||
node.queue_free()
|
81
project_gdscript/Demos/DemoSelector.tscn
Normal file
81
project_gdscript/Demos/DemoSelector.tscn
Normal file
@ -0,0 +1,81 @@
|
||||
[gd_scene load_steps=7 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/PopulateItemList.gd" type="Script" id=1]
|
||||
[ext_resource path="res://assets/theme/gdquest.theme" type="Theme" id=2]
|
||||
[ext_resource path="res://assets/sprites/background.png" type="Texture" id=3]
|
||||
[ext_resource path="res://Demos/DemoPickerUI.gd" type="Script" id=4]
|
||||
[ext_resource path="res://Demos/DemoPlayer.gd" type="Script" id=5]
|
||||
[ext_resource path="res://Demos/Demos.gd" type="Script" id=6]
|
||||
|
||||
[node name="Demos" type="Node"]
|
||||
script = ExtResource( 6 )
|
||||
|
||||
[node name="DemoPlayer" type="Node2D" parent="."]
|
||||
script = ExtResource( 5 )
|
||||
|
||||
[node name="DemoPickerUI" type="Control" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
theme = ExtResource( 2 )
|
||||
script = ExtResource( 4 )
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="DemoPickerUI"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
rect_min_size = Vector2( 1024, 600 )
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
texture = ExtResource( 3 )
|
||||
expand = true
|
||||
stretch_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="DemoPickerUI"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -341.0
|
||||
margin_top = -290.0
|
||||
margin_right = 341.0
|
||||
margin_bottom = 290.0
|
||||
rect_min_size = Vector2( 682, 0 )
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
alignment = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ItemList" type="ItemList" parent="DemoPickerUI/VBoxContainer"]
|
||||
margin_top = 231.0
|
||||
margin_right = 682.0
|
||||
margin_bottom = 240.0
|
||||
auto_height = true
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Button" type="Button" parent="DemoPickerUI/VBoxContainer"]
|
||||
margin_left = 201.0
|
||||
margin_top = 248.0
|
||||
margin_right = 481.0
|
||||
margin_bottom = 348.0
|
||||
rect_min_size = Vector2( 280, 100 )
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 13
|
||||
text = "Load scene"
|
||||
|
||||
[node name="ButtonGoBack" type="Button" parent="."]
|
||||
visible = false
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 48.0
|
||||
margin_top = -156.0
|
||||
margin_right = 328.0
|
||||
margin_bottom = -56.0
|
||||
rect_min_size = Vector2( 280, 100 )
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 13
|
||||
theme = ExtResource( 2 )
|
||||
text = "Go back"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
30
project_gdscript/Demos/Demos.gd
Normal file
30
project_gdscript/Demos/Demos.gd
Normal file
@ -0,0 +1,30 @@
|
||||
extends Node
|
||||
|
||||
onready var demo_picker: DemoPickerUI = $DemoPickerUI
|
||||
onready var demo_player := $DemoPlayer
|
||||
onready var button_go_back: Button = $ButtonGoBack
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
demo_picker.connect("demo_requested", self, "_on_DemoPickerUI_demo_requested")
|
||||
# warning-ignore:return_value_discarded
|
||||
button_go_back.connect("pressed", self, "_on_ButtonGoBack_pressed")
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("toggle_fullscreen"):
|
||||
OS.window_fullscreen = not OS.window_fullscreen
|
||||
get_tree().set_input_as_handled()
|
||||
|
||||
|
||||
func _on_DemoPickerUI_demo_requested() -> void:
|
||||
demo_player.load_demo(demo_picker.demo_path)
|
||||
demo_picker.hide()
|
||||
button_go_back.show()
|
||||
|
||||
|
||||
func _on_ButtonGoBack_pressed() -> void:
|
||||
demo_player.unload()
|
||||
button_go_back.hide()
|
||||
demo_picker.show()
|
61
project_gdscript/Demos/Face/FaceDemo.gd
Normal file
61
project_gdscript/Demos/Face/FaceDemo.gd
Normal file
@ -0,0 +1,61 @@
|
||||
extends Node
|
||||
|
||||
export (int, 0, 1080, 2) var angular_speed_max := 120 setget set_angular_speed_max
|
||||
export (int, 0, 2048, 2) var angular_accel_max := 10 setget set_angular_accel_max
|
||||
export (int, 0, 180, 2) var align_tolerance := 5 setget set_align_tolerance
|
||||
export (int, 0, 359, 2) var deceleration_radius := 45 setget set_deceleration_radius
|
||||
export (float, 0, 1000, 40) var player_speed := 600.0 setget set_player_speed
|
||||
|
||||
onready var player := $Player
|
||||
onready var turret := $Turret
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
player.speed = player_speed
|
||||
turret.setup(
|
||||
player.agent,
|
||||
deg2rad(align_tolerance),
|
||||
deg2rad(deceleration_radius),
|
||||
deg2rad(angular_accel_max),
|
||||
deg2rad(angular_speed_max)
|
||||
)
|
||||
|
||||
|
||||
func set_align_tolerance(value: int) -> void:
|
||||
align_tolerance = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
turret.face.alignment_tolerance = deg2rad(value)
|
||||
|
||||
|
||||
func set_deceleration_radius(value: int) -> void:
|
||||
deceleration_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
turret.face.deceleration_radius = deg2rad(value)
|
||||
|
||||
|
||||
func set_angular_accel_max(value: int) -> void:
|
||||
angular_accel_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
turret.agent.angular_acceleration_max = deg2rad(value)
|
||||
|
||||
|
||||
func set_angular_speed_max(value: int) -> void:
|
||||
angular_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
turret.agent.angular_speed_max = deg2rad(value)
|
||||
|
||||
|
||||
func set_player_speed(value: float) -> void:
|
||||
player_speed = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
player.speed = player_speed
|
48
project_gdscript/Demos/Face/FaceDemo.tscn
Normal file
48
project_gdscript/Demos/Face/FaceDemo.tscn
Normal file
@ -0,0 +1,48 @@
|
||||
[gd_scene load_steps=9 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/Face/Turret.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/Face/FaceDemo.gd" type="Script" id=2]
|
||||
[ext_resource path="res://Demos/Face/Player.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=8]
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 20.2633
|
||||
|
||||
[sub_resource type="CircleShape2D" id=2]
|
||||
radius = 37.1052
|
||||
|
||||
[node name="FaceDemo" type="Node"]
|
||||
script = ExtResource( 2 )
|
||||
angular_speed_max = 662
|
||||
angular_accel_max = 924
|
||||
deceleration_radius = 136
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 5 )]
|
||||
|
||||
[node name="Player" type="KinematicBody2D" parent="."]
|
||||
position = Vector2( 687.363, 351.005 )
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Player"]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 8 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
outer_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
stroke = 6.0
|
||||
|
||||
[node name="Turret" type="KinematicBody2D" parent="."]
|
||||
position = Vector2( 984.348, 571.959 )
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Turret"]
|
||||
shape = SubResource( 2 )
|
||||
script = ExtResource( 8 )
|
||||
inner_color = Color( 0.890196, 0.411765, 0.337255, 1 )
|
||||
outer_color = Color( 1, 0.709804, 0.439216, 1 )
|
||||
stroke = 8.0
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 4 )]
|
||||
text_bbcode = "Face Demo
|
||||
Move the [color=lime]green player[/color] around with WASD and notice the [color=#ffb570]orange turret[/color] orient itself"
|
18
project_gdscript/Demos/Face/Player.gd
Normal file
18
project_gdscript/Demos/Face/Player.gd
Normal file
@ -0,0 +1,18 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
var speed: float
|
||||
|
||||
onready var agent := GSAIAgentLocation.new()
|
||||
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
var movement := _get_movement()
|
||||
move_and_slide(movement * speed)
|
||||
agent.position = Vector3(global_position.x, global_position.y, 0)
|
||||
|
||||
|
||||
func _get_movement() -> Vector2:
|
||||
return Vector2(
|
||||
Input.get_action_strength("sf_right") - Input.get_action_strength("sf_left"),
|
||||
Input.get_action_strength("sf_down") - Input.get_action_strength("sf_up")
|
||||
)
|
47
project_gdscript/Demos/Face/Turret.gd
Normal file
47
project_gdscript/Demos/Face/Turret.gd
Normal file
@ -0,0 +1,47 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
var face: GSAIFace
|
||||
var agent := GSAIKinematicBody2DAgent.new()
|
||||
|
||||
var _accel := GSAITargetAcceleration.new()
|
||||
var _angular_drag := 0.1
|
||||
var _cannon: Rect2
|
||||
var _color: Color
|
||||
|
||||
onready var collision_shape := $CollisionShape2D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
agent.body = self
|
||||
|
||||
var radius = collision_shape.shape.radius
|
||||
_cannon = Rect2(Vector2(-5, 0), Vector2(10, -radius * 2))
|
||||
_color = collision_shape.outer_color
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
face.calculate_steering(_accel)
|
||||
agent.apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
draw_rect(_cannon, _color)
|
||||
|
||||
|
||||
func setup(
|
||||
player_agent: GSAIAgentLocation,
|
||||
align_tolerance: float,
|
||||
deceleration_radius: float,
|
||||
angular_accel_max: float,
|
||||
angular_speed_max: float
|
||||
) -> void:
|
||||
face = GSAIFace.new()
|
||||
face.agent = agent
|
||||
face.target = player_agent
|
||||
|
||||
face.alignment_tolerance = align_tolerance
|
||||
face.deceleration_radius = deceleration_radius
|
||||
|
||||
agent.angular_acceleration_max = angular_accel_max
|
||||
agent.angular_speed_max = angular_speed_max
|
||||
agent.angular_drag_percentage = _angular_drag
|
53
project_gdscript/Demos/FollowPath/Drawer.gd
Normal file
53
project_gdscript/Demos/FollowPath/Drawer.gd
Normal file
@ -0,0 +1,53 @@
|
||||
extends Node2D
|
||||
|
||||
signal path_established(points)
|
||||
|
||||
var active_points := []
|
||||
var is_drawing := false
|
||||
var distance_threshold := 100.0
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion:
|
||||
if is_drawing:
|
||||
active_points.append(event.position)
|
||||
update()
|
||||
elif event is InputEventMouseButton:
|
||||
if event.pressed and event.button_index == BUTTON_LEFT:
|
||||
active_points.clear()
|
||||
active_points.append(event.position)
|
||||
is_drawing = true
|
||||
update()
|
||||
elif not event.pressed:
|
||||
is_drawing = false
|
||||
if active_points.size() >= 2:
|
||||
_simplify()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if is_drawing:
|
||||
for point in active_points:
|
||||
draw_circle(point, 2, Color.red)
|
||||
else:
|
||||
if active_points.size() > 0:
|
||||
draw_circle(active_points.front(), 2, Color.red)
|
||||
draw_circle(active_points.back(), 2, Color.yellow)
|
||||
draw_polyline(active_points, Color.skyblue, 1.0)
|
||||
|
||||
|
||||
func _simplify() -> void:
|
||||
var first: Vector2 = active_points.front()
|
||||
var last: Vector2 = active_points.back()
|
||||
var key := first
|
||||
var simplified_path := [first]
|
||||
for i in range(1, active_points.size()):
|
||||
var point: Vector2 = active_points[i]
|
||||
var distance := point.distance_to(key)
|
||||
if distance > distance_threshold:
|
||||
key = point
|
||||
simplified_path.append(key)
|
||||
active_points = simplified_path
|
||||
if active_points.back() != last:
|
||||
active_points.append(last)
|
||||
update()
|
||||
emit_signal("path_established", active_points)
|
70
project_gdscript/Demos/FollowPath/FollowPathDemo.gd
Normal file
70
project_gdscript/Demos/FollowPath/FollowPathDemo.gd
Normal file
@ -0,0 +1,70 @@
|
||||
extends Node
|
||||
|
||||
export (float, 0, 2000, 40) var linear_speed_max := 600.0 setget set_linear_speed_max
|
||||
export (float, 0, 9000, 10.0) var linear_acceleration_max := 40.0 setget set_linear_acceleration_max
|
||||
export (float, 0, 100, 0.1) var arrival_tolerance := 10.0 setget set_arrival_tolerance
|
||||
export (float, 0, 500, 10) var deceleration_radius := 100.0 setget set_deceleration_radius
|
||||
export (float, 0, 5, 0.1) var predict_time := 0.3 setget set_predict_time
|
||||
export (float, 0, 200, 10.0) var path_offset := 20.0 setget set_path_offset
|
||||
|
||||
onready var drawer := $Drawer
|
||||
onready var follower := $PathFollower
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
follower.setup(
|
||||
path_offset,
|
||||
predict_time,
|
||||
linear_acceleration_max,
|
||||
linear_speed_max,
|
||||
deceleration_radius,
|
||||
arrival_tolerance
|
||||
)
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
linear_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
follower.agent.linear_speed_max = value
|
||||
|
||||
|
||||
func set_linear_acceleration_max(value: float) -> void:
|
||||
linear_acceleration_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
follower.agent.linear_acceleration_max = value
|
||||
|
||||
|
||||
func set_arrival_tolerance(value: float) -> void:
|
||||
arrival_tolerance = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
follower.follow.arrival_tolerance = value
|
||||
|
||||
|
||||
func set_deceleration_radius(value: float) -> void:
|
||||
deceleration_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
follower.follow.deceleration_radius = value
|
||||
|
||||
|
||||
func set_predict_time(value: float) -> void:
|
||||
predict_time = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
follower.follow.prediction_time = value
|
||||
|
||||
|
||||
func set_path_offset(value: float) -> void:
|
||||
path_offset = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
follower.follow.path_offset = value
|
37
project_gdscript/Demos/FollowPath/FollowPathDemo.tscn
Normal file
37
project_gdscript/Demos/FollowPath/FollowPathDemo.tscn
Normal file
@ -0,0 +1,37 @@
|
||||
[gd_scene load_steps=8 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/FollowPath/Drawer.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://Demos/FollowPath/PathFollower.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/FollowPath/FollowPathDemo.gd" type="Script" id=4]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=6]
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 24.1954
|
||||
|
||||
[node name="FollowPathDemo" type="Node"]
|
||||
script = ExtResource( 4 )
|
||||
linear_speed_max = 920.0
|
||||
linear_acceleration_max = 3740.0
|
||||
deceleration_radius = 200.0
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 5 )]
|
||||
|
||||
[node name="Drawer" type="Node2D" parent="."]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="PathFollower" type="KinematicBody2D" parent="."]
|
||||
position = Vector2( 960, 540 )
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="PathFollower"]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 6 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
outer_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
stroke = 6.0
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 2 )]
|
||||
text_bbcode = "Follow Path Demo
|
||||
Use the mouse to draw a path on screen and watch the [color=lime]green \"Agent\"[/color] follow it to the end."
|
57
project_gdscript/Demos/FollowPath/PathFollower.gd
Normal file
57
project_gdscript/Demos/FollowPath/PathFollower.gd
Normal file
@ -0,0 +1,57 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
var _velocity := Vector2.ZERO
|
||||
var _accel := GSAITargetAcceleration.new()
|
||||
var _valid := false
|
||||
var _drag := 0.1
|
||||
|
||||
var agent := GSAIKinematicBody2DAgent.new()
|
||||
var path := GSAIPath.new()
|
||||
var follow := GSAIFollowPath.new()
|
||||
|
||||
func _ready() -> void:
|
||||
agent.body = self
|
||||
|
||||
path.initialize(
|
||||
[
|
||||
Vector3(global_position.x, global_position.y, 0),
|
||||
Vector3(global_position.x, global_position.y, 0)
|
||||
],
|
||||
true
|
||||
)
|
||||
|
||||
follow.agent = agent
|
||||
follow.path = path
|
||||
follow.agent = agent
|
||||
|
||||
func setup(
|
||||
path_offset: float,
|
||||
predict_time: float,
|
||||
accel_max: float,
|
||||
speed_max: float,
|
||||
decel_radius: float,
|
||||
arrival_tolerance: float
|
||||
) -> void:
|
||||
owner.drawer.connect("path_established", self, "_on_Drawer_path_established")
|
||||
follow.path_offset = path_offset
|
||||
follow.prediction_time = predict_time
|
||||
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:
|
||||
follow.calculate_steering(_accel)
|
||||
agent.apply_steering(_accel, delta)
|
||||
|
||||
|
||||
func _on_Drawer_path_established(points: Array) -> void:
|
||||
var positions := PoolVector3Array()
|
||||
for p in points:
|
||||
positions.append(Vector3(p.x, p.y, 0))
|
||||
path.create_path(positions)
|
||||
_valid = true
|
79
project_gdscript/Demos/GroupBehaviors/GroupBehaviorsDemo.gd
Normal file
79
project_gdscript/Demos/GroupBehaviors/GroupBehaviorsDemo.gd
Normal file
@ -0,0 +1,79 @@
|
||||
extends Node
|
||||
|
||||
onready var spawner := $Spawner
|
||||
|
||||
export (float, 0, 2000, 40.0) var linear_speed_max := 600.0 setget set_linear_speed_max
|
||||
export (float, 0, 9000, 2.0) var linear_accel_max := 40.0 setget set_linear_accel_max
|
||||
export (float, 0, 300, 2.0) var proximity_radius := 140.0 setget set_proximity_radius
|
||||
export (float, 0, 200000, 250) var separation_decay_coefficient := 2000.0 setget set_separation_decay_coef
|
||||
export (float, 0, 2, 0.1) var cohesion_strength := 0.1 setget set_cohesion_strength
|
||||
export (float, 0, 10, 0.2) var separation_strength := 1.5 setget set_separation_strength
|
||||
export var show_proximity_radius := true setget set_show_proximity_radius
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
spawner.setup(
|
||||
linear_speed_max,
|
||||
linear_accel_max,
|
||||
proximity_radius,
|
||||
separation_decay_coefficient,
|
||||
cohesion_strength,
|
||||
separation_strength,
|
||||
show_proximity_radius
|
||||
)
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
linear_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_linear_speed_max(value)
|
||||
|
||||
|
||||
func set_linear_accel_max(value: float) -> void:
|
||||
linear_accel_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_linear_accel_max(value)
|
||||
|
||||
|
||||
func set_proximity_radius(value: float) -> void:
|
||||
proximity_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_proximity_radius(value)
|
||||
|
||||
|
||||
func set_show_proximity_radius(value: bool) -> void:
|
||||
show_proximity_radius = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_show_proximity_radius(value)
|
||||
|
||||
|
||||
func set_separation_decay_coef(value: float) -> void:
|
||||
separation_decay_coefficient = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_separation_decay_coef(value)
|
||||
|
||||
|
||||
func set_cohesion_strength(value: float) -> void:
|
||||
cohesion_strength = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_cohesion_strength(value)
|
||||
|
||||
|
||||
func set_separation_strength(value: float) -> void:
|
||||
separation_strength = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
spawner.set_separation_strength(value)
|
@ -0,0 +1,29 @@
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/GroupBehaviors/Member.tscn" type="PackedScene" id=1]
|
||||
[ext_resource path="res://Demos/GroupBehaviors/Spawner.gd" type="Script" id=2]
|
||||
[ext_resource path="res://Demos/GroupBehaviors/GroupBehaviorsDemo.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=5]
|
||||
|
||||
|
||||
[node name="GroupBehaviorsDemo" type="Node"]
|
||||
script = ExtResource( 3 )
|
||||
linear_accel_max = 4234.0
|
||||
proximity_radius = 158.0
|
||||
separation_decay_coefficient = 121500.0
|
||||
cohesion_strength = 0.2
|
||||
separation_strength = 8.8
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 5 )]
|
||||
|
||||
[node name="Spawner" type="Node2D" parent="."]
|
||||
position = Vector2( 973.261, 528.829 )
|
||||
script = ExtResource( 2 )
|
||||
member = ExtResource( 1 )
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 4 )]
|
||||
mouse_filter = 2
|
||||
text_bbcode = "Group Behavior Demo
|
||||
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."
|
69
project_gdscript/Demos/GroupBehaviors/Member.gd
Normal file
69
project_gdscript/Demos/GroupBehaviors/Member.gd
Normal file
@ -0,0 +1,69 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
var separation: GSAISeparation
|
||||
var cohesion: GSAICohesion
|
||||
var proximity: GSAIRadiusProximity
|
||||
var agent :GSAIKinematicBody2DAgent = null
|
||||
var blend : GSAIBlend = null
|
||||
var acceleration : GSAITargetAcceleration = null
|
||||
var draw_proximity := false
|
||||
|
||||
var _color := Color.red
|
||||
var _velocity := Vector2()
|
||||
|
||||
onready var collision_shape := $CollisionShape2D
|
||||
|
||||
func _init() -> void:
|
||||
agent = GSAIKinematicBody2DAgent.new()
|
||||
agent.body = self
|
||||
|
||||
blend = GSAIBlend.new()
|
||||
blend.agent = agent
|
||||
|
||||
acceleration = GSAITargetAcceleration.new()
|
||||
|
||||
func setup(
|
||||
linear_speed_max: float,
|
||||
linear_accel_max: float,
|
||||
proximity_radius: float,
|
||||
separation_decay_coefficient: float,
|
||||
cohesion_strength: float,
|
||||
separation_strength: float
|
||||
) -> void:
|
||||
_color = Color(rand_range(0.5, 1), rand_range(0.25, 1), rand_range(0, 1))
|
||||
collision_shape.inner_color = _color
|
||||
|
||||
agent.linear_acceleration_max = linear_accel_max
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
agent.linear_drag_percentage = 0.1
|
||||
|
||||
proximity = GSAIRadiusProximity.new()
|
||||
proximity.agent = agent
|
||||
proximity.radius = proximity_radius
|
||||
|
||||
separation = GSAISeparation.new()
|
||||
separation.agent = agent
|
||||
separation.proximity = proximity
|
||||
separation.decay_coefficient = separation_decay_coefficient
|
||||
|
||||
cohesion = GSAICohesion.new()
|
||||
cohesion.agent = agent
|
||||
cohesion.proximity = proximity
|
||||
|
||||
blend.add_behavior(separation, separation_strength)
|
||||
blend.add_behavior(cohesion, cohesion_strength)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if draw_proximity:
|
||||
draw_circle(Vector2.ZERO, proximity.radius, Color(0.4, 1.0, 0.89, 0.3))
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if blend:
|
||||
blend.calculate_steering(acceleration)
|
||||
agent.apply_steering(acceleration, delta)
|
||||
|
||||
|
||||
func set_neighbors(neighbor: Array) -> void:
|
||||
proximity.agents = neighbor
|
18
project_gdscript/Demos/GroupBehaviors/Member.tscn
Normal file
18
project_gdscript/Demos/GroupBehaviors/Member.tscn
Normal file
@ -0,0 +1,18 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/GroupBehaviors/Member.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=3]
|
||||
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 16.0
|
||||
|
||||
[node name="Member" type="KinematicBody2D"]
|
||||
input_pickable = true
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 3 )
|
||||
outer_color = Color( 0.301961, 0.65098, 1, 1 )
|
||||
stroke = 4.0
|
92
project_gdscript/Demos/GroupBehaviors/Spawner.gd
Normal file
92
project_gdscript/Demos/GroupBehaviors/Spawner.gd
Normal file
@ -0,0 +1,92 @@
|
||||
extends Node2D
|
||||
|
||||
export var member: PackedScene
|
||||
|
||||
|
||||
func follower_input_event(
|
||||
viewport: Node,
|
||||
event: InputEvent,
|
||||
shape_idx: int,
|
||||
follower: KinematicBody2D
|
||||
) -> void:
|
||||
if event.is_action_pressed("click"):
|
||||
for other in get_children():
|
||||
if other.draw_proximity:
|
||||
other.draw_proximity = false
|
||||
other.update()
|
||||
follower.draw_proximity = true
|
||||
follower.update()
|
||||
move_child(follower, get_child_count())
|
||||
|
||||
|
||||
func setup(
|
||||
linear_speed_max: float,
|
||||
linear_accel_max: float,
|
||||
proximity_radius: float,
|
||||
separation_decay_coefficient: float,
|
||||
cohesion_strength: float,
|
||||
separation_strength: float,
|
||||
show_proximity_radius: bool
|
||||
) -> void:
|
||||
var followers := []
|
||||
for i in range(19):
|
||||
var follower : KinematicBody2D = member.instance()
|
||||
add_child(follower)
|
||||
follower.position += Vector2(rand_range(-60, 60), rand_range(-60, 60))
|
||||
followers.append(follower)
|
||||
follower.setup(
|
||||
linear_speed_max,
|
||||
linear_accel_max,
|
||||
proximity_radius,
|
||||
separation_decay_coefficient,
|
||||
cohesion_strength,
|
||||
separation_strength
|
||||
)
|
||||
|
||||
if i == 0 and show_proximity_radius:
|
||||
follower.draw_proximity = true
|
||||
follower.update()
|
||||
follower.connect("input_event", self, "follower_input_event", [follower])
|
||||
|
||||
var agents := []
|
||||
for i in followers:
|
||||
agents.append(i.agent)
|
||||
for i in followers:
|
||||
i.proximity.agents = agents
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.agent.linear_speed_max = value
|
||||
|
||||
|
||||
func set_linear_accel_max(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.agent.linear_acceleration_max = value
|
||||
|
||||
|
||||
func set_proximity_radius(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.proximity.radius = value
|
||||
if child == get_child(0):
|
||||
child.update()
|
||||
|
||||
|
||||
func set_show_proximity_radius(value: bool) -> void:
|
||||
get_child(0).draw_proximity = value
|
||||
get_child(0).update()
|
||||
|
||||
|
||||
func set_separation_decay_coef(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.separation.decay_coefficient = value
|
||||
|
||||
|
||||
func set_cohesion_strength(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.blend.get_behavior(1).weight = value
|
||||
|
||||
|
||||
func set_separation_strength(value: float) -> void:
|
||||
for child in get_children():
|
||||
child.blend.get_behavior(0).weight = value
|
68
project_gdscript/Demos/PopulateItemList.gd
Normal file
68
project_gdscript/Demos/PopulateItemList.gd
Normal file
@ -0,0 +1,68 @@
|
||||
extends ItemList
|
||||
|
||||
signal demo_selected(scene_path)
|
||||
|
||||
var file_paths := PoolStringArray()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
self.connect("item_selected", self, "_on_item_selected")
|
||||
|
||||
var this_directory: String = get_tree().current_scene.filename.rsplit("/", false, 1)[0]
|
||||
file_paths = _find_files(this_directory, ["*Demo.tscn"], true)
|
||||
populate(file_paths)
|
||||
select(0)
|
||||
|
||||
|
||||
func populate(demos: PoolStringArray) -> void:
|
||||
for path in demos:
|
||||
var demo_name: String = path.rsplit("/", true, 1)[-1]
|
||||
demo_name = demo_name.rsplit("Demo", true, 1)[0]
|
||||
demo_name = sentencify(demo_name)
|
||||
add_item(demo_name)
|
||||
|
||||
|
||||
func sentencify(line: String) -> String:
|
||||
var regex := RegEx.new()
|
||||
# warning-ignore:return_value_discarded
|
||||
regex.compile("[A-Z]")
|
||||
|
||||
line = line.split(".", true, 1)[0]
|
||||
line = regex.sub(line, " $0", true)
|
||||
return line
|
||||
|
||||
|
||||
func _find_files(
|
||||
dirpath := "", patterns := PoolStringArray(), is_recursive := false, do_skip_hidden := true
|
||||
) -> PoolStringArray:
|
||||
var paths := PoolStringArray()
|
||||
var directory := Directory.new()
|
||||
|
||||
if not directory.dir_exists(dirpath):
|
||||
printerr("The directory does not exist: %s" % dirpath)
|
||||
return paths
|
||||
if not directory.open(dirpath) == OK:
|
||||
printerr("Could not open the following dirpath: %s" % dirpath)
|
||||
return paths
|
||||
|
||||
# warning-ignore:return_value_discarded
|
||||
directory.list_dir_begin(true, do_skip_hidden)
|
||||
var file_name := directory.get_next()
|
||||
while file_name != "":
|
||||
if directory.current_is_dir() and is_recursive:
|
||||
var subdirectory := dirpath.plus_file(file_name)
|
||||
paths.append_array(_find_files(subdirectory, patterns, is_recursive))
|
||||
else:
|
||||
for pattern in patterns:
|
||||
if file_name.match(pattern):
|
||||
paths.append(dirpath.plus_file(file_name))
|
||||
file_name = directory.get_next()
|
||||
|
||||
directory.list_dir_end()
|
||||
return paths
|
||||
|
||||
|
||||
func _on_item_selected(index: int) -> void:
|
||||
var demo_path := file_paths[index]
|
||||
emit_signal("demo_selected", demo_path)
|
15
project_gdscript/Demos/PursueSeek/BoundaryManager.gd
Normal file
15
project_gdscript/Demos/PursueSeek/BoundaryManager.gd
Normal file
@ -0,0 +1,15 @@
|
||||
extends Node2D
|
||||
# Wraps the ships' positions around the world border.
|
||||
|
||||
var _world_bounds: Vector2
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_world_bounds = Vector2(
|
||||
ProjectSettings["display/window/size/width"], ProjectSettings["display/window/size/height"]
|
||||
)
|
||||
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
for ship in get_children():
|
||||
ship.position = ship.position.posmodv(_world_bounds)
|
96
project_gdscript/Demos/PursueSeek/Player.gd
Normal file
96
project_gdscript/Demos/PursueSeek/Player.gd
Normal file
@ -0,0 +1,96 @@
|
||||
extends KinematicBody2D
|
||||
# Controls the player ship's movements based on player input.
|
||||
|
||||
export var thruster_strength := 175.0
|
||||
export var side_thruster_strength := 10.0
|
||||
export var velocity_max := 300.0
|
||||
export var angular_velocity_max := 2.0
|
||||
export var angular_drag := 0.025
|
||||
export var linear_drag := 0.025
|
||||
|
||||
var _linear_velocity := Vector2()
|
||||
var _angular_velocity := 0.0
|
||||
|
||||
onready var agent := GSAISteeringAgent.new()
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var movement := _get_movement()
|
||||
_angular_velocity = _calculate_angular_velocity(
|
||||
movement.x,
|
||||
_angular_velocity,
|
||||
side_thruster_strength,
|
||||
angular_velocity_max,
|
||||
angular_drag,
|
||||
delta
|
||||
)
|
||||
rotation += _angular_velocity * delta
|
||||
|
||||
_linear_velocity = _calculate_linear_velocity(
|
||||
movement.y,
|
||||
_linear_velocity,
|
||||
Vector2.UP.rotated(rotation),
|
||||
linear_drag,
|
||||
thruster_strength,
|
||||
velocity_max,
|
||||
delta
|
||||
)
|
||||
|
||||
_linear_velocity = move_and_slide(_linear_velocity)
|
||||
_update_agent()
|
||||
|
||||
|
||||
func _calculate_angular_velocity(
|
||||
horizontal_movement: float,
|
||||
current_velocity: float,
|
||||
_thruster_strength: float,
|
||||
_velocity_max: float,
|
||||
ship_drag: float,
|
||||
delta: float
|
||||
) -> float:
|
||||
var velocity := clamp(
|
||||
current_velocity + _thruster_strength * horizontal_movement * delta,
|
||||
-_velocity_max,
|
||||
_velocity_max
|
||||
)
|
||||
|
||||
velocity = lerp(velocity, 0, ship_drag)
|
||||
|
||||
return velocity
|
||||
|
||||
|
||||
func _calculate_linear_velocity(
|
||||
vertical_movement: float,
|
||||
current_velocity: Vector2,
|
||||
facing_direction: Vector2,
|
||||
ship_drag_coefficient: float,
|
||||
strength: float,
|
||||
speed_max: float,
|
||||
delta: float
|
||||
) -> Vector2:
|
||||
var actual_strength := 0.0
|
||||
if vertical_movement > 0:
|
||||
actual_strength = strength
|
||||
elif vertical_movement < 0:
|
||||
actual_strength = -strength / 1.5
|
||||
|
||||
var velocity := current_velocity + facing_direction * actual_strength * delta
|
||||
velocity = velocity.linear_interpolate(Vector2.ZERO, ship_drag_coefficient)
|
||||
|
||||
return velocity.clamped(speed_max)
|
||||
|
||||
|
||||
func _get_movement() -> Vector2:
|
||||
return Vector2(
|
||||
Input.get_action_strength("sf_right") - Input.get_action_strength("sf_left"),
|
||||
Input.get_action_strength("sf_up") - Input.get_action_strength("sf_down")
|
||||
)
|
||||
|
||||
|
||||
func _update_agent() -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
agent.linear_velocity.x = _linear_velocity.x
|
||||
agent.linear_velocity.y = _linear_velocity.y
|
||||
agent.angular_velocity = _angular_velocity
|
||||
agent.orientation = rotation
|
39
project_gdscript/Demos/PursueSeek/PursueAndSeekDemo.gd
Normal file
39
project_gdscript/Demos/PursueSeek/PursueAndSeekDemo.gd
Normal file
@ -0,0 +1,39 @@
|
||||
extends Node
|
||||
|
||||
export (float, 0, 2000, 40) var linear_speed_max := 120.0 setget set_linear_speed_max
|
||||
export (float, 0, 2000, 20) var linear_accel_max := 10.0 setget set_linear_accel_max
|
||||
export (float, 0, 5, 0.1) var predict_time := 1.0 setget set_predict_time
|
||||
|
||||
onready var pursuer := $BoundaryManager/Pursuer
|
||||
onready var seeker := $BoundaryManager/Seeker
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
pursuer.setup(predict_time, linear_speed_max, linear_accel_max)
|
||||
seeker.setup(predict_time, linear_speed_max, linear_accel_max)
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
linear_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
pursuer.agent.linear_speed_max = value
|
||||
seeker.agent.linear_speed_max = value
|
||||
|
||||
|
||||
func set_linear_accel_max(value: float) -> void:
|
||||
linear_accel_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
pursuer.agent.linear_acceleration_max = value
|
||||
seeker.agent.linear_acceleration_max = value
|
||||
|
||||
|
||||
func set_predict_time(value: float) -> void:
|
||||
predict_time = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
pursuer._behavior.predict_time_max = value
|
92
project_gdscript/Demos/PursueSeek/PursueAndSeekDemo.tscn
Normal file
92
project_gdscript/Demos/PursueSeek/PursueAndSeekDemo.tscn
Normal file
@ -0,0 +1,92 @@
|
||||
[gd_scene load_steps=8 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/PursueSeek/Pursuer.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/PursueSeek/Player.gd" type="Script" id=2]
|
||||
[ext_resource path="res://Demos/PursueSeek/BoundaryManager.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/PursueSeek/PursueAndSeekDemo.gd" type="Script" id=4]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://Demos/Utils/Line2DDraw.gd" type="Script" id=6]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=7]
|
||||
|
||||
[node name="PursueVSSeekDemo" type="Node"]
|
||||
script = ExtResource( 4 )
|
||||
linear_speed_max = 1280.0
|
||||
linear_accel_max = 1040.0
|
||||
predict_time = 1.1
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 7 )]
|
||||
|
||||
[node name="BoundaryManager" type="Node2D" parent="."]
|
||||
script = ExtResource( 3 )
|
||||
|
||||
[node name="Player" type="KinematicBody2D" parent="BoundaryManager"]
|
||||
position = Vector2( 307.552, 555.999 )
|
||||
rotation = 1.5708
|
||||
collision_mask = 2
|
||||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_group_": true
|
||||
}
|
||||
thruster_strength = 1000.0
|
||||
side_thruster_strength = 40.0
|
||||
velocity_max = 450.0
|
||||
angular_velocity_max = 3.0
|
||||
|
||||
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="BoundaryManager/Player"]
|
||||
polygon = PoolVector2Array( 0, -32, -24, 32, 24, 32 )
|
||||
|
||||
[node name="Line2D" type="Line2D" parent="BoundaryManager/Player"]
|
||||
points = PoolVector2Array( 0, 32, 24, 32, 0, -32, -24, 32, 0, 32 )
|
||||
width = 8.0
|
||||
default_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
joint_mode = 2
|
||||
antialiased = true
|
||||
script = ExtResource( 6 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
|
||||
[node name="Pursuer" type="KinematicBody2D" parent="BoundaryManager"]
|
||||
position = Vector2( 1240.22, 866.784 )
|
||||
collision_layer = 2
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_group_": true
|
||||
}
|
||||
|
||||
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="BoundaryManager/Pursuer"]
|
||||
polygon = PoolVector2Array( 0, -32, -24, 32, 24, 32 )
|
||||
|
||||
[node name="Line2D" type="Line2D" parent="BoundaryManager/Pursuer"]
|
||||
points = PoolVector2Array( 0, 32, 24, 32, 0, -32, -24, 32, 0, 32 )
|
||||
width = 8.0
|
||||
default_color = Color( 1, 0.709804, 0.439216, 1 )
|
||||
joint_mode = 2
|
||||
antialiased = true
|
||||
script = ExtResource( 6 )
|
||||
inner_color = Color( 0.890196, 0.411765, 0.337255, 1 )
|
||||
|
||||
[node name="Seeker" type="KinematicBody2D" parent="BoundaryManager"]
|
||||
position = Vector2( 1240.22, 280.108 )
|
||||
rotation = 3.14159
|
||||
collision_layer = 2
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_group_": true
|
||||
}
|
||||
use_seek = true
|
||||
|
||||
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="BoundaryManager/Seeker"]
|
||||
polygon = PoolVector2Array( 0, -32, -24, 32, 24, 32 )
|
||||
|
||||
[node name="Line2D" type="Line2D" parent="BoundaryManager/Seeker"]
|
||||
points = PoolVector2Array( 0, 32, 24, 32, 0, -32, -24, 32, 0, 32 )
|
||||
width = 8.0
|
||||
default_color = Color( 0.301961, 0.65098, 1, 1 )
|
||||
joint_mode = 2
|
||||
antialiased = true
|
||||
script = ExtResource( 6 )
|
||||
inner_color = Color( 0.294118, 0.356863, 0.670588, 1 )
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 5 )]
|
||||
text_bbcode = "Pursue vs. Seek Demo
|
||||
Move the player around with WASD and notice the [color=#ffb570]orange Pursuer[/color] and the [color=aqua]blue Seeker[/color] follow
|
||||
the [color=lime]green \"Ship\"[/color] around"
|
80
project_gdscript/Demos/PursueSeek/Pursuer.gd
Normal file
80
project_gdscript/Demos/PursueSeek/Pursuer.gd
Normal file
@ -0,0 +1,80 @@
|
||||
extends KinematicBody2D
|
||||
# Represents a ship that chases after the player.
|
||||
|
||||
export var use_seek: bool = false
|
||||
|
||||
var _blend: GSAIBlend
|
||||
|
||||
var _linear_drag_coefficient := 0.025
|
||||
var _angular_drag := 0.1
|
||||
var _direction_face := GSAIAgentLocation.new()
|
||||
|
||||
var agent : GSAIKinematicBody2DAgent= null
|
||||
var accel : GSAITargetAcceleration = null
|
||||
var player_agent : GSAISteeringAgent = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
agent = GSAIKinematicBody2DAgent.new()
|
||||
agent.body = self
|
||||
|
||||
accel = GSAITargetAcceleration.new()
|
||||
player_agent = owner.find_node("Player", true, false).agent
|
||||
|
||||
agent.calculate_velocities = false
|
||||
set_physics_process(false)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
_direction_face.position = agent.position + accel.linear.normalized()
|
||||
|
||||
_blend.calculate_steering(accel)
|
||||
|
||||
agent.angular_velocity = clamp(
|
||||
agent.angular_velocity + accel.angular * delta, -agent.angular_speed_max, agent.angular_speed_max
|
||||
)
|
||||
agent.angular_velocity = lerp(agent.angular_velocity, 0, _angular_drag)
|
||||
|
||||
rotation += agent.angular_velocity * delta
|
||||
|
||||
var linear_velocity := (
|
||||
GSAIUtils.to_vector2(agent.linear_velocity)
|
||||
+ (GSAIUtils.angle_to_vector2(rotation) * -agent.linear_acceleration_max * delta)
|
||||
)
|
||||
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 = GSAIUtils.to_vector3(linear_velocity)
|
||||
|
||||
|
||||
func setup(predict_time: float, linear_speed_max: float, linear_accel_max: float) -> void:
|
||||
var behavior: GSAISteeringBehavior
|
||||
if use_seek:
|
||||
behavior = GSAISeek.new()
|
||||
behavior.agent = agent
|
||||
behavior.target = player_agent
|
||||
else:
|
||||
behavior = GSAIPursue.new()
|
||||
behavior.agent = agent
|
||||
behavior.target = player_agent
|
||||
behavior.predict_time_max = predict_time
|
||||
|
||||
var orient_behavior : GSAIFace = GSAIFace.new()
|
||||
orient_behavior.agent = agent
|
||||
orient_behavior.target = _direction_face
|
||||
|
||||
orient_behavior.alignment_tolerance = deg2rad(5)
|
||||
orient_behavior.deceleration_radius = deg2rad(30)
|
||||
|
||||
_blend = GSAIBlend.new()
|
||||
_blend.agent = agent
|
||||
_blend.add_behavior(behavior, 1)
|
||||
_blend.add_behavior(orient_behavior, 1)
|
||||
|
||||
agent.angular_acceleration_max = deg2rad(1080)
|
||||
agent.angular_speed_max = deg2rad(360)
|
||||
agent.linear_acceleration_max = linear_accel_max
|
||||
agent.linear_speed_max = linear_speed_max
|
||||
|
||||
set_physics_process(true)
|
195
project_gdscript/Demos/Quickstart/Agent.gd
Normal file
195
project_gdscript/Demos/Quickstart/Agent.gd
Normal file
@ -0,0 +1,195 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
# Maximum possible linear velocity
|
||||
export var speed_max := 450.0
|
||||
# Maximum change in linear velocity
|
||||
export var acceleration_max := 50.0
|
||||
# Maximum rotation velocity represented in degrees
|
||||
export var angular_speed_max := 240
|
||||
# Maximum change in rotation velocity represented in degrees
|
||||
export var angular_acceleration_max := 40
|
||||
|
||||
export var health_max := 100
|
||||
export var flee_health_threshold := 20
|
||||
|
||||
var velocity := Vector2.ZERO
|
||||
var angular_velocity := 0.0
|
||||
var linear_drag := 0.1
|
||||
var angular_drag := 0.1
|
||||
|
||||
# Holds the linear and angular components calculated by our steering behaviors.
|
||||
var acceleration : GSAITargetAcceleration = null
|
||||
|
||||
onready var current_health := health_max
|
||||
|
||||
# GSAISteeringAgent holds our agent's position, orientation, maximum speed and acceleration.
|
||||
var agent : GSAISteeringAgent = null
|
||||
|
||||
var player: Node = null
|
||||
# This assumes that our player class will keep its own agent updated.
|
||||
var player_agent : GSAISteeringAgent = null
|
||||
|
||||
# Proximities represent an area with which an agent can identify where neighbors in its relevant
|
||||
# group are. In our case, the group will feature the player, which will be used to avoid a
|
||||
# collision with them. We use a radius proximity so the player is only relevant inside 100 pixels.
|
||||
var proximity : GSAIRadiusProximity = null
|
||||
|
||||
# GSAIBlend combines behaviors together, calculating all of their acceleration together and adding
|
||||
# them together, multiplied by a strength. We will have one for fleeing, and one for pursuing,
|
||||
# toggling them depending on the agent's health. Since we want the agent to rotate AND move, then
|
||||
# we aim to blend them together.
|
||||
var flee_blend : GSAIBlend = null
|
||||
var pursue_blend : GSAIBlend = null
|
||||
|
||||
# GSAIPriority will be the main steering behavior we use. It holds sub-behaviors and will pick the
|
||||
# first one that returns non-zero acceleration, ignoring any afterwards.
|
||||
var priority : GSAIPriority = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
acceleration = GSAITargetAcceleration.new()
|
||||
agent = GSAISteeringAgent.new()
|
||||
player = get_tree().get_nodes_in_group("Player")[0]
|
||||
player_agent = player.agent
|
||||
|
||||
proximity = GSAIRadiusProximity.new()
|
||||
proximity.agent = agent
|
||||
proximity.agents = [ player_agent ]
|
||||
proximity.radius = 100
|
||||
|
||||
flee_blend = GSAIBlend.new()
|
||||
flee_blend.agent = agent
|
||||
|
||||
pursue_blend = GSAIBlend.new()
|
||||
pursue_blend.agent = agent
|
||||
|
||||
priority = GSAIPriority.new()
|
||||
priority.agent = agent
|
||||
|
||||
# ---------- Configuration for our agent ----------
|
||||
agent.linear_speed_max = speed_max
|
||||
agent.linear_acceleration_max = acceleration_max
|
||||
agent.angular_speed_max = deg2rad(angular_speed_max)
|
||||
agent.angular_acceleration_max = deg2rad(angular_acceleration_max)
|
||||
agent.bounding_radius = calculate_radius($CollisionPolygon2D.polygon)
|
||||
update_agent()
|
||||
|
||||
# ---------- Configuration for our behaviors ----------
|
||||
# Pursue will happen while the agent is in good health. It produces acceleration that takes
|
||||
# the agent on an intercept course with the target, predicting its position in the future.
|
||||
var pursue : GSAIPursue = GSAIPursue.new()
|
||||
pursue.agent = agent
|
||||
pursue.target = player_agent
|
||||
pursue.predict_time_max = 1.5
|
||||
|
||||
# Flee will happen while the agent is in bad health, so will start disabled. It produces
|
||||
# acceleration that takes the agent directly away from the target with no prediction.
|
||||
var flee : GSAIFlee = GSAIFlee.new()
|
||||
flee.agent = agent
|
||||
flee.target = player_agent
|
||||
|
||||
# AvoidCollision tries to keep the agent from running into any of the neighbors found in its
|
||||
# proximity group. In our case, this will be the player, if they are close enough.
|
||||
var avoid : GSAIAvoidCollisions = GSAIAvoidCollisions.new()
|
||||
avoid.agent = agent
|
||||
avoid.proximity = proximity
|
||||
|
||||
# Face turns the agent to keep looking towards its target. It will be enabled while the agent
|
||||
# is not fleeing due to low health. It tries to arrive 'on alignment' with 0 remaining velocity.
|
||||
var face : GSAIFace = GSAIFace.new()
|
||||
face.agent = agent
|
||||
face.target = player_agent
|
||||
|
||||
# We use deg2rad because the math in the toolkit assumes radians.
|
||||
# How close for the agent to be 'aligned', if not exact.
|
||||
face.alignment_tolerance = deg2rad(5)
|
||||
# When to start slowing down
|
||||
face.deceleration_radius = deg2rad(60)
|
||||
|
||||
# LookWhereYouGo turns the agent to keep looking towards its direction of travel. It will only
|
||||
# be enabled while the agent is at low health.
|
||||
var look : GSAILookWhereYouGo = GSAILookWhereYouGo.new()
|
||||
look.agent = agent
|
||||
# How close for the agent to be 'aligned', if not exact
|
||||
look.alignment_tolerance = deg2rad(5)
|
||||
# When to start slowing down.
|
||||
look.deceleration_radius = deg2rad(60)
|
||||
|
||||
# Behaviors that are not enabled produce 0 acceleration.
|
||||
# Adding our fleeing behaviors to a blend. The order does not matter.
|
||||
flee_blend.is_enabled = false
|
||||
flee_blend.add_behavior(look, 1)
|
||||
flee_blend.add_behavior(flee, 1)
|
||||
|
||||
# Adding our pursuit behaviors to a blend. The order does not matter.
|
||||
pursue_blend.add_behavior(face, 1)
|
||||
pursue_blend.add_behavior(pursue, 1)
|
||||
|
||||
# Adding our final behaviors to the main priority behavior. The order does matter here.
|
||||
# We want to avoid collision with the player first, flee from the player second when enabled,
|
||||
# and pursue the player last when enabled.
|
||||
priority.add_behavior(avoid)
|
||||
priority.add_behavior(flee_blend)
|
||||
priority.add_behavior(pursue_blend)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
# Make sure any change in position and speed has been recorded.
|
||||
update_agent()
|
||||
|
||||
if current_health <= flee_health_threshold:
|
||||
pursue_blend.is_enabled = false
|
||||
flee_blend.is_enabled = true
|
||||
|
||||
# Calculate the desired acceleration.
|
||||
priority.calculate_steering(acceleration)
|
||||
|
||||
# We add the discovered acceleration to our linear velocity. The toolkit does not limit
|
||||
# velocity, just acceleration, so we clamp the result ourselves here.
|
||||
velocity = (velocity + Vector2(acceleration.linear.x, acceleration.linear.y) * delta).clamped(
|
||||
agent.linear_speed_max
|
||||
)
|
||||
|
||||
# This applies drag on the agent's motion, helping it to slow down naturally.
|
||||
velocity = velocity.linear_interpolate(Vector2.ZERO, linear_drag)
|
||||
|
||||
# And since we're using a KinematicBody2D, we use Godot's excellent move_and_slide to actually
|
||||
# apply the final movement, and record any change in velocity the physics engine discovered.
|
||||
velocity = move_and_slide(velocity)
|
||||
|
||||
# We then do something similar to apply our agent's rotational speed.
|
||||
angular_velocity = clamp(
|
||||
angular_velocity + acceleration.angular * delta, -agent.angular_speed_max, agent.angular_speed_max
|
||||
)
|
||||
# This applies drag on the agent's rotation, helping it slow down naturally.
|
||||
angular_velocity = lerp(angular_velocity, 0, angular_drag)
|
||||
rotation += angular_velocity * delta
|
||||
|
||||
|
||||
# In order to support both 2D and 3D, the toolkit uses Vector3, so the conversion is required
|
||||
# when using 2D nodes. The Z component can be left to 0 safely.
|
||||
func update_agent() -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
agent.orientation = rotation
|
||||
agent.linear_velocity.x = velocity.x
|
||||
agent.linear_velocity.y = velocity.y
|
||||
agent.angular_velocity = angular_velocity
|
||||
|
||||
|
||||
# We calculate the radius from the collision shape - this will approximate the agent's size in the
|
||||
# game world, to avoid collisions with the player.
|
||||
func calculate_radius(polygon: PoolVector2Array) -> float:
|
||||
var furthest_point := Vector2(-INF, -INF)
|
||||
for p in polygon:
|
||||
if abs(p.x) > furthest_point.x:
|
||||
furthest_point.x = p.x
|
||||
if abs(p.y) > furthest_point.y:
|
||||
furthest_point.y = p.y
|
||||
return furthest_point.length()
|
||||
|
||||
|
||||
func damage(amount: int) -> void:
|
||||
current_health -= amount
|
||||
if current_health <= 0:
|
||||
queue_free()
|
33
project_gdscript/Demos/Quickstart/Bullet.gd
Normal file
33
project_gdscript/Demos/Quickstart/Bullet.gd
Normal file
@ -0,0 +1,33 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
export var speed := 1500.0
|
||||
|
||||
var velocity := Vector2.ZERO
|
||||
var player: Node
|
||||
|
||||
onready var timer := $Lifetime
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
timer.connect("timeout", self, "_on_Lifetime_timeout")
|
||||
timer.start()
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var collision := move_and_collide(velocity * delta)
|
||||
if collision:
|
||||
timer.stop()
|
||||
clear()
|
||||
collision.collider.damage(10)
|
||||
|
||||
|
||||
func start(direction: Vector2) -> void:
|
||||
velocity = direction * speed
|
||||
|
||||
|
||||
func clear() -> void:
|
||||
queue_free()
|
||||
|
||||
|
||||
func _on_Lifetime_timeout() -> void:
|
||||
clear()
|
23
project_gdscript/Demos/Quickstart/Bullet.tscn
Normal file
23
project_gdscript/Demos/Quickstart/Bullet.tscn
Normal file
@ -0,0 +1,23 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/Quickstart/Bullet.gd" type="Script" id=2]
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 4.0
|
||||
|
||||
[node name="Bullet" type="KinematicBody2D"]
|
||||
collision_layer = 4
|
||||
collision_mask = 2
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 1 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
outer_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
stroke = 2.0
|
||||
|
||||
[node name="Lifetime" type="Timer" parent="."]
|
||||
process_mode = 0
|
||||
wait_time = 3.0
|
99
project_gdscript/Demos/Quickstart/Player.gd
Normal file
99
project_gdscript/Demos/Quickstart/Player.gd
Normal file
@ -0,0 +1,99 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
export var speed_max := 650.0
|
||||
export var acceleration_max := 70.0
|
||||
export var rotation_speed_max := 240
|
||||
export var rotation_accel_max := 40
|
||||
export var bullet: PackedScene
|
||||
|
||||
var velocity := Vector2.ZERO
|
||||
var angular_velocity := 0.0
|
||||
var direction := Vector2.RIGHT
|
||||
|
||||
var agent : GSAISteeringAgent = null
|
||||
var proxy_target : GSAIAgentLocation = null
|
||||
var face : GSAIFace = null
|
||||
|
||||
onready var accel := GSAITargetAcceleration.new()
|
||||
onready var bullets := owner.get_node("Bullets")
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
agent = GSAISteeringAgent.new()
|
||||
proxy_target = GSAIAgentLocation.new()
|
||||
face = GSAIFace.new()
|
||||
face.agent = agent
|
||||
face.target = proxy_target
|
||||
|
||||
agent.linear_speed_max = speed_max
|
||||
agent.linear_acceleration_max = acceleration_max
|
||||
agent.angular_speed_max = deg2rad(rotation_speed_max)
|
||||
agent.angular_acceleration_max = deg2rad(rotation_accel_max)
|
||||
agent.bounding_radius = calculate_radius($CollisionPolygon2D.polygon)
|
||||
update_agent()
|
||||
|
||||
var mouse_pos := get_global_mouse_position()
|
||||
proxy_target.position.x = mouse_pos.x
|
||||
proxy_target.position.y = mouse_pos.y
|
||||
|
||||
face.alignment_tolerance = deg2rad(5)
|
||||
face.deceleration_radius = deg2rad(45)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
update_agent()
|
||||
|
||||
var movement := get_movement()
|
||||
|
||||
direction = GSAIUtils.angle_to_vector2(rotation)
|
||||
|
||||
velocity += direction * acceleration_max * movement * delta
|
||||
velocity = velocity.clamped(speed_max)
|
||||
velocity = velocity.linear_interpolate(Vector2.ZERO, 0.1)
|
||||
velocity = move_and_slide(velocity)
|
||||
|
||||
face.calculate_steering(accel)
|
||||
angular_velocity += accel.angular * delta
|
||||
angular_velocity = clamp(angular_velocity, -agent.angular_speed_max, agent.angular_speed_max)
|
||||
angular_velocity = lerp(angular_velocity, 0, 0.1)
|
||||
rotation += angular_velocity * delta
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion:
|
||||
var mouse_pos: Vector2 = event.position
|
||||
proxy_target.position.x = mouse_pos.x
|
||||
proxy_target.position.y = mouse_pos.y
|
||||
elif event is InputEventMouseButton:
|
||||
if event.button_index == BUTTON_LEFT and event.pressed:
|
||||
var next_bullet := bullet.instance()
|
||||
next_bullet.global_position = (
|
||||
global_position
|
||||
- direction * (agent.bounding_radius - 5)
|
||||
)
|
||||
next_bullet.player = self
|
||||
next_bullet.start(-direction)
|
||||
bullets.add_child(next_bullet)
|
||||
|
||||
|
||||
func get_movement() -> float:
|
||||
return Input.get_action_strength("sf_down") - Input.get_action_strength("sf_up")
|
||||
|
||||
|
||||
func update_agent() -> void:
|
||||
agent.position.x = global_position.x
|
||||
agent.position.y = global_position.y
|
||||
agent.orientation = rotation
|
||||
agent.linear_velocity.x = velocity.x
|
||||
agent.linear_velocity.y = velocity.y
|
||||
agent.angular_velocity = angular_velocity
|
||||
|
||||
|
||||
func calculate_radius(polygon: PoolVector2Array) -> float:
|
||||
var furthest_point := Vector2(-INF, -INF)
|
||||
for p in polygon:
|
||||
if abs(p.x) > furthest_point.x:
|
||||
furthest_point.x = p.x
|
||||
if abs(p.y) > furthest_point.y:
|
||||
furthest_point.y = p.y
|
||||
return furthest_point.length()
|
61
project_gdscript/Demos/Quickstart/QuickStartDemo.tscn
Normal file
61
project_gdscript/Demos/Quickstart/QuickStartDemo.tscn
Normal file
@ -0,0 +1,61 @@
|
||||
[gd_scene load_steps=6 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/Utils/Line2DDraw.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/Quickstart/Agent.gd" type="Script" id=2]
|
||||
[ext_resource path="res://Demos/Quickstart/Player.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/Quickstart/Bullet.tscn" type="PackedScene" id=4]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=5]
|
||||
|
||||
[node name="QuickStartDemo" type="Node"]
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 5 )]
|
||||
|
||||
[node name="Player" type="KinematicBody2D" parent="." groups=[
|
||||
"Player",
|
||||
]]
|
||||
position = Vector2( 402.346, 573.791 )
|
||||
rotation = 1.5708
|
||||
collision_mask = 2
|
||||
script = ExtResource( 3 )
|
||||
speed_max = 900.0
|
||||
acceleration_max = 4200.0
|
||||
rotation_speed_max = 360
|
||||
rotation_accel_max = 1280
|
||||
bullet = ExtResource( 4 )
|
||||
|
||||
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Player"]
|
||||
polygon = PoolVector2Array( 0, -32, -24, 32, 24, 32 )
|
||||
|
||||
[node name="Line2D" type="Line2D" parent="Player"]
|
||||
points = PoolVector2Array( 0, 32, 24, 32, 0, -32, -24, 32, 0, 32 )
|
||||
width = 8.0
|
||||
default_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
joint_mode = 2
|
||||
antialiased = true
|
||||
script = ExtResource( 1 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
|
||||
[node name="Agent" type="KinematicBody2D" parent="."]
|
||||
position = Vector2( 974.675, 266.224 )
|
||||
rotation = 1.5708
|
||||
collision_layer = 2
|
||||
collision_mask = 5
|
||||
script = ExtResource( 2 )
|
||||
speed_max = 600.0
|
||||
acceleration_max = 2800.0
|
||||
angular_speed_max = 360
|
||||
angular_acceleration_max = 1280
|
||||
|
||||
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Agent"]
|
||||
polygon = PoolVector2Array( 0, -32, -24, 32, 24, 32 )
|
||||
|
||||
[node name="Line2D" type="Line2D" parent="Agent"]
|
||||
points = PoolVector2Array( 0, 32, 24, 32, 0, -32, -24, 32, 0, 32 )
|
||||
width = 8.0
|
||||
default_color = Color( 1, 0.709804, 0.439216, 1 )
|
||||
joint_mode = 2
|
||||
antialiased = true
|
||||
script = ExtResource( 1 )
|
||||
inner_color = Color( 0.890196, 0.411765, 0.337255, 1 )
|
||||
|
||||
[node name="Bullets" type="Node2D" parent="."]
|
32
project_gdscript/Demos/SeekFlee/Boundaries.gd
Normal file
32
project_gdscript/Demos/SeekFlee/Boundaries.gd
Normal file
@ -0,0 +1,32 @@
|
||||
extends Node2D
|
||||
|
||||
const COLOR := Color("8fde5d")
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
get_tree().root.connect("size_changed", self, "_on_SceneTree_size_changed")
|
||||
_on_SceneTree_size_changed()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
for b in get_children():
|
||||
var extents: Vector2 = b.get_node("CollisionShape2D").shape.extents
|
||||
draw_rect(Rect2(b.global_position - extents, extents * 2), COLOR)
|
||||
|
||||
|
||||
func _on_SceneTree_size_changed() -> void:
|
||||
var size := Vector2(
|
||||
ProjectSettings["display/window/size/width"], ProjectSettings["display/window/size/height"]
|
||||
)
|
||||
for b in get_children():
|
||||
var boundary: String = str(b.name).rsplit("Boundary")[0]
|
||||
match boundary:
|
||||
"Left":
|
||||
b.global_position = Vector2(0, size.y / 2)
|
||||
"Right":
|
||||
b.global_position = Vector2(size.x, size.y / 2)
|
||||
"Top":
|
||||
b.global_position = Vector2(size.x / 2, 0)
|
||||
"Bottom":
|
||||
b.global_position = Vector2(size.x / 2, size.y)
|
||||
update()
|
26
project_gdscript/Demos/SeekFlee/Player.gd
Normal file
26
project_gdscript/Demos/SeekFlee/Player.gd
Normal file
@ -0,0 +1,26 @@
|
||||
extends KinematicBody2D
|
||||
# Class to control the player in basic left/right up/down movement.
|
||||
|
||||
var speed: float
|
||||
onready var agent := GSAIAgentLocation.new()
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
agent.position = GSAIUtils.to_vector3(global_position)
|
||||
|
||||
|
||||
func _physics_process(_delta: float) -> void:
|
||||
var movement := _get_movement()
|
||||
if movement.length_squared() < 0.01:
|
||||
return
|
||||
|
||||
# warning-ignore:return_value_discarded
|
||||
move_and_slide(movement * speed)
|
||||
agent.position = GSAIUtils.to_vector3(global_position)
|
||||
|
||||
|
||||
func _get_movement() -> Vector2:
|
||||
return Vector2(
|
||||
Input.get_action_strength("sf_right") - Input.get_action_strength("sf_left"),
|
||||
Input.get_action_strength("sf_down") - Input.get_action_strength("sf_up")
|
||||
)
|
82
project_gdscript/Demos/SeekFlee/SeekFleeDemo.gd
Normal file
82
project_gdscript/Demos/SeekFlee/SeekFleeDemo.gd
Normal file
@ -0,0 +1,82 @@
|
||||
extends Node
|
||||
# Access helper class for children to access window boundaries.
|
||||
|
||||
enum Mode { FLEE, SEEK }
|
||||
|
||||
export (Mode) var behavior_mode := Mode.SEEK setget set_behavior_mode
|
||||
export (float, 0, 1000, 30) var linear_speed_max := 200.0 setget set_linear_speed_max
|
||||
export (float, 0, 2000, 40) var linear_accel_max := 10.0 setget set_linear_accel_max
|
||||
export (float) var player_speed := 600.0 setget set_player_speed
|
||||
|
||||
var camera_boundaries: Rect2
|
||||
|
||||
onready var player: KinematicBody2D = $Player
|
||||
onready var spawner: Node2D = $Spawner
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
camera_boundaries = Rect2(
|
||||
Vector2.ZERO,
|
||||
Vector2(
|
||||
ProjectSettings["display/window/size/width"],
|
||||
ProjectSettings["display/window/size/height"]
|
||||
)
|
||||
)
|
||||
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.randomize()
|
||||
|
||||
player.speed = player_speed
|
||||
|
||||
for i in range(spawner.entity_count):
|
||||
var new_pos := Vector2(
|
||||
rng.randf_range(0, camera_boundaries.size.x),
|
||||
rng.randf_range(0, camera_boundaries.size.y)
|
||||
)
|
||||
var entity: KinematicBody2D = spawner.Entity.instance()
|
||||
entity.global_position = new_pos
|
||||
entity.player_agent = player.agent
|
||||
entity.start_speed = linear_speed_max
|
||||
entity.start_accel = linear_accel_max
|
||||
entity.use_seek = behavior_mode == Mode.SEEK
|
||||
spawner.add_child(entity)
|
||||
|
||||
|
||||
func set_behavior_mode(mode: int) -> void:
|
||||
behavior_mode = mode
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
match mode:
|
||||
Mode.SEEK:
|
||||
for child in spawner.get_children():
|
||||
child.use_seek = true
|
||||
Mode.FLEE:
|
||||
for child in spawner.get_children():
|
||||
child.use_seek = false
|
||||
|
||||
|
||||
func set_linear_speed_max(value: float) -> void:
|
||||
linear_speed_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
for child in spawner.get_children():
|
||||
child.agent.linear_speed_max = value
|
||||
|
||||
|
||||
func set_linear_accel_max(value: float) -> void:
|
||||
linear_accel_max = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
for child in spawner.get_children():
|
||||
child.agent.linear_acceleration_max = value
|
||||
|
||||
|
||||
func set_player_speed(value: float) -> void:
|
||||
player_speed = value
|
||||
if not is_inside_tree():
|
||||
return
|
||||
|
||||
player.speed = player_speed
|
81
project_gdscript/Demos/SeekFlee/SeekFleeDemo.tscn
Normal file
81
project_gdscript/Demos/SeekFlee/SeekFleeDemo.tscn
Normal file
@ -0,0 +1,81 @@
|
||||
[gd_scene load_steps=12 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/SeekFlee/Player.gd" type="Script" id=2]
|
||||
[ext_resource path="res://Demos/SeekFlee/SeekFleeDemo.gd" type="Script" id=3]
|
||||
[ext_resource path="res://Demos/SeekFlee/Spawner.gd" type="Script" id=4]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.tscn" type="PackedScene" id=5]
|
||||
[ext_resource path="res://Demos/SeekFlee/Seeker.tscn" type="PackedScene" id=6]
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=7]
|
||||
[ext_resource path="res://Demos/Utils/BackgroudLayer.tscn" type="PackedScene" id=8]
|
||||
[ext_resource path="res://Demos/SeekFlee/Boundaries.gd" type="Script" id=9]
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 32.0
|
||||
|
||||
[sub_resource type="RectangleShape2D" id=2]
|
||||
extents = Vector2( 10, 542 )
|
||||
|
||||
[sub_resource type="RectangleShape2D" id=3]
|
||||
extents = Vector2( 965.654, 10 )
|
||||
|
||||
[node name="SeekFleeDemo" type="Node"]
|
||||
script = ExtResource( 3 )
|
||||
linear_speed_max = 570.0
|
||||
linear_accel_max = 1160.0
|
||||
|
||||
[node name="BackgroudLayer" parent="." instance=ExtResource( 8 )]
|
||||
|
||||
[node name="Player" type="KinematicBody2D" parent="."]
|
||||
position = Vector2( 960, 540 )
|
||||
collision_mask = 2
|
||||
script = ExtResource( 2 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Player"]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 7 )
|
||||
inner_color = Color( 0.235294, 0.639216, 0.439216, 1 )
|
||||
outer_color = Color( 0.560784, 0.870588, 0.364706, 1 )
|
||||
stroke = 4.0
|
||||
|
||||
[node name="Boundaries" type="Node2D" parent="."]
|
||||
script = ExtResource( 9 )
|
||||
|
||||
[node name="LeftBoundary" type="StaticBody2D" parent="Boundaries"]
|
||||
position = Vector2( 0, 540 )
|
||||
collision_layer = 2
|
||||
collision_mask = 5
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Boundaries/LeftBoundary"]
|
||||
shape = SubResource( 2 )
|
||||
|
||||
[node name="RightBoundary" type="StaticBody2D" parent="Boundaries"]
|
||||
position = Vector2( 1920, 540 )
|
||||
collision_layer = 2
|
||||
collision_mask = 5
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Boundaries/RightBoundary"]
|
||||
shape = SubResource( 2 )
|
||||
|
||||
[node name="TopBoundary" type="StaticBody2D" parent="Boundaries"]
|
||||
position = Vector2( 960, 0 )
|
||||
collision_layer = 2
|
||||
collision_mask = 5
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Boundaries/TopBoundary"]
|
||||
shape = SubResource( 3 )
|
||||
|
||||
[node name="BottomBoundary" type="StaticBody2D" parent="Boundaries"]
|
||||
position = Vector2( 960, 1080 )
|
||||
collision_layer = 2
|
||||
collision_mask = 5
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Boundaries/BottomBoundary"]
|
||||
shape = SubResource( 3 )
|
||||
|
||||
[node name="Spawner" type="Node2D" parent="."]
|
||||
script = ExtResource( 4 )
|
||||
Entity = ExtResource( 6 )
|
||||
|
||||
[node name="DemoInterface" parent="." instance=ExtResource( 5 )]
|
||||
text_bbcode = "Seek & Flee Demo
|
||||
Move the [color=lime]green \"Player\"[/color] around with WASD and notice the [color=#ffb570]orange \"Enemies\"[/color] try to seek to or flee from the player."
|
42
project_gdscript/Demos/SeekFlee/Seeker.gd
Normal file
42
project_gdscript/Demos/SeekFlee/Seeker.gd
Normal file
@ -0,0 +1,42 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
var player_agent: GSAIAgentLocation
|
||||
var velocity := Vector2.ZERO
|
||||
var start_speed: float
|
||||
var start_accel: float
|
||||
var use_seek := true
|
||||
|
||||
var agent : GSAIKinematicBody2DAgent = null
|
||||
var accel : GSAITargetAcceleration = null
|
||||
var seek : GSAISeek = null
|
||||
var flee : GSAIFlee = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
agent = GSAIKinematicBody2DAgent.new()
|
||||
agent.body = self
|
||||
|
||||
accel = GSAITargetAcceleration.new()
|
||||
|
||||
seek = GSAISeek.new()
|
||||
seek.agent = agent
|
||||
seek.target = player_agent
|
||||
|
||||
flee = GSAIFlee.new()
|
||||
flee.agent = agent
|
||||
flee.target = player_agent
|
||||
|
||||
agent.linear_acceleration_max = start_accel
|
||||
agent.linear_speed_max = start_speed
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not player_agent:
|
||||
return
|
||||
|
||||
if use_seek:
|
||||
seek.calculate_steering(accel)
|
||||
else:
|
||||
flee.calculate_steering(accel)
|
||||
|
||||
agent.apply_steering(accel, delta)
|
19
project_gdscript/Demos/SeekFlee/Seeker.tscn
Normal file
19
project_gdscript/Demos/SeekFlee/Seeker.tscn
Normal file
@ -0,0 +1,19 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://Demos/SeekFlee/Seeker.gd" type="Script" id=1]
|
||||
[ext_resource path="res://Demos/Utils/CircleDraw.gd" type="Script" id=2]
|
||||
|
||||
[sub_resource type="CircleShape2D" id=1]
|
||||
radius = 16.0
|
||||
|
||||
[node name="Seeker" type="KinematicBody2D"]
|
||||
collision_layer = 4
|
||||
collision_mask = 2
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
shape = SubResource( 1 )
|
||||
script = ExtResource( 2 )
|
||||
inner_color = Color( 0.890196, 0.411765, 0.337255, 1 )
|
||||
outer_color = Color( 1, 0.709804, 0.439216, 1 )
|
||||
stroke = 4.0
|
6
project_gdscript/Demos/SeekFlee/Spawner.gd
Normal file
6
project_gdscript/Demos/SeekFlee/Spawner.gd
Normal file
@ -0,0 +1,6 @@
|
||||
extends Node2D
|
||||
# Holds data to instantiate and configure a number of agent entities.
|
||||
|
||||
export (PackedScene) var Entity: PackedScene
|
||||
export var entity_count := 10
|
||||
export var entity_color := Color.blue
|
17
project_gdscript/Demos/Utils/BackgroudLayer.tscn
Normal file
17
project_gdscript/Demos/Utils/BackgroudLayer.tscn
Normal file
@ -0,0 +1,17 @@
|
||||
[gd_scene load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://assets/sprites/background.png" type="Texture" id=1]
|
||||
|
||||
[node name="BackgroudLayer" type="CanvasLayer"]
|
||||
layer = -1
|
||||
|
||||
[node name="Background" type="TextureRect" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
texture = ExtResource( 1 )
|
||||
expand = true
|
||||
stretch_mode = 7
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
26
project_gdscript/Demos/Utils/CircleDraw.gd
Normal file
26
project_gdscript/Demos/Utils/CircleDraw.gd
Normal file
@ -0,0 +1,26 @@
|
||||
tool
|
||||
extends CollisionShape2D
|
||||
|
||||
export (Color) var inner_color := Color() setget set_inner_color
|
||||
export (Color) var outer_color := Color() setget set_outer_color
|
||||
export (float) var stroke := 0.0 setget set_stroke
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
draw_circle(Vector2.ZERO, shape.radius + stroke, outer_color)
|
||||
draw_circle(Vector2.ZERO, shape.radius, inner_color)
|
||||
|
||||
|
||||
func set_inner_color(val: Color) -> void:
|
||||
inner_color = val
|
||||
update()
|
||||
|
||||
|
||||
func set_outer_color(val: Color) -> void:
|
||||
outer_color = val
|
||||
update()
|
||||
|
||||
|
||||
func set_stroke(val: float) -> void:
|
||||
stroke = val
|
||||
update()
|
13
project_gdscript/Demos/Utils/DemoInterface.gd
Normal file
13
project_gdscript/Demos/Utils/DemoInterface.gd
Normal file
@ -0,0 +1,13 @@
|
||||
tool
|
||||
extends PanelContainer
|
||||
|
||||
export (String, MULTILINE) var text_bbcode := "" setget set_text_bbcode
|
||||
|
||||
onready var rich_text_label: RichTextLabel = $MarginContainer/RichTextLabel
|
||||
|
||||
|
||||
func set_text_bbcode(value: String) -> void:
|
||||
text_bbcode = value
|
||||
if not rich_text_label:
|
||||
yield(self, "ready")
|
||||
rich_text_label.bbcode_text = text_bbcode
|
37
project_gdscript/Demos/Utils/DemoInterface.tscn
Normal file
37
project_gdscript/Demos/Utils/DemoInterface.tscn
Normal file
@ -0,0 +1,37 @@
|
||||
[gd_scene load_steps=4 format=2]
|
||||
|
||||
[ext_resource path="res://assets/theme/gdquest.theme" type="Theme" id=1]
|
||||
[ext_resource path="res://Demos/Utils/DemoInterface.gd" type="Script" id=2]
|
||||
|
||||
[sub_resource type="GDScript" id=1]
|
||||
script/source = "tool
|
||||
extends RichTextLabel
|
||||
|
||||
"
|
||||
|
||||
[node name="DemoInterface" type="PanelContainer"]
|
||||
anchor_right = 1.0
|
||||
margin_bottom = 140.0
|
||||
rect_min_size = Vector2( 1024, 0 )
|
||||
theme = ExtResource( 1 )
|
||||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
text_bbcode = "Replace this text for the demo."
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||
margin_right = 1920.0
|
||||
margin_bottom = 140.0
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="MarginContainer"]
|
||||
margin_left = 16.0
|
||||
margin_top = 16.0
|
||||
margin_right = 1904.0
|
||||
margin_bottom = 124.0
|
||||
rect_min_size = Vector2( 0, 55 )
|
||||
bbcode_enabled = true
|
||||
bbcode_text = "Replace this text for the demo."
|
||||
text = "Replace this text for the demo."
|
||||
scroll_active = false
|
||||
script = SubResource( 1 )
|
13
project_gdscript/Demos/Utils/Line2DDraw.gd
Normal file
13
project_gdscript/Demos/Utils/Line2DDraw.gd
Normal file
@ -0,0 +1,13 @@
|
||||
tool
|
||||
extends Line2D
|
||||
|
||||
export (Color) var inner_color := Color() setget set_inner_color
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
draw_colored_polygon(points, inner_color)
|
||||
|
||||
|
||||
func set_inner_color(val: Color) -> void:
|
||||
inner_color = val
|
||||
update()
|
@ -0,0 +1,164 @@
|
||||
extends GSAISpecializedAgent
|
||||
class_name GSAIKinematicBody2DAgent
|
||||
|
||||
# A specialized steering agent that updates itself every frame so the user does
|
||||
# not have to using a KinematicBody2D
|
||||
# @category - Specialized agents
|
||||
|
||||
# SLIDE uses `move_and_slide`
|
||||
# COLLIDE uses `move_and_collide`
|
||||
# POSITION changes the `global_position` directly
|
||||
enum MovementType {
|
||||
SLIDE,
|
||||
COLLIDE,
|
||||
POSITION
|
||||
}
|
||||
|
||||
# The KinematicBody2D to keep track of
|
||||
var body: KinematicBody2D setget _set_body
|
||||
|
||||
# The type of movement the body executes
|
||||
var movement_type : int = MovementType.SLIDE
|
||||
|
||||
var _last_position : Vector2
|
||||
var _body_ref : WeakRef
|
||||
|
||||
|
||||
func _body_ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame")
|
||||
|
||||
|
||||
# Moves the agent's `body` by target `acceleration`.
|
||||
# @tags - virtual
|
||||
func _apply_steering(acceleration: GSAITargetAcceleration, delta: float) -> void:
|
||||
applied_steering = true
|
||||
|
||||
if movement_type == MovementType.COLLIDE:
|
||||
_apply_collide_steering(acceleration.linear, delta)
|
||||
elif movement_type == MovementType.SLIDE:
|
||||
_apply_sliding_steering(acceleration.linear, delta)
|
||||
else:
|
||||
_apply_position_steering(acceleration.linear, delta)
|
||||
|
||||
_apply_orientation_steering(acceleration.angular, delta)
|
||||
|
||||
|
||||
func _apply_sliding_steering(accel: Vector3, delta: float) -> void:
|
||||
var _body: KinematicBody2D = _body_ref.get_ref()
|
||||
|
||||
if !_body:
|
||||
return
|
||||
|
||||
if !_body.is_inside_tree() or _body.get_tree().paused:
|
||||
return
|
||||
|
||||
var velocity : Vector2 = GSAIUtils.to_vector2(linear_velocity + accel * delta).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 = GSAIUtils.to_vector3(velocity)
|
||||
|
||||
|
||||
func _apply_collide_steering(accel: Vector3, delta: float) -> void:
|
||||
var _body: KinematicBody2D = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var velocity : Vector3 = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max)
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
|
||||
|
||||
# warning-ignore:return_value_discarded
|
||||
_body.move_and_collide(GSAIUtils.to_vector2(velocity) * delta)
|
||||
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_position_steering(accel: Vector3, delta: float) -> void:
|
||||
var _body: KinematicBody2D = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var velocity : Vector3 = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max)
|
||||
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
|
||||
|
||||
_body.global_position += GSAIUtils.to_vector2(velocity) * delta
|
||||
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void:
|
||||
var _body: KinematicBody2D = _body_ref.get_ref()
|
||||
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var velocity = clamp(angular_velocity + angular_acceleration * delta, -angular_speed_max, angular_speed_max)
|
||||
|
||||
if apply_angular_drag:
|
||||
velocity = lerp(velocity, 0, angular_drag_percentage)
|
||||
|
||||
_body.rotation += velocity * delta
|
||||
|
||||
if calculate_velocities:
|
||||
angular_velocity = velocity
|
||||
|
||||
|
||||
func _set_body(value: KinematicBody2D) -> void:
|
||||
var had_body : bool = false
|
||||
|
||||
if body:
|
||||
had_body = true
|
||||
|
||||
body = value
|
||||
_body_ref = weakref(body)
|
||||
|
||||
_last_position = value.global_position
|
||||
last_orientation = value.rotation
|
||||
|
||||
position = GSAIUtils.to_vector3(_last_position)
|
||||
orientation = last_orientation
|
||||
|
||||
if !had_body:
|
||||
if !body.is_inside_tree():
|
||||
body.connect("ready", self, "_body_ready")
|
||||
else:
|
||||
_body_ready()
|
||||
|
||||
|
||||
func _on_SceneTree_physics_frame() -> void:
|
||||
var _body: KinematicBody2D = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var current_position : Vector2 = _body.global_position
|
||||
var current_orientation : float = _body.rotation
|
||||
|
||||
position = GSAIUtils.to_vector3(current_position)
|
||||
orientation = current_orientation
|
||||
|
||||
if calculate_velocities:
|
||||
if applied_steering:
|
||||
applied_steering = false
|
||||
else:
|
||||
linear_velocity = GSAIUtils.clampedv3(GSAIUtils.to_vector3(current_position - _last_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
|
@ -0,0 +1,160 @@
|
||||
extends GSAISpecializedAgent
|
||||
class_name GSAIKinematicBody3DAgent
|
||||
|
||||
# A specialized steering agent that updates itself every frame so the user does
|
||||
# not have to using a KinematicBody
|
||||
# @category - Specialized agents
|
||||
|
||||
# SLIDE uses `move_and_slide`
|
||||
# COLLIDE uses `move_and_collide`
|
||||
# POSITION changes the global_position directly
|
||||
enum MovementType {
|
||||
SLIDE,
|
||||
COLLIDE,
|
||||
POSITION
|
||||
}
|
||||
|
||||
# The KinematicBody to keep track of
|
||||
var body : KinematicBody setget _set_body
|
||||
|
||||
# The type of movement the body executes
|
||||
var movement_type : int = 0
|
||||
|
||||
var _last_position : Vector3
|
||||
var _body_ref : WeakRef
|
||||
|
||||
|
||||
func _body_ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame")
|
||||
|
||||
|
||||
# Moves the agent's `body` by target `acceleration`.
|
||||
# @tags - virtual
|
||||
func _apply_steering(acceleration: GSAITargetAcceleration, delta: float) -> void:
|
||||
applied_steering = true
|
||||
if movement_type == MovementType.COLLIDE:
|
||||
_apply_collide_steering(acceleration.linear, delta)
|
||||
elif movement_type == MovementType.SLIDE:
|
||||
_apply_sliding_steering(acceleration.linear, delta)
|
||||
else:
|
||||
_apply_position_steering(acceleration.linear, delta)
|
||||
|
||||
_apply_orientation_steering(acceleration.angular, delta)
|
||||
|
||||
|
||||
func _apply_sliding_steering(accel: Vector3, delta: float) -> void:
|
||||
var _body: KinematicBody = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var velocity : Vector3 = GSAIUtils.clampedv3(linear_velocity + accel * delta, 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 _body: KinematicBody = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var velocity : Vector3 = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max)
|
||||
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
|
||||
|
||||
# warning-ignore:return_value_discarded
|
||||
_body.move_and_collide(velocity * delta)
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_position_steering(accel: Vector3, delta: float) -> void:
|
||||
var _body: KinematicBody = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var velocity : Vector3 = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max)
|
||||
|
||||
if apply_linear_drag:
|
||||
velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage)
|
||||
|
||||
_body.global_transform.origin += velocity * delta
|
||||
|
||||
if calculate_velocities:
|
||||
linear_velocity = velocity
|
||||
|
||||
|
||||
func _apply_orientation_steering(angular_acceleration: float, delta: float) -> void:
|
||||
var _body: KinematicBody = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
var velocity = clamp(angular_velocity + angular_acceleration * delta,-angular_speed_max,angular_speed_max)
|
||||
|
||||
if apply_angular_drag:
|
||||
velocity = lerp(velocity, 0, angular_drag_percentage)
|
||||
|
||||
_body.rotation.y += velocity * delta
|
||||
|
||||
if calculate_velocities:
|
||||
angular_velocity = velocity
|
||||
|
||||
|
||||
func _set_body(value: KinematicBody) -> void:
|
||||
var had_body : bool = false
|
||||
|
||||
if body:
|
||||
had_body = true
|
||||
|
||||
body = value
|
||||
_body_ref = weakref(value)
|
||||
|
||||
_last_position = value.transform.origin
|
||||
last_orientation = value.rotation.y
|
||||
|
||||
position = _last_position
|
||||
orientation = last_orientation
|
||||
|
||||
if !had_body:
|
||||
if !body.is_inside_tree():
|
||||
body.connect("ready", self, "_body_ready")
|
||||
else:
|
||||
_body_ready()
|
||||
|
||||
func _on_SceneTree_physics_frame() -> void:
|
||||
var _body : KinematicBody = _body_ref.get_ref()
|
||||
|
||||
if !_body:
|
||||
return
|
||||
|
||||
if !_body.is_inside_tree() || _body.get_tree().paused:
|
||||
return
|
||||
|
||||
var current_position : Vector3 = _body.transform.origin
|
||||
var current_orientation : float = _body.rotation.y
|
||||
|
||||
position = current_position
|
||||
orientation = current_orientation
|
||||
|
||||
if calculate_velocities:
|
||||
if applied_steering:
|
||||
applied_steering = false
|
||||
else:
|
||||
linear_velocity = GSAIUtils.clampedv3(current_position - _last_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
|
@ -0,0 +1,76 @@
|
||||
extends GSAISpecializedAgent
|
||||
class_name GSAIRigidBody2DAgent
|
||||
|
||||
# A specialized steering agent that updates itself every frame so the user does
|
||||
# not have to using a RigidBody2D
|
||||
# @category - Specialized agents
|
||||
|
||||
# The RigidBody2D to keep track of
|
||||
var body : RigidBody2D setget _set_body
|
||||
|
||||
var _last_position : Vector2
|
||||
var _body_ref : WeakRef
|
||||
|
||||
|
||||
func _body_ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
|
||||
|
||||
# Moves the agent's `body` by target `acceleration`.
|
||||
# @tags - virtual
|
||||
func _apply_steering(acceleration : GSAITargetAcceleration, _delta : float) -> void:
|
||||
var _body: RigidBody2D = _body_ref.get_ref()
|
||||
if not _body:
|
||||
return
|
||||
|
||||
applied_steering = true
|
||||
_body.apply_central_impulse(GSAIUtils.to_vector2(acceleration.linear))
|
||||
_body.apply_torque_impulse(acceleration.angular)
|
||||
if calculate_velocities:
|
||||
linear_velocity = GSAIUtils.to_vector3(_body.linear_velocity)
|
||||
angular_velocity = _body.angular_velocity
|
||||
|
||||
|
||||
func _set_body(value: RigidBody2D) -> void:
|
||||
var had_body : bool = false
|
||||
|
||||
if body:
|
||||
had_body = true
|
||||
|
||||
body = value
|
||||
_body_ref = weakref(value)
|
||||
|
||||
_last_position = value.global_position
|
||||
last_orientation = value.rotation
|
||||
|
||||
position = GSAIUtils.to_vector3(_last_position)
|
||||
orientation = last_orientation
|
||||
|
||||
if !had_body:
|
||||
if !body.is_inside_tree():
|
||||
body.connect("ready", self, "_body_ready")
|
||||
else:
|
||||
_body_ready()
|
||||
|
||||
|
||||
func _on_SceneTree_frame() -> void:
|
||||
var _body: RigidBody2D = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
if !_body.is_inside_tree() or _body.get_tree().paused:
|
||||
return
|
||||
|
||||
var current_position : Vector2 = _body.global_position
|
||||
var current_orientation : float = _body.rotation
|
||||
|
||||
position = GSAIUtils.to_vector3(current_position)
|
||||
orientation = current_orientation
|
||||
|
||||
if calculate_velocities:
|
||||
if applied_steering:
|
||||
applied_steering = false
|
||||
else:
|
||||
linear_velocity = GSAIUtils.to_vector3(_body.linear_velocity)
|
||||
angular_velocity = _body.angular_velocity
|
@ -0,0 +1,74 @@
|
||||
extends GSAISpecializedAgent
|
||||
class_name GSAIRigidBody3DAgent
|
||||
|
||||
# A specialized steering agent that updates itself every frame so the user does
|
||||
# not have to using a RigidBody
|
||||
# @category - Specialized agents
|
||||
|
||||
# The RigidBody to keep track of
|
||||
var body: RigidBody setget _set_body
|
||||
|
||||
var _last_position: Vector3
|
||||
var _body_ref: WeakRef
|
||||
|
||||
func _body_ready() -> void:
|
||||
# warning-ignore:return_value_discarded
|
||||
body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame")
|
||||
|
||||
|
||||
# Moves the agent's `body` by target `acceleration`.
|
||||
# @tags - virtual
|
||||
func _apply_steering(acceleration: GSAITargetAcceleration, _delta: float) -> void:
|
||||
var _body: RigidBody = _body_ref.get_ref()
|
||||
if !_body:
|
||||
return
|
||||
|
||||
applied_steering = true
|
||||
_body.apply_central_impulse(acceleration.linear)
|
||||
_body.apply_torque_impulse(Vector3.UP * acceleration.angular)
|
||||
if calculate_velocities:
|
||||
linear_velocity = _body.linear_velocity
|
||||
angular_velocity = _body.angular_velocity.y
|
||||
|
||||
|
||||
func _set_body(value: RigidBody) -> void:
|
||||
var had_body : bool = false
|
||||
|
||||
if body:
|
||||
had_body = true
|
||||
|
||||
body = value
|
||||
_body_ref = weakref(value)
|
||||
|
||||
_last_position = value.transform.origin
|
||||
last_orientation = value.rotation.y
|
||||
|
||||
position = _last_position
|
||||
orientation = last_orientation
|
||||
|
||||
if !had_body:
|
||||
if !body.is_inside_tree():
|
||||
body.connect("ready", self, "_body_ready")
|
||||
else:
|
||||
_body_ready()
|
||||
|
||||
func _on_SceneTree_frame() -> void:
|
||||
var _body: RigidBody = _body_ref.get_ref()
|
||||
if not _body:
|
||||
return
|
||||
|
||||
if not _body.is_inside_tree() or _body.get_tree().paused:
|
||||
return
|
||||
|
||||
var current_position : Vector3 = _body.transform.origin
|
||||
var current_orientation : float = _body.rotation.y
|
||||
|
||||
position = current_position
|
||||
orientation = current_orientation
|
||||
|
||||
if calculate_velocities:
|
||||
if applied_steering:
|
||||
applied_steering = false
|
||||
else:
|
||||
linear_velocity = _body.linear_velocity
|
||||
angular_velocity = _body.angular_velocity.y
|
@ -0,0 +1,42 @@
|
||||
extends GSAISteeringAgent
|
||||
class_name GSAISpecializedAgent
|
||||
|
||||
# A base class for a specialized steering agent that updates itself every frame
|
||||
# so the user does not have to. All other specialized agents derive from this.
|
||||
# @category - Specialized agents
|
||||
# @tags - abstract
|
||||
|
||||
# If `true`, calculates linear and angular velocities based on the previous
|
||||
# frame. When `false`, the user must keep those values updated.
|
||||
var calculate_velocities : bool = true
|
||||
|
||||
# If `true`, interpolates the current linear velocity towards 0 by the
|
||||
# `linear_drag_percentage` value.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var apply_linear_drag : bool = true
|
||||
|
||||
# If `true`, interpolates the current angular velocity towards 0 by the
|
||||
# `angular_drag_percentage` value.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var apply_angular_drag : bool = true
|
||||
|
||||
# The percentage between the current linear velocity and 0 to interpolate by if
|
||||
# `apply_linear_drag` is true.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var linear_drag_percentage : float = 0.0
|
||||
|
||||
# The percentage between the current angular velocity and 0 to interpolate by if
|
||||
# `apply_angular_drag` is true.
|
||||
# Does not apply to `RigidBody` and `RigidBody2D` nodes.
|
||||
var angular_drag_percentage : float = 0.0
|
||||
|
||||
var last_orientation : float = 0.0
|
||||
var applied_steering : bool = false
|
||||
|
||||
func apply_steering(_acceleration : GSAITargetAcceleration, _delta : float) -> void:
|
||||
call("_apply_steering", _acceleration, _delta)
|
||||
|
||||
# Moves the agent's body by target `acceleration`.
|
||||
# @tags - virtual
|
||||
func _apply_steering(_acceleration : GSAITargetAcceleration, _delta : float) -> void:
|
||||
pass
|
@ -0,0 +1,41 @@
|
||||
class_name GSAIArrive
|
||||
extends GSAISteeringBehavior
|
||||
|
||||
# Calculates acceleration to take an agent to its target's location. The
|
||||
# calculation attempts to arrive with zero remaining velocity.
|
||||
# @category - Individual behaviors
|
||||
|
||||
# Target agent to arrive to.
|
||||
var target : GSAIAgentLocation
|
||||
# Distance from the target for the agent to be considered successfully
|
||||
# arrived.
|
||||
var arrival_tolerance : float = 0.0
|
||||
# Distance from the target for the agent to begin slowing down.
|
||||
var deceleration_radius : float = 0.0
|
||||
# Represents the time it takes to change acceleration.
|
||||
var time_to_reach : float = 0.1
|
||||
|
||||
func arrive(acceleration : GSAITargetAcceleration, target_position : Vector3) -> void:
|
||||
call("_arrive", acceleration, target_position)
|
||||
|
||||
func _arrive(acceleration : GSAITargetAcceleration, target_position : Vector3) -> void:
|
||||
var to_target : Vector3 = target_position - agent.position
|
||||
var distance : float = to_target.length()
|
||||
|
||||
if distance <= arrival_tolerance:
|
||||
acceleration.set_zero()
|
||||
else:
|
||||
var desired_speed : float = agent.linear_speed_max
|
||||
|
||||
if distance <= deceleration_radius:
|
||||
desired_speed *= distance / deceleration_radius
|
||||
|
||||
var desired_velocity : Vector3 = to_target * desired_speed / distance
|
||||
|
||||
desired_velocity = ((desired_velocity - agent.linear_velocity) * 1.0 / time_to_reach)
|
||||
|
||||
acceleration.linear = GSAIUtils.clampedv3(desired_velocity, agent.linear_acceleration_max)
|
||||
acceleration.angular = 0
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
arrive(acceleration, target.position)
|
@ -0,0 +1,63 @@
|
||||
class_name GSAIAvoidCollisions
|
||||
extends GSAIGroupBehavior
|
||||
|
||||
# Steers the agent to avoid obstacles in its path. Approximates obstacles as
|
||||
# spheres.
|
||||
# @category - Group behaviors
|
||||
|
||||
var _first_neighbor: GSAISteeringAgent
|
||||
var _shortest_time : float = 0.0
|
||||
var _first_minimum_separation : float = 0.0
|
||||
var _first_distance : float = 0.0
|
||||
var _first_relative_position : Vector3
|
||||
var _first_relative_velocity : Vector3
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
_shortest_time = INF
|
||||
_first_neighbor = null
|
||||
_first_minimum_separation = 0
|
||||
_first_distance = 0
|
||||
|
||||
var neighbor_count : int = proximity.find_neighbors(_callback)
|
||||
|
||||
if neighbor_count == 0 or not _first_neighbor:
|
||||
acceleration.set_zero()
|
||||
else:
|
||||
if (_first_minimum_separation <= 0 || _first_distance < agent.bounding_radius + _first_neighbor.bounding_radius):
|
||||
acceleration.linear = _first_neighbor.position - agent.position
|
||||
else:
|
||||
acceleration.linear = (_first_relative_position+ (_first_relative_velocity * _shortest_time))
|
||||
|
||||
acceleration.linear = (acceleration.linear.normalized() * -agent.linear_acceleration_max)
|
||||
acceleration.angular = 0
|
||||
|
||||
|
||||
# Callback for the proximity to call when finding neighbors. Keeps track of every `neighbor`
|
||||
# that was found but only keeps the one the owning agent will most likely collide with.
|
||||
# @tags - virtual
|
||||
func _report_neighbor(neighbor: GSAISteeringAgent) -> bool:
|
||||
var relative_position : Vector3 = neighbor.position - agent.position
|
||||
var relative_velocity : Vector3 = neighbor.linear_velocity - agent.linear_velocity
|
||||
var relative_speed_squared : float = relative_velocity.length_squared()
|
||||
|
||||
if relative_speed_squared == 0:
|
||||
return false
|
||||
else:
|
||||
var time_to_collision : float = -relative_position.dot(relative_velocity) / relative_speed_squared
|
||||
|
||||
if time_to_collision <= 0 || time_to_collision >= _shortest_time:
|
||||
return false
|
||||
else:
|
||||
var distance = relative_position.length()
|
||||
var minimum_separation: float = (distance - sqrt(relative_speed_squared) * time_to_collision)
|
||||
|
||||
if minimum_separation > agent.bounding_radius + neighbor.bounding_radius:
|
||||
return false
|
||||
else:
|
||||
_shortest_time = time_to_collision
|
||||
_first_neighbor = neighbor
|
||||
_first_minimum_separation = minimum_separation
|
||||
_first_distance = distance
|
||||
_first_relative_position = relative_position
|
||||
_first_relative_velocity = relative_velocity
|
||||
return true
|
@ -0,0 +1,64 @@
|
||||
class_name GSAIBlend
|
||||
extends GSAISteeringBehavior
|
||||
|
||||
# Blends multiple steering behaviors into one, and returns a weighted
|
||||
# acceleration from their calculations.
|
||||
#
|
||||
# Stores the behaviors internally as dictionaries of the form
|
||||
# {
|
||||
# behavior : GSAISteeringBehavior,
|
||||
# weight : float
|
||||
# }
|
||||
# @category - Combination behaviors
|
||||
|
||||
var _behaviors : Array = Array()
|
||||
var _accel : GSAITargetAcceleration = GSAITargetAcceleration.new()
|
||||
|
||||
# Appends a behavior to the internal array along with its `weight`.
|
||||
func add_behavior(behavior : GSAISteeringBehavior, weight : float) -> void:
|
||||
behavior.agent = agent
|
||||
|
||||
var dict : Dictionary = Dictionary()
|
||||
|
||||
dict["behavior"] = behavior;
|
||||
dict["weight"] = weight;
|
||||
|
||||
_behaviors.append(dict)
|
||||
|
||||
# Returns the behavior at the specified `index`, or an empty `Dictionary` if
|
||||
# none was found.
|
||||
func get_behavior(index : int) -> Dictionary:
|
||||
if _behaviors.size() > index:
|
||||
return _behaviors[index]
|
||||
|
||||
printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size()))
|
||||
|
||||
return Dictionary()
|
||||
|
||||
func remove_behavior(index : int) -> void:
|
||||
if _behaviors.size() > index:
|
||||
_behaviors.remove(index)
|
||||
|
||||
return
|
||||
|
||||
printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size()))
|
||||
|
||||
return
|
||||
|
||||
func get_behaviour_count() -> int:
|
||||
return _behaviors.size()
|
||||
|
||||
func get_accel() -> GSAITargetAcceleration:
|
||||
return _accel
|
||||
|
||||
func _calculate_steering(blended_accel: GSAITargetAcceleration) -> void:
|
||||
blended_accel.set_zero()
|
||||
|
||||
for i in range(_behaviors.size()):
|
||||
var bw : Dictionary = _behaviors[i]
|
||||
bw.behavior.calculate_steering(_accel)
|
||||
|
||||
blended_accel.add_scaled_accel(_accel, bw.weight)
|
||||
|
||||
blended_accel.linear = GSAIUtils.clampedv3(blended_accel.linear, agent.linear_acceleration_max)
|
||||
blended_accel.angular = clamp(blended_accel.angular, -agent.angular_acceleration_max, agent.angular_acceleration_max)
|
@ -0,0 +1,26 @@
|
||||
class_name GSAICohesion
|
||||
extends GSAIGroupBehavior
|
||||
|
||||
# Calculates an acceleration that attempts to move the agent towards the center
|
||||
# of mass of the agents in the area defined by the `GSAIProximity`.
|
||||
# @category - Group behaviors
|
||||
|
||||
var _center_of_mass: Vector3
|
||||
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
acceleration.set_zero()
|
||||
_center_of_mass = Vector3.ZERO
|
||||
|
||||
var neighbor_count = proximity.find_neighbors(_callback)
|
||||
if neighbor_count > 0:
|
||||
_center_of_mass *= 1.0 / neighbor_count
|
||||
acceleration.linear = ((_center_of_mass - agent.position).normalized() * agent.linear_acceleration_max)
|
||||
|
||||
|
||||
# Callback for the proximity to call when finding neighbors. Adds `neighbor`'s position
|
||||
# to the center of mass of the group.
|
||||
# @tags - virtual
|
||||
func _report_neighbor(neighbor: GSAISteeringAgent) -> bool:
|
||||
_center_of_mass += neighbor.position
|
||||
return true
|
@ -0,0 +1,9 @@
|
||||
class_name GSAIEvade
|
||||
extends GSAIPursue
|
||||
|
||||
# Calculates acceleration to take an agent away from where a target agent is
|
||||
# moving.
|
||||
# @category - Individual behaviors
|
||||
|
||||
func _get_modified_acceleration() -> float:
|
||||
return -agent.linear_acceleration_max
|
@ -0,0 +1,28 @@
|
||||
class_name GSAIFace
|
||||
extends GSAIMatchOrientation
|
||||
|
||||
# Calculates angular acceleration to rotate a target to face its target's
|
||||
# position. The behavior attemps to arrive with zero remaining angular velocity.
|
||||
# @category - Individual behaviors
|
||||
|
||||
func face(acceleration: GSAITargetAcceleration, target_position: Vector3) -> void:
|
||||
call("_face", acceleration, target_position)
|
||||
|
||||
func _face(acceleration: GSAITargetAcceleration, target_position: Vector3) -> void:
|
||||
var to_target : Vector3 = target_position - agent.position
|
||||
var distance_squared : float = to_target.length_squared()
|
||||
|
||||
if distance_squared < agent.zero_linear_speed_threshold:
|
||||
acceleration.set_zero()
|
||||
else:
|
||||
var orientation : float
|
||||
|
||||
if use_z:
|
||||
orientation = GSAIUtils.vector3_to_angle(to_target)
|
||||
else:
|
||||
orientation = GSAIUtils.vector2_to_angle(GSAIUtils.to_vector2(to_target))
|
||||
|
||||
match_orientation(acceleration, orientation)
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
face(acceleration, target.position)
|
@ -0,0 +1,9 @@
|
||||
class_name GSAIFlee
|
||||
extends GSAISeek
|
||||
|
||||
# Calculates acceleration to take an agent directly away from a target agent.
|
||||
# @category - Individual behaviors
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
acceleration.linear = ((agent.position - target.position).normalized() * agent.linear_acceleration_max)
|
||||
acceleration.angular = 0
|
@ -0,0 +1,48 @@
|
||||
class_name GSAIFollowPath
|
||||
extends GSAIArrive
|
||||
|
||||
# Produces a linear acceleration that moves the agent along the specified path.
|
||||
# @category - Individual behaviors
|
||||
|
||||
# The path to follow and travel along.
|
||||
var path : GSAIPath
|
||||
# The distance along the path to generate the next target position.
|
||||
var path_offset : float = 0.0
|
||||
|
||||
# Whether to use `GSAIArrive` behavior on an open path.
|
||||
var is_arrive_enabled : bool = true
|
||||
# The amount of time in the future to predict the owning agent's position along
|
||||
# the path. Setting it to 0.0 will force non-predictive path following.
|
||||
var prediction_time : float = 0.0
|
||||
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
var location : Vector3
|
||||
|
||||
if prediction_time == 0:
|
||||
location = agent.position
|
||||
else:
|
||||
location = agent.position + (agent.linear_velocity * prediction_time)
|
||||
|
||||
var distance : float = path.calculate_distance(location)
|
||||
var target_distance : float = distance + path_offset
|
||||
|
||||
if prediction_time > 0 and path.is_open:
|
||||
if target_distance < path.calculate_distance(agent.position):
|
||||
target_distance = path.length
|
||||
|
||||
var target_position : Vector3 = path.calculate_target_position(target_distance)
|
||||
|
||||
if is_arrive_enabled and path.is_open:
|
||||
if path_offset >= 0:
|
||||
if target_distance > path.length - deceleration_radius:
|
||||
arrive(acceleration, target_position)
|
||||
return
|
||||
else:
|
||||
if target_distance < deceleration_radius:
|
||||
arrive(acceleration, target_position)
|
||||
return
|
||||
|
||||
acceleration.linear = (target_position - agent.position).normalized()
|
||||
acceleration.linear *= agent.linear_acceleration_max
|
||||
acceleration.angular = 0
|
@ -0,0 +1,19 @@
|
||||
class_name GSAILookWhereYouGo
|
||||
extends GSAIMatchOrientation
|
||||
|
||||
# Calculates an angular acceleration to match an agent's orientation to its
|
||||
# direction of travel.
|
||||
# @category - Individual behaviors
|
||||
|
||||
func _calculate_steering(accel: GSAITargetAcceleration) -> void:
|
||||
if agent.linear_velocity.length_squared() < agent.zero_linear_speed_threshold:
|
||||
accel.set_zero()
|
||||
else:
|
||||
var orientation : float
|
||||
|
||||
if use_z:
|
||||
orientation = GSAIUtils.vector3_to_angle(agent.linear_velocity)
|
||||
else:
|
||||
orientation = GSAIUtils.vector2_to_angle(GSAIUtils.to_vector2(agent.linear_velocity))
|
||||
|
||||
match_orientation(accel, orientation)
|
@ -0,0 +1,50 @@
|
||||
class_name GSAIMatchOrientation
|
||||
extends GSAISteeringBehavior
|
||||
|
||||
# Calculates an angular acceleration to match an agent's orientation to that of
|
||||
# its target. Attempts to make the agent arrive with zero remaining angular
|
||||
# velocity.
|
||||
# @category - Individual behaviors
|
||||
|
||||
# The target orientation for the behavior to try and match rotations to.
|
||||
var target : GSAIAgentLocation
|
||||
# The amount of distance in radians for the behavior to consider itself close
|
||||
# enough to be matching the target agent's rotation.
|
||||
var alignment_tolerance : float = 0.0
|
||||
# The amount of distance in radians from the goal to start slowing down.
|
||||
var deceleration_radius : float = 0.0
|
||||
# The amount of time to reach the target velocity
|
||||
var time_to_reach : float = 0.1
|
||||
# Whether to use the X and Z components instead of X and Y components when
|
||||
# determining angles. X and Z should be used in 3D.
|
||||
var use_z : bool = false
|
||||
|
||||
func match_orientation(acceleration: GSAITargetAcceleration, desired_orientation: float) -> void:
|
||||
call("_match_orientation", acceleration, desired_orientation)
|
||||
|
||||
func _match_orientation(acceleration: GSAITargetAcceleration, desired_orientation: float) -> void:
|
||||
var rotation : float = wrapf(desired_orientation - agent.orientation, -PI, PI)
|
||||
|
||||
var rotation_size : float = abs(rotation)
|
||||
|
||||
if rotation_size <= alignment_tolerance:
|
||||
acceleration.set_zero()
|
||||
else:
|
||||
var desired_rotation : float = agent.angular_speed_max
|
||||
|
||||
if rotation_size <= deceleration_radius:
|
||||
desired_rotation *= rotation_size / deceleration_radius
|
||||
|
||||
desired_rotation *= rotation / rotation_size
|
||||
|
||||
acceleration.angular = ((desired_rotation - agent.angular_velocity) / time_to_reach)
|
||||
|
||||
var limited_acceleration : float = abs(acceleration.angular)
|
||||
if limited_acceleration > agent.angular_acceleration_max:
|
||||
acceleration.angular *= (agent.angular_acceleration_max / limited_acceleration)
|
||||
|
||||
acceleration.linear = Vector3.ZERO
|
||||
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
match_orientation(acceleration, target.orientation)
|
@ -0,0 +1,60 @@
|
||||
class_name GSAIPriority
|
||||
extends GSAISteeringBehavior
|
||||
|
||||
# Container for multiple behaviors that returns the result of the first child
|
||||
# behavior with non-zero acceleration.
|
||||
# @category - Combination behaviors
|
||||
|
||||
# If a behavior's acceleration is lower than this threshold, the container
|
||||
# considers it has an acceleration of zero.
|
||||
var zero_threshold : float = 0.0
|
||||
|
||||
# The index of the last behavior the container prioritized.
|
||||
var _last_selected_index : int = 0
|
||||
var _behaviors : Array = Array()
|
||||
|
||||
# Appends a steering behavior as a child of this container.
|
||||
func add_behavior(behavior: GSAISteeringBehavior) -> void:
|
||||
_behaviors.append(behavior)
|
||||
|
||||
# Returns the behavior at the position in the pool referred to by `index`, or
|
||||
# `null` if no behavior was found.
|
||||
func get_behavior(index : int) -> GSAISteeringBehavior:
|
||||
if _behaviors.size() > index:
|
||||
return _behaviors[index]
|
||||
|
||||
printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size()))
|
||||
|
||||
return null
|
||||
|
||||
func remove_behavior(index : int) -> void:
|
||||
if _behaviors.size() > index:
|
||||
_behaviors.remove(index)
|
||||
|
||||
return
|
||||
|
||||
printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size()))
|
||||
|
||||
return
|
||||
|
||||
func get_behaviour_count() -> int:
|
||||
return _behaviors.size()
|
||||
|
||||
|
||||
func _calculate_steering(accel : GSAITargetAcceleration) -> void:
|
||||
var threshold_squared : float = zero_threshold * zero_threshold
|
||||
|
||||
_last_selected_index = -1
|
||||
|
||||
var size : int = _behaviors.size()
|
||||
|
||||
if size > 0:
|
||||
for i in range(size):
|
||||
_last_selected_index = i
|
||||
var behavior: GSAISteeringBehavior = _behaviors[i]
|
||||
behavior.calculate_steering(accel)
|
||||
|
||||
if accel.get_magnitude_squared() > threshold_squared:
|
||||
break
|
||||
else:
|
||||
accel.set_zero()
|
@ -0,0 +1,35 @@
|
||||
class_name GSAIPursue
|
||||
extends GSAISteeringBehavior
|
||||
|
||||
# Calculates an acceleration to make an agent intercept another based on the
|
||||
# target agent's movement.
|
||||
# @category - Individual behaviors
|
||||
|
||||
# The target agent that the behavior is trying to intercept.
|
||||
var target : GSAISteeringAgent
|
||||
# The maximum amount of time in the future the behavior predicts the target's
|
||||
# location.
|
||||
var predict_time_max : float = 1.0
|
||||
|
||||
func _calculate_steering(acceleration : GSAITargetAcceleration) -> void:
|
||||
var target_position : Vector3 = target.position
|
||||
var distance_squared : float = (target_position - agent.position).length_squared()
|
||||
|
||||
var speed_squared : float = agent.linear_velocity.length_squared()
|
||||
var predict_time : float = predict_time_max
|
||||
|
||||
if speed_squared > 0:
|
||||
var predict_time_squared := distance_squared / speed_squared
|
||||
if predict_time_squared < predict_time_max * predict_time_max:
|
||||
predict_time = sqrt(predict_time_squared)
|
||||
|
||||
acceleration.linear = ((target_position + (target.linear_velocity * predict_time)) - agent.position).normalized()
|
||||
acceleration.linear *= get_modified_acceleration()
|
||||
|
||||
acceleration.angular = 0
|
||||
|
||||
func get_modified_acceleration() -> float:
|
||||
return call("_get_modified_acceleration")
|
||||
|
||||
func _get_modified_acceleration() -> float:
|
||||
return agent.linear_acceleration_max
|
@ -0,0 +1,14 @@
|
||||
class_name GSAISeek
|
||||
extends GSAISteeringBehavior
|
||||
|
||||
# Calculates an acceleration to take an agent to a target agent's position
|
||||
# directly.
|
||||
# @category - Individual behaviors
|
||||
|
||||
# The target that the behavior aims to move the agent to.
|
||||
var target : GSAIAgentLocation
|
||||
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
acceleration.linear = ((target.position - agent.position).normalized() * agent.linear_acceleration_max)
|
||||
acceleration.angular = 0
|
@ -0,0 +1,40 @@
|
||||
class_name GSAISeparation
|
||||
extends GSAIGroupBehavior
|
||||
|
||||
# Calculates an acceleration that repels the agent from its neighbors in the
|
||||
# given `GSAIProximity`.
|
||||
#
|
||||
# The acceleration is an average based on all neighbors, multiplied by a
|
||||
# strength decreasing by the inverse square law in relation to distance, and it
|
||||
# accumulates.
|
||||
# @category - Group behaviors
|
||||
|
||||
# The coefficient to calculate how fast the separation strength decays with distance.
|
||||
var decay_coefficient : float = 1.0
|
||||
|
||||
var acceleration : GSAITargetAcceleration
|
||||
|
||||
|
||||
func _calculate_steering(_acceleration : GSAITargetAcceleration) -> void:
|
||||
self.acceleration = _acceleration
|
||||
acceleration.set_zero()
|
||||
# warning-ignore:return_value_discarded
|
||||
proximity.find_neighbors(_callback)
|
||||
|
||||
|
||||
# Callback for the proximity to call when finding neighbors. Determines the amount of
|
||||
# acceleration that `neighbor` imposes based on its distance from the owner agent.
|
||||
# @tags - virtual
|
||||
func _report_neighbor(neighbor : GSAISteeringAgent) -> bool:
|
||||
var to_agent : Vector3 = agent.position - neighbor.position
|
||||
|
||||
var distance_squared : float = to_agent.length_squared()
|
||||
var acceleration_max : float = agent.linear_acceleration_max
|
||||
|
||||
var strength := decay_coefficient / distance_squared
|
||||
if strength > acceleration_max:
|
||||
strength = acceleration_max
|
||||
|
||||
acceleration.linear += to_agent * (strength / sqrt(distance_squared))
|
||||
|
||||
return true
|
@ -0,0 +1,10 @@
|
||||
extends Reference
|
||||
class_name GSAIAgentLocation
|
||||
|
||||
# Represents an agent with only a location and an orientation.
|
||||
# @category - Base types
|
||||
|
||||
# The agent's position in space.
|
||||
var position : Vector3 = Vector3.ZERO
|
||||
# The agent's orientation on its Y axis rotation.
|
||||
var orientation : float = 0.0
|
@ -0,0 +1,19 @@
|
||||
class_name GSAIGroupBehavior
|
||||
extends GSAISteeringBehavior
|
||||
|
||||
# Base type for group-based steering behaviors.
|
||||
# @category - Base types
|
||||
|
||||
# Container to find neighbors of the agent and calculate group behavior.
|
||||
var proximity : GSAIProximity
|
||||
|
||||
var _callback : FuncRef = funcref(self, "_report_neighbor")
|
||||
|
||||
func get_callback() -> FuncRef:
|
||||
return _callback
|
||||
|
||||
# Internal callback for the behavior to define whether or not a member is
|
||||
# relevant
|
||||
# @tags - virtual
|
||||
func _report_neighbor(_neighbor : GSAISteeringAgent) -> bool:
|
||||
return false
|
@ -0,0 +1,129 @@
|
||||
class_name GSAIPath
|
||||
extends Reference
|
||||
|
||||
# Represents a path made up of Vector3 waypoints, split into segments path
|
||||
# follow behaviors can use.
|
||||
# @category - Base types
|
||||
|
||||
# If `false`, the path loops.
|
||||
var is_open : bool
|
||||
# Total length of the path.
|
||||
var length : float
|
||||
|
||||
var _segments : Array
|
||||
|
||||
var _nearest_point_on_segment : Vector3
|
||||
var _nearest_point_on_path : Vector3
|
||||
|
||||
|
||||
func initialize(waypoints : Array, _is_open : bool = false) -> void:
|
||||
self.is_open = _is_open
|
||||
create_path(waypoints)
|
||||
_nearest_point_on_segment = waypoints[0]
|
||||
_nearest_point_on_path = waypoints[0]
|
||||
|
||||
|
||||
# Creates a path from a list of waypoints.
|
||||
func create_path(waypoints : Array) -> void:
|
||||
if not waypoints or waypoints.size() < 2:
|
||||
printerr("Waypoints cannot be null and must contain at least two (2) waypoints.")
|
||||
return
|
||||
|
||||
_segments = []
|
||||
length = 0
|
||||
var current : Vector3 = waypoints.front()
|
||||
var previous : Vector3
|
||||
|
||||
for i in range(1, waypoints.size(), 1):
|
||||
previous = current
|
||||
if i < waypoints.size():
|
||||
current = waypoints[i]
|
||||
elif is_open:
|
||||
break
|
||||
else:
|
||||
current = waypoints[0]
|
||||
|
||||
var segment : GSAISegment = GSAISegment.new(previous, current)
|
||||
length += segment.length
|
||||
segment.cumulative_length = length
|
||||
_segments.append(segment)
|
||||
|
||||
|
||||
# Returns the distance from `agent_current_position` to the next waypoint.
|
||||
func calculate_distance(agent_current_position : Vector3) -> float:
|
||||
if _segments.size() == 0:
|
||||
return 0.0
|
||||
|
||||
var smallest_distance_squared : float = INF
|
||||
var nearest_segment : GSAISegment = null
|
||||
|
||||
for i in range(_segments.size()):
|
||||
var segment: GSAISegment = _segments[i]
|
||||
var distance_squared : float = _calculate_point_segment_distance_squared(segment.begin, segment.end, agent_current_position)
|
||||
|
||||
if distance_squared < smallest_distance_squared:
|
||||
_nearest_point_on_path = _nearest_point_on_segment
|
||||
smallest_distance_squared = distance_squared
|
||||
nearest_segment = segment
|
||||
|
||||
var length_on_path : float = nearest_segment.cumulative_length - _nearest_point_on_path.distance_to(nearest_segment.end)
|
||||
|
||||
return length_on_path
|
||||
|
||||
|
||||
# Calculates a target position from the path's starting point based on the `target_distance`.
|
||||
func calculate_target_position(target_distance: float) -> Vector3:
|
||||
if is_open:
|
||||
target_distance = clamp(target_distance, 0, length)
|
||||
else:
|
||||
if target_distance < 0:
|
||||
target_distance = length + fmod(target_distance, length)
|
||||
elif target_distance > length:
|
||||
target_distance = fmod(target_distance, length)
|
||||
|
||||
var desired_segment: GSAISegment
|
||||
for i in range(_segments.size()):
|
||||
var segment: GSAISegment = _segments[i]
|
||||
if segment.cumulative_length >= target_distance:
|
||||
desired_segment = segment
|
||||
break
|
||||
|
||||
if not desired_segment:
|
||||
desired_segment = _segments.back()
|
||||
|
||||
var distance := desired_segment.cumulative_length - target_distance
|
||||
|
||||
return (((desired_segment.begin - desired_segment.end) * (distance / desired_segment.length)) + desired_segment.end)
|
||||
|
||||
|
||||
# Returns the position of the first point on the path.
|
||||
func get_start_point() -> Vector3:
|
||||
return _segments.front().begin
|
||||
|
||||
|
||||
# Returns the position of the last point on the path.
|
||||
func get_end_point() -> Vector3:
|
||||
return _segments.back().end
|
||||
|
||||
|
||||
func _calculate_point_segment_distance_squared(start: Vector3, end: Vector3, position: Vector3) -> float:
|
||||
_nearest_point_on_segment = start
|
||||
var start_end : Vector3 = end - start
|
||||
var start_end_length_squared : float = start_end.length_squared()
|
||||
if start_end_length_squared != 0:
|
||||
var t = (position - start).dot(start_end) / start_end_length_squared
|
||||
_nearest_point_on_segment += start_end * clamp(t, 0, 1)
|
||||
|
||||
return _nearest_point_on_segment.distance_squared_to(position)
|
||||
|
||||
# not exposed helper struct
|
||||
class GSAISegment:
|
||||
var begin: Vector3
|
||||
var end: Vector3
|
||||
var length: float
|
||||
var cumulative_length: float
|
||||
|
||||
func _init(_begin: Vector3, _end: Vector3) -> void:
|
||||
self.begin = _begin
|
||||
self.end = _end
|
||||
length = _begin.distance_to(_end)
|
@ -0,0 +1,29 @@
|
||||
extends GSAIAgentLocation
|
||||
class_name GSAISteeringAgent
|
||||
|
||||
# Adds velocity, speed, and size data to `GSAIAgentLocation`.
|
||||
#
|
||||
# It is the character's responsibility to keep this information up to date for
|
||||
# the steering toolkit to work correctly.
|
||||
# @category - Base types
|
||||
|
||||
# The amount of velocity to be considered effectively not moving.
|
||||
var zero_linear_speed_threshold : float = 0.01
|
||||
# The maximum speed at which the agent can move.
|
||||
var linear_speed_max : float = 0.0
|
||||
# The maximum amount of acceleration that any behavior can apply to the agent.
|
||||
var linear_acceleration_max : float = 0.0
|
||||
# The maximum amount of angular speed at which the agent can rotate.
|
||||
var angular_speed_max : float = 0.0
|
||||
# The maximum amount of angular acceleration that any behavior can apply to an
|
||||
# agent.
|
||||
var angular_acceleration_max : float = 0.0
|
||||
# Current velocity of the agent.
|
||||
var linear_velocity : Vector3 = Vector3.ZERO
|
||||
# Current angular velocity of the agent.
|
||||
var angular_velocity : float = 0.0
|
||||
# The radius of the sphere that approximates the agent's size in space.
|
||||
var bounding_radius : float = 0.0
|
||||
# Used internally by group behaviors and proximities to mark the agent as already
|
||||
# considered.
|
||||
var is_tagged : bool = false
|
@ -0,0 +1,26 @@
|
||||
extends Reference
|
||||
class_name GSAISteeringBehavior
|
||||
|
||||
# Base class for all steering behaviors.
|
||||
#
|
||||
# Steering behaviors calculate the linear and the angular acceleration to be
|
||||
# to the agent that owns them.
|
||||
#
|
||||
# The `calculate_steering` function is the entry point for all behaviors.
|
||||
# Individual steering behaviors encapsulate the steering logic.
|
||||
# @category - Base types
|
||||
|
||||
# If `false`, all calculations return zero amounts of acceleration.
|
||||
var is_enabled : bool = true
|
||||
# The AI agent on which the steering behavior bases its calculations.
|
||||
var agent : GSAISteeringAgent
|
||||
|
||||
# Sets the `acceleration` with the behavior's desired amount of acceleration.
|
||||
func calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
if is_enabled:
|
||||
call("_calculate_steering", acceleration)
|
||||
else:
|
||||
acceleration.set_zero()
|
||||
|
||||
func _calculate_steering(acceleration: GSAITargetAcceleration) -> void:
|
||||
acceleration.set_zero()
|
@ -0,0 +1,31 @@
|
||||
extends Reference
|
||||
class_name GSAITargetAcceleration
|
||||
|
||||
# A desired linear and angular amount of acceleration requested by the steering
|
||||
# system.
|
||||
# @category - Base types
|
||||
|
||||
# Linear acceleration
|
||||
var linear : Vector3 = Vector3.ZERO
|
||||
# Angular acceleration
|
||||
var angular : float = 0.0
|
||||
|
||||
# Sets the linear and angular components to 0.
|
||||
func set_zero() -> void:
|
||||
linear.x = 0.0
|
||||
linear.y = 0.0
|
||||
linear.z = 0.0
|
||||
angular = 0.0
|
||||
|
||||
# Adds `accel`'s components, multiplied by `scalar`, to this one.
|
||||
func add_scaled_accel(accel: GSAITargetAcceleration, scalar: float) -> void:
|
||||
linear += accel.linear * scalar
|
||||
angular += accel.angular * scalar
|
||||
|
||||
# Returns the squared magnitude of the linear and angular components.
|
||||
func get_magnitude_squared() -> float:
|
||||
return linear.length_squared() + angular * angular
|
||||
|
||||
# Returns the magnitude of the linear and angular components.
|
||||
func get_magnitude() -> float:
|
||||
return sqrt(get_magnitude_squared())
|
@ -0,0 +1,40 @@
|
||||
class_name GSAIUtils
|
||||
|
||||
# Math and vector utility functions.
|
||||
# @Category - Utilities
|
||||
|
||||
# Returns the `vector` with its length capped to `limit`.
|
||||
static func clampedv3(vector: Vector3, limit: float) -> Vector3:
|
||||
var length_squared : float = vector.length_squared()
|
||||
var limit_squared : float = limit * limit
|
||||
|
||||
if length_squared > limit_squared:
|
||||
vector *= sqrt(limit_squared / length_squared)
|
||||
|
||||
return vector
|
||||
|
||||
# Returns an angle in radians between the positive X axis and the `vector`.
|
||||
#
|
||||
# This assumes orientation for 3D agents that are upright and rotate
|
||||
# around the Y axis.
|
||||
static func vector3_to_angle(vector: Vector3) -> float:
|
||||
return atan2(vector.x, vector.z)
|
||||
|
||||
# Returns an angle in radians between the positive X axis and the `vector`.
|
||||
static func vector2_to_angle(vector: Vector2) -> float:
|
||||
return atan2(vector.x, -vector.y)
|
||||
|
||||
# Returns a directional vector from the given orientation angle.
|
||||
#
|
||||
# This assumes orientation for 2D agents or 3D agents that are upright and
|
||||
# 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)
|
@ -0,0 +1,23 @@
|
||||
extends GSAIProximity
|
||||
class_name GSAIInfiniteProximity
|
||||
|
||||
# Determines any agent that is in the specified list as being neighbors with the
|
||||
# owner agent, regardless of distance.
|
||||
# @category - Proximities
|
||||
|
||||
# Returns a number of neighbors based on a `callback` function.
|
||||
#
|
||||
# `_find_neighbors` calls `callback` for each agent in the `agents` array and
|
||||
# adds one to the count if its `callback` returns true.
|
||||
# @tags - virtual
|
||||
func _find_neighbors(callback: FuncRef) -> int:
|
||||
var neighbor_count : int = 0
|
||||
var agent_count : int = agents.size()
|
||||
for i in range(agent_count):
|
||||
var current_agent : GSAISteeringAgent = agents[i] as GSAISteeringAgent
|
||||
|
||||
if current_agent != agent:
|
||||
if callback.call_func(current_agent):
|
||||
neighbor_count += 1
|
||||
|
||||
return neighbor_count
|
@ -0,0 +1,22 @@
|
||||
extends Reference
|
||||
class_name GSAIProximity
|
||||
|
||||
# Base container type that stores data to find the neighbors of an agent.
|
||||
# @category - Proximities
|
||||
# @tags - abstract
|
||||
|
||||
# The owning agent whose neighbors are found in the group
|
||||
var agent : GSAISteeringAgent
|
||||
# The agents who are part of this group and could be potential neighbors
|
||||
var agents : Array = Array()
|
||||
|
||||
func find_neighbors(_callback: FuncRef) -> int:
|
||||
return call("_find_neighbors", _callback)
|
||||
|
||||
# Returns a number of neighbors based on a `callback` function.
|
||||
#
|
||||
# `_find_neighbors` calls `callback` for each agent in the `agents` array and
|
||||
# adds one to the count if its `callback` returns true.
|
||||
# @tags - virtual
|
||||
func _find_neighbors(_callback: FuncRef) -> int:
|
||||
return 0
|
@ -0,0 +1,62 @@
|
||||
extends GSAIProximity
|
||||
class_name GSAIRadiusProximity
|
||||
|
||||
# Determines any agent that is in the specified list as being neighbors with the owner agent if
|
||||
# they lie within the specified radius.
|
||||
# @category - Proximities
|
||||
|
||||
# The radius around the owning agent to find neighbors in
|
||||
var radius : float = 0.0
|
||||
|
||||
var _last_frame : int = 0
|
||||
var _scene_tree : SceneTree
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
_scene_tree = Engine.get_main_loop()
|
||||
|
||||
# Returns a number of neighbors based on a `callback` function.
|
||||
#
|
||||
# `_find_neighbors` calls `callback` for each agent in the `agents` array that lie within
|
||||
# the radius around the owning agent and adds one to the count if its `callback` returns true.
|
||||
# @tags - virtual
|
||||
func _find_neighbors(callback : FuncRef) -> int:
|
||||
var agent_count : int = agents.size()
|
||||
var neighbor_count : int = 0
|
||||
|
||||
var current_frame : int
|
||||
|
||||
if _scene_tree:
|
||||
current_frame = _scene_tree.get_frame()
|
||||
else:
|
||||
current_frame = -_last_frame
|
||||
|
||||
if current_frame != _last_frame:
|
||||
_last_frame = current_frame
|
||||
|
||||
var owner_position : Vector3 = agent.position
|
||||
|
||||
for i in range(agent_count):
|
||||
var current_agent : GSAISteeringAgent = agents[i] as GSAISteeringAgent
|
||||
|
||||
if current_agent != agent:
|
||||
var distance_squared : float = owner_position.distance_squared_to(current_agent.position)
|
||||
|
||||
var range_to : float = radius + current_agent.bounding_radius
|
||||
|
||||
if distance_squared < range_to * range_to:
|
||||
if callback.call_func(current_agent):
|
||||
current_agent.is_tagged = true
|
||||
neighbor_count += 1
|
||||
continue
|
||||
|
||||
current_agent.is_tagged = false
|
||||
else:
|
||||
for i in range(agent_count):
|
||||
var current_agent : GSAISteeringAgent = agents[i] as GSAISteeringAgent
|
||||
|
||||
if current_agent != agent and current_agent.is_tagged:
|
||||
if callback.call_func(current_agent):
|
||||
neighbor_count += 1
|
||||
|
||||
return neighbor_count
|
BIN
project_gdscript/assets/icon.png
Normal file
BIN
project_gdscript/assets/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
35
project_gdscript/assets/icon.png.import
Normal file
35
project_gdscript/assets/icon.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/icon.png"
|
||||
dest_files=[ "res://.import/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
151
project_gdscript/assets/icon.svg
Normal file
151
project_gdscript/assets/icon.svg
Normal file
@ -0,0 +1,151 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
sodipodi:docname="icon.svg"
|
||||
inkscape:version="1.1-dev (83f2ee46ff, 2020-02-13)"
|
||||
id="svg1340"
|
||||
version="1.1"
|
||||
viewBox="0 0 16.933333 16.933334"
|
||||
height="64"
|
||||
width="64">
|
||||
<defs
|
||||
id="defs1334">
|
||||
<clipPath
|
||||
id="clipPath1560"
|
||||
clipPathUnits="userSpaceOnUse">
|
||||
<rect
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#293252;fill-opacity:1;stroke:none;stroke-width:1.40425;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;stop-color:#000000;stop-opacity:1"
|
||||
id="rect1562"
|
||||
width="16.933332"
|
||||
height="16.933332"
|
||||
x="0"
|
||||
y="9.6346028e-08" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-others="false"
|
||||
inkscape:object-nodes="false"
|
||||
inkscape:snap-page="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-height="1043"
|
||||
inkscape:window-width="1920"
|
||||
units="px"
|
||||
showgrid="false"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:cy="31.438019"
|
||||
inkscape:cx="15.072392"
|
||||
inkscape:zoom="8"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1.0"
|
||||
bordercolor="#666666"
|
||||
pagecolor="#ffffff"
|
||||
id="base" />
|
||||
<metadata
|
||||
id="metadata1337">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Layer 1">
|
||||
<rect
|
||||
y="0"
|
||||
x="0"
|
||||
height="16.933332"
|
||||
width="16.933332"
|
||||
id="rect1504"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#293252;fill-opacity:1;stroke:none;stroke-width:1.40425;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0;stop-color:#000000;stop-opacity:1" />
|
||||
<path
|
||||
clip-path="url(#clipPath1560)"
|
||||
id="path2490"
|
||||
d="M 2.8526138,-0.0226856 2.9507848,1.274306 C 2.5771927,1.4791664 2.2289224,1.7185349 1.9075218,1.988743 L 0.29690799,1.3221671 -0.40866643,2.1624613 0.56110833,3.5585158 C 0.33791661,3.9257554 0.15316113,4.3166174 0.00975169,4.7250204 l -0.7432386,0.090259 -0.009472,1.7228785 0.40521717,0.191826 c 0,0.2151562 0.0190602,0.4320655 0.0310875,0.6505844 0.0205584,0.2185642 0.0528327,0.4331209 0.0974454,0.6435666 l -0.54416748,0.401985 -0.008756,1.5907221 1.2930922,-0.090536 C 0.75306491,10.299542 1.0115466,10.64731 1.3029499,10.96517 l -0.672761,1.553839 0.858959,0.689584 1.4456413,-0.959917 c 0.3687832,0.203771 0.7600417,0.372343 1.1673979,0.502553 l 0.2153945,1.714295 1.0964166,0.176342 0.7818497,-1.5822 c 0.2037711,0 0.4094501,-0.01685 0.6159876,-0.03895 H 6.812698 L 9.8042599,12.732617 9.3779282,8.4823903 6.3854715,8.7696026 6.5157868,10.092058 C 4.6330774,10.272636 2.9585336,8.9258916 2.7734622,7.0836627 2.5888891,5.2418454 3.9644158,3.6030022 5.8465387,3.4220125 L 8.8381053,3.1348571 8.5438181,0.20619376 5.5531989,0.49334922 C 5.3459823,0.51050176 5.1420605,0.5378074 4.9415586,0.57229759 L 4.4901554,-0.01349219 Z M 10.360111,2.7244603 9.577372,4.3066562 c -0.2035882,0 -0.4084223,0.016659 -0.6150947,0.038946 L 5.969764,4.633705 6.2641051,7.5605285 9.2556723,7.273373 a 3.4244787,3.3513826 0 0 1 3.7431617,3.007448 3.4244787,3.3513826 0 0 1 -3.0739683,3.662595 l -2.9915662,0.28894 0.2652052,2.646411 2.7965066,0.01566 0.2241417,-0.0215 c 0.206675,-0.01714 0.41087,-0.0444 0.610692,-0.07889 l 0.0814,0.105523 1.973596,0.01109 -0.06151,-0.818622 c 0.373232,-0.204827 0.723028,-0.444409 1.044158,-0.714444 l 1.609666,0.667466 0.705575,-0.840294 -0.968773,-1.396053 c 0.222649,-0.367242 0.407573,-0.758822 0.551301,-1.167399 l 1.699205,-0.205645 0.181684,-1.073405 -1.533445,-0.725078 c 0.01889,-0.214756 -0.01899,-0.432247 -0.03098,-0.6505829 -0.02061,-0.2185654 -0.05301,-0.4339146 -0.09935,-0.6444001 L 17.342103,8.3379231 16.950314,7.3203422 15.242313,7.4418002 C 15.020345,7.0685674 14.760954,6.7200461 14.469323,6.4020456 L 15.142137,4.8490976 14.284072,4.1595128 12.836646,5.1194284 C 12.467685,4.9156577 12.078251,4.7477386 11.671033,4.6177136 L 11.454747,2.9034731 Z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#2f395f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.17682;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
style="stroke-width:1.24672"
|
||||
transform="matrix(0.18285727,0,0,0.14154908,150.31973,-187.36868)"
|
||||
id="g2450">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;vector-effect:none;fill:#5e315b;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
d="m -822.06042,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
id="path2430" />
|
||||
<path
|
||||
id="path2432"
|
||||
d="m -812.80001,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
style="opacity:1;vector-effect:none;fill:#8c3f5d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;vector-effect:none;fill:#ba6156;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
d="m -803.5396,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
id="path2434" />
|
||||
<path
|
||||
id="path2436"
|
||||
d="m -794.27919,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
style="opacity:1;vector-effect:none;fill:#f2a65e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;vector-effect:none;fill:#ffe478;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
d="m -785.01878,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
id="path2438" />
|
||||
<path
|
||||
id="path2440"
|
||||
d="m -775.75837,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
style="opacity:1;vector-effect:none;fill:#cfff70;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;vector-effect:none;fill:#8fde5d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
d="m -766.49796,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
id="path2442" />
|
||||
<path
|
||||
id="path2444"
|
||||
d="m -757.23755,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
style="opacity:1;vector-effect:none;fill:#3ca370;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="opacity:1;vector-effect:none;fill:#3d6e70;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
d="m -747.97714,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
id="path2446" />
|
||||
<path
|
||||
id="path2448"
|
||||
d="m -738.71673,1434.5708 h 9.26041 v 9.2604 h -9.26041 z"
|
||||
style="opacity:1;vector-effect:none;fill:#323e4f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.989584;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.97917, 1.97917;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="cccc"
|
||||
d="M 1.7415414,13.864431 C 2.8091455,12.301646 3.8954985,10.918152 4.8840406,10.004274 M 12.44693,3.6341761 c 0,0 1.658558,-1.2568206 2.909891,-2.065662"
|
||||
style="fill:none;stroke:#a0a9ce;stroke-width:1.087;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:1.6;stroke-dasharray:2.174, 2.174;stroke-dashoffset:1.087;stroke-opacity:1"
|
||||
id="path2283"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path2351"
|
||||
d="M 4.6789148,3.8638581 6.5936823,6.200326 4.9999674,7.512026 7.0448877,10.007103 8.6388709,8.6952013 10.553866,11.032019 11.16251,4.529541 Z"
|
||||
style="vector-effect:none;fill:#293252;fill-opacity:1;fill-rule:nonzero;stroke:#7786bb;stroke-width:0.827338;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:normal" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
35
project_gdscript/assets/icon.svg.import
Normal file
35
project_gdscript/assets/icon.svg.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/icon.svg"
|
||||
dest_files=[ "res://.import/icon.svg-56083ea2a1f1a4f1e49773bdc6d7826c.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
project_gdscript/assets/sprites/background.png
Normal file
BIN
project_gdscript/assets/sprites/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
35
project_gdscript/assets/sprites/background.png.import
Normal file
35
project_gdscript/assets/sprites/background.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/background.png-dde469fb1f19281f3784b52d4bea96cd.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/sprites/background.png"
|
||||
dest_files=[ "res://.import/background.png-dde469fb1f19281f3784b52d4bea96cd.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=true
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=true
|
||||
svg/scale=1.0
|
BIN
project_gdscript/assets/theme/button/disabled.stylebox
Normal file
BIN
project_gdscript/assets/theme/button/disabled.stylebox
Normal file
Binary file not shown.
BIN
project_gdscript/assets/theme/button/focused.stylebox
Normal file
BIN
project_gdscript/assets/theme/button/focused.stylebox
Normal file
Binary file not shown.
BIN
project_gdscript/assets/theme/button/hover.stylebox
Normal file
BIN
project_gdscript/assets/theme/button/hover.stylebox
Normal file
Binary file not shown.
BIN
project_gdscript/assets/theme/button/normal.stylebox
Normal file
BIN
project_gdscript/assets/theme/button/normal.stylebox
Normal file
Binary file not shown.
BIN
project_gdscript/assets/theme/button/pressed.stylebox
Normal file
BIN
project_gdscript/assets/theme/button/pressed.stylebox
Normal file
Binary file not shown.
BIN
project_gdscript/assets/theme/empty.stylebox
Normal file
BIN
project_gdscript/assets/theme/empty.stylebox
Normal file
Binary file not shown.
8
project_gdscript/assets/theme/fonts/default_font.tres
Normal file
8
project_gdscript/assets/theme/fonts/default_font.tres
Normal file
@ -0,0 +1,8 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://assets/theme/fonts/montserrat/Montserrat-Medium.ttf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
size = 26
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
@ -0,0 +1,8 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://assets/theme/fonts/montserrat/Montserrat-Bold.ttf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
size = 26
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
@ -0,0 +1,8 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://assets/theme/fonts/source_code_pro/SourceCodePro-Medium.otf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
size = 26
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
8
project_gdscript/assets/theme/fonts/font_title.tres
Normal file
8
project_gdscript/assets/theme/fonts/font_title.tres
Normal file
@ -0,0 +1,8 @@
|
||||
[gd_resource type="DynamicFont" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://assets/theme/fonts/montserrat/Montserrat-Black.ttf" type="DynamicFontData" id=1]
|
||||
|
||||
[resource]
|
||||
size = 34
|
||||
use_filter = true
|
||||
font_data = ExtResource( 1 )
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user