Skip to content

Commit

Permalink
Bot ai scene and navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
nezvers committed Sep 14, 2024
1 parent 3ac12a8 commit 22beefe
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 39 deletions.
4 changes: 4 additions & 0 deletions project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ dash={
2d_physics/layer_2="Player"
2d_physics/layer_3="Enemy"

[navigation]

2d/default_link_connection_radius=16.0

[rendering]

textures/canvas_textures/default_texture_filter=0
Expand Down
38 changes: 19 additions & 19 deletions resources/tilesets/dungeon_tileset.tres
Original file line number Diff line number Diff line change
Expand Up @@ -80,28 +80,28 @@ polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3)])
outlines = Array[PackedVector2Array]([PackedVector2Array(-8, 0.264865, 8, 0.353154, 8, -8, -8, -8)])
agent_radius = 0.0

[sub_resource type="NavigationPolygon" id="NavigationPolygon_y16hi"]
vertices = PackedVector2Array(-0.351563, -0.53125, -0.445313, -8, 8, -8, 8, 8, -8, 8, -8, -0.351563)
[sub_resource type="NavigationPolygon" id="NavigationPolygon_iryr3"]
vertices = PackedVector2Array(5.64844, 5.38281, 5.5625, -8, 8, -8, 8, 8, -8, 8, -8, 5.47656)
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(0, 3, 4, 5)])
outlines = Array[PackedVector2Array]([PackedVector2Array(-0.441443, -8, -0.353154, -0.529732, -8, -0.353155, -8, 8, 8, 8, 8, -8)])
outlines = Array[PackedVector2Array]([PackedVector2Array(5.56218, -8, 5.65047, 5.3856, -8, 5.47389, -8, 8, 8, 8, 8, -8)])
agent_radius = 0.0

[sub_resource type="NavigationPolygon" id="NavigationPolygon_26jlb"]
vertices = PackedVector2Array(8, 8, -0.265625, 8, -0.179688, 0.0859375, 8, -8, -8, -8, -8, -0.179688)
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 3, 2, 5)])
outlines = Array[PackedVector2Array]([PackedVector2Array(-8, -0.176578, -0.176578, 0.0882883, -0.264866, 8, 8, 8, 8, -8, -8, -8)])
[sub_resource type="NavigationPolygon" id="NavigationPolygon_wewyq"]
vertices = PackedVector2Array(8, -8, 8, 8, 4.59375, 8, 4.5, -4.67969, -8, -8, -8, -5.29688)
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 0, 3, 5)])
outlines = Array[PackedVector2Array]([PackedVector2Array(-8, -5.29732, 4.50272, -4.6793, 4.59101, 8, 8, 8, 8, -8, -8, -8)])
agent_radius = 0.0

[sub_resource type="NavigationPolygon" id="NavigationPolygon_g65kr"]
vertices = PackedVector2Array(-8, -8, 0, -8, -0.0859375, -0.265625, -8, 8, 8, 8, 8, -0.179688)
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 3, 2, 5)])
outlines = Array[PackedVector2Array]([PackedVector2Array(0, -8, -0.0882883, -0.264866, 8, -0.176578, 8, 8, -8, 8, -8, -8)])
[sub_resource type="NavigationPolygon" id="NavigationPolygon_4bfpl"]
vertices = PackedVector2Array(-4.76563, 5.03125, 8, 4.94531, 8, 8, -8, 8, -8, -8, -4.67969, -8)
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(0, 3, 4, 5)])
outlines = Array[PackedVector2Array]([PackedVector2Array(-4.6793, -8, -4.76758, 5.03245, 8, 4.94416, 8, 8, -8, 8, -8, -8)])
agent_radius = 0.0

[sub_resource type="NavigationPolygon" id="NavigationPolygon_dnnbw"]
vertices = PackedVector2Array(8, -8, 8, 0, -0.0859375, 0, -8, -8, -8, 8, -0.179688, 8)
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 3, 2, 5)])
outlines = Array[PackedVector2Array]([PackedVector2Array(8, 0, -0.0882883, 0, -0.176578, 8, -8, 8, -8, -8, 8, -8)])
[sub_resource type="NavigationPolygon" id="NavigationPolygon_ddcnl"]
vertices = PackedVector2Array(-4.59375, -4.67969, -4.59375, 8, -8, 8, -8, -8, 8, -8, 8, -5.03125)
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(0, 3, 4, 5)])
outlines = Array[PackedVector2Array]([PackedVector2Array(8, -5.03245, -4.59101, -4.6793, -4.59101, 8, -8, 8, -8, -8, 8, -8)])
agent_radius = 0.0

[sub_resource type="NavigationPolygon" id="NavigationPolygon_gshl1"]
Expand Down Expand Up @@ -239,7 +239,7 @@ texture = ExtResource("1_0i6id")
5:1/0/terrains_peering_bit/left_side = 0
5:1/0/terrains_peering_bit/top_side = 0
5:1/0/terrains_peering_bit/top_right_corner = 0
5:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_y16hi")
5:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_iryr3")
6:1/0 = 0
6:1/0/terrain_set = 0
6:1/0/terrain = 0
Expand All @@ -251,7 +251,7 @@ texture = ExtResource("1_0i6id")
6:1/0/terrains_peering_bit/left_side = 0
6:1/0/terrains_peering_bit/top_left_corner = 0
6:1/0/terrains_peering_bit/top_side = 0
6:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_g65kr")
6:1/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_4bfpl")
7:1/0 = 0
7:1/0/terrain_set = 0
7:1/0/terrain = 0
Expand Down Expand Up @@ -315,7 +315,7 @@ texture = ExtResource("1_0i6id")
5:2/0/terrains_peering_bit/top_left_corner = 0
5:2/0/terrains_peering_bit/top_side = 0
5:2/0/terrains_peering_bit/top_right_corner = 0
5:2/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_26jlb")
5:2/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_wewyq")
6:2/0 = 0
6:2/0/terrain_set = 0
6:2/0/terrain = 0
Expand All @@ -327,7 +327,7 @@ texture = ExtResource("1_0i6id")
6:2/0/terrains_peering_bit/top_left_corner = 0
6:2/0/terrains_peering_bit/top_side = 0
6:2/0/terrains_peering_bit/top_right_corner = 0
6:2/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_dnnbw")
6:2/0/navigation_layer_0/polygon = SubResource("NavigationPolygon_ddcnl")
0:3/0 = 0
0:3/0/terrain_set = 0
0:3/0/terrain = 0
Expand Down
14 changes: 11 additions & 3 deletions scenes/actors/ai/enemy_ai.tscn
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
[gd_scene load_steps=7 format=3 uid="uid://cenq1bawfywu8"]
[gd_scene load_steps=8 format=3 uid="uid://cenq1bawfywu8"]

[ext_resource type="Script" path="res://scripts/actor/bots/BotInput.gd" id="1_qo7hy"]
[ext_resource type="Script" path="res://scripts/actor/bots/NavigationVisualizer2D.gd" id="2_2yt0t"]
[ext_resource type="Script" path="res://scripts/actor/bots/ProximityAttack.gd" id="3_4n00q"]
[ext_resource type="Script" path="res://scripts/actor/bots/TargetFinder.gd" id="4_y3qb6"]
[ext_resource type="Script" path="res://scripts/actor/bots/TargetDirection.gd" id="5_ekfo4"]
[ext_resource type="Script" path="res://scripts/actor/bots/TargetAim.gd" id="6_xkm0h"]

[sub_resource type="RectangleShape2D" id="RectangleShape2D_ixqjj"]
size = Vector2(1000, 500)

[node name="EnemyAi" type="Node2D"]
script = ExtResource("1_qo7hy")
attack_distance = 8.0

[node name="Area2D" type="Area2D" parent="."]
collision_layer = 0
Expand All @@ -26,12 +28,13 @@ enabled = false

[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
path_desired_distance = 5.0
avoidance_enabled = true
radius = 16.0

[node name="NavigationVisualizer" type="Line2D" parent="NavigationAgent2D" node_paths=PackedStringArray("navigation_agent")]
[node name="NavigationVisualizer" type="Line2D" parent="." node_paths=PackedStringArray("navigation_agent")]
width = 1.0
script = ExtResource("2_2yt0t")
navigation_agent = NodePath("..")
navigation_agent = NodePath("../NavigationAgent2D")

[node name="TargetFinder" type="Node" parent="." node_paths=PackedStringArray("area", "bot_input")]
script = ExtResource("4_y3qb6")
Expand All @@ -49,3 +52,8 @@ target_finder = NodePath("../TargetFinder")
bot_input = NodePath("..")
raycast = NodePath("../RayCast2D")
navigation_agent = NodePath("../NavigationAgent2D")

[node name="TargetAim" type="Node" parent="." node_paths=PackedStringArray("bot_input", "target_finder")]
script = ExtResource("6_xkm0h")
bot_input = NodePath("..")
target_finder = NodePath("../TargetFinder")
1 change: 1 addition & 0 deletions scenes/weapons/zombie_attack.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

[node name="ZombieAttack" instance=ExtResource("1_tygi1")]
projectile_scene = ExtResource("2_wxtpk")
spawn_distance = 10.0
sound_resource = ExtResource("3_4gid6")
projectile_parent_reference = ExtResource("4_tkm0f")
damage_resource = ExtResource("5_0dp6e")
Expand Down
1 change: 1 addition & 0 deletions scripts/actor/bots/BotInput.gd
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ signal input_update
@export var enabled:bool = true
## Commands the movement
@export var mover:MoverTopDown2D
@export var attack_distance:float = 16.0

var axis_compensation:Vector2 # top down movement can use different speed for X&Y axis

Expand Down
15 changes: 14 additions & 1 deletion scripts/actor/bots/NavigationVisualizer2D.gd
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
class_name NavigationVisualizer2D
extends Line2D

@export var enabled:bool
@export var navigation_agent:NavigationAgent2D

func set_enabled(value:bool)->void:
enabled = value
visible = enabled
if enabled:
if !navigation_agent.path_changed.is_connected(update_path):
navigation_agent.path_changed.connect(update_path)
else:
if navigation_agent.path_changed.is_connected(update_path):
navigation_agent.path_changed.disconnect(update_path)

func _ready()->void:
navigation_agent.path_changed.connect(update_path)
top_level = true
global_position = Vector2.ZERO
set_enabled(enabled)

func update_path()->void:
points = navigation_agent.get_current_navigation_path()
3 changes: 1 addition & 2 deletions scripts/actor/bots/ProximityAttack.gd
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
class_name ProximityAttack
extends Node

@export var attack_range:float = 16.0
@export var target_finder:TargetFinder
@export var bot_input:BotInput

Expand All @@ -13,4 +12,4 @@ func on_target_update()->void:
bot_input.mover.input_resource.set_action(false)
return
var distance:float = (target_finder.closest.global_position - bot_input.global_position).length()
bot_input.mover.input_resource.set_action(distance <= attack_range)
bot_input.mover.input_resource.set_action(distance <= bot_input.attack_distance)
19 changes: 19 additions & 0 deletions scripts/actor/bots/TargetAim.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class_name TargetAim
extends Node

@export var bot_input:BotInput
@export var target_finder:TargetFinder
## just point attack direction to the same direction as walking.
@export var aim_walking_direction:bool

func _ready()->void:
target_finder.target_update.connect(on_target_update)

func on_target_update()->void:
if aim_walking_direction:
bot_input.mover.input_resource.set_aim_direction((bot_input.mover.input_resource.axis * bot_input.axis_compensation).normalized())
return
if target_finder.closest == null:
return
var direction:Vector2 = target_finder.closest.global_position - bot_input.global_position
bot_input.mover.input_resource.set_aim_direction((direction * bot_input.axis_compensation).normalized())
44 changes: 32 additions & 12 deletions scripts/actor/bots/TargetDirection.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,43 @@ extends Node

@export var target_finder:TargetFinder
@export var bot_input:BotInput
@export var attack_distance:float = 16.0
@export var raycast:RayCast2D
@export var navigation_agent:NavigationAgent2D
## Value used to change recalculation cooldown
@export var retarget_distance:float = 16.0

var detected: = false
var last_target_position:Vector2

## From bot to target
var local_direction:Vector2
## Track targets position
var last_target_position:Vector2
## Time last navigation update happened
var last_update_time:float
var navigation_cooldown:float = 1.0


func _ready()->void:
target_finder.target_update.connect(on_target_update)

func set_direction(direction:Vector2)->void:
bot_input.mover.input_resource.set_axis((direction * bot_input.axis_compensation).normalized())

func on_target_update()->void:
if target_finder.closest == null:
set_direction(Vector2.ZERO)
return
local_direction = target_finder.closest.global_position - bot_input.global_position
if local_direction.length() > attack_distance && line_of_sight():
bot_input.mover.input_resource.set_axis((local_direction * bot_input.axis_compensation).normalized())
bot_input.mover.input_resource.set_aim_direction(bot_input.mover.input_resource.axis)
var attack_dist_squared:float = bot_input.attack_distance * bot_input.attack_distance
if (local_direction.length_squared() < attack_dist_squared):
set_direction(Vector2.ZERO)
return
if line_of_sight() && local_direction.length_squared() > attack_dist_squared:
set_direction(local_direction)
return
navigation_update()
var point:Vector2 = navigation_agent.get_next_path_position()
var direction:Vector2 = (point - bot_input.global_position)
set_direction(direction)

## Raycast checks if anything from environment is in the way
func line_of_sight()->bool:
Expand All @@ -32,9 +48,13 @@ func line_of_sight()->bool:
return !raycast.is_colliding()

func navigation_update()->void:
if (navigation_agent.target_position - bot_input.global_position).length() > 16.0:
navigation_agent.target_position = target_finder.closest.global_position
var point:Vector2 = navigation_agent.get_next_path_position()
var direction:Vector2 = (point - bot_input.global_position).normalized()
bot_input.mover.input_resource.set_axis((direction * bot_input.axis_compensation).normalized())
bot_input.mover.input_resource.set_aim_direction(bot_input.mover.input_resource.axis)
var time: = Time.get_ticks_msec() * 0.001
if last_update_time + navigation_cooldown > time:
return
last_update_time = time
# the bigger distance overshoot, the sooner update happens
var moved_direction:Vector2 = (last_target_position - target_finder.closest.global_position)
var ratio:float = (retarget_distance) / max(moved_direction.length(), 1.0)
navigation_cooldown = min(ratio, 5.0)
last_target_position = target_finder.closest.global_position
navigation_agent.target_position = last_target_position
6 changes: 4 additions & 2 deletions scripts/weapon_system/projectile/SubProjectileManager.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ extends Node
@export var start_projectile_scene:PackedScene
@export var exit_projectile_scene:PackedScene

var axis_compensation:Vector2

func _ready()->void:
# TODO: just a mockup. Need some kind per situation configurations
projectile_spawner.collision_mask = Bitwise.append_flags(projectile_spawner.collision_mask, projectile.collision_mask)
projectile_spawner.axis_multiplication = projectile.axis_multiplier
axis_compensation = Vector2.ONE / projectile.axis_multiplier

if start_projectile_scene != null:
spawn.call_deferred(start_projectile_scene)
Expand All @@ -22,6 +24,6 @@ func _ready()->void:
func spawn(scene:PackedScene)->void:
projectile_spawner.projectile_scene = scene
projectile_spawner.projectile_position = projectile.global_position
projectile_spawner.direction = projectile.direction
projectile_spawner.direction = projectile.direction * projectile.axis_multiplier
projectile_spawner.damage_resource = projectile.damage_resource
projectile_spawner.spawn()

0 comments on commit 22beefe

Please sign in to comment.