Skip to content

Commit

Permalink
simple bot navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
nezvers committed Sep 13, 2024
1 parent b8fd3d7 commit 3ac12a8
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 96 deletions.
11 changes: 11 additions & 0 deletions addons/nezvers_library/static/GameMath/GameMath.gd
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ static func dist_to_line_3d(pos:Vector3, line_start:Vector3, line_end:Vector3)->
return bv.length()
return ab.cross(ac).length() / ab.length()

static func get_closest_node_2d(point:Vector2, body_list:Array[Node2D])->Node2D:
if body_list.is_empty():
return null
var closest:Node2D = body_list.front()
var dist:float = (closest.global_position - point).length_squared()
for body:Node2D in body_list:
var _dist:float = (body.global_position - point).length_squared()
if _dist < dist:
dist = _dist
closest = body
return closest

## Outline of the formula
static func dampened_spring(displacement, damp, velocity, spring:float, delta:float):
Expand Down
51 changes: 51 additions & 0 deletions scenes/actors/ai/enemy_ai.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[gd_scene load_steps=7 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"]

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

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

[node name="Area2D" type="Area2D" parent="."]
collision_layer = 0
collision_mask = 2
monitorable = false

[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
shape = SubResource("RectangleShape2D_ixqjj")
debug_color = Color(0, 0.6, 0.701961, 0)

[node name="RayCast2D" type="RayCast2D" parent="."]
enabled = false

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

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

[node name="TargetFinder" type="Node" parent="." node_paths=PackedStringArray("area", "bot_input")]
script = ExtResource("4_y3qb6")
area = NodePath("../Area2D")
bot_input = NodePath("..")

[node name="ProximityAttack" type="Node" parent="." node_paths=PackedStringArray("target_finder", "bot_input")]
script = ExtResource("3_4n00q")
target_finder = NodePath("../TargetFinder")
bot_input = NodePath("..")

[node name="TargetDirection" type="Node" parent="." node_paths=PackedStringArray("target_finder", "bot_input", "raycast", "navigation_agent")]
script = ExtResource("5_ekfo4")
target_finder = NodePath("../TargetFinder")
bot_input = NodePath("..")
raycast = NodePath("../RayCast2D")
navigation_agent = NodePath("../NavigationAgent2D")
33 changes: 5 additions & 28 deletions scenes/actors/zombie.tscn
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
[gd_scene load_steps=20 format=3 uid="uid://62j3fy0o3uwl"]
[gd_scene load_steps=18 format=3 uid="uid://62j3fy0o3uwl"]

[ext_resource type="PackedScene" uid="uid://botai66n8rwt3" path="res://scenes/actors/actor.tscn" id="1_fkm2k"]
[ext_resource type="Shader" path="res://scripts/shaders/color_flash.gdshader" id="2_duvie"]
[ext_resource type="Texture2D" uid="uid://dqqfb3tyiyk2v" path="res://assets/images/characters/zombie_16x16_strip8.png" id="2_giu7j"]
[ext_resource type="Script" path="res://scripts/actor/ZombieInput.gd" id="2_yis6g"]
[ext_resource type="Resource" uid="uid://k6kulmw3ddq8" path="res://resources/health_resource/zombie_health.tres" id="2_ysqwl"]
[ext_resource type="Resource" uid="uid://bgj1j2b6mc1u7" path="res://resources/ActorStatsResource/zombie_stats.tres" id="5_srg4e"]
[ext_resource type="Resource" uid="uid://dq8mhmimpehoi" path="res://resources/InputResource/zombie_input_resource.tres" id="6_o1vf4"]
[ext_resource type="PackedScene" uid="uid://baisjmcsnk3on" path="res://scenes/weapons/zombie_attack.tscn" id="7_tyqu3"]
[ext_resource type="Resource" uid="uid://bmurrqosq7bec" path="res://resources/sounds/kill_zombie.tres" id="7_uiw1f"]
[ext_resource type="Script" path="res://scripts/actor/AddPoint.gd" id="9_3rsmj"]
[ext_resource type="PackedScene" uid="uid://bu2n5cvym7bse" path="res://scenes/vfx/zombie_body.tscn" id="9_3xjhw"]
[ext_resource type="PackedScene" uid="uid://cenq1bawfywu8" path="res://scenes/actors/ai/enemy_ai.tscn" id="9_4t8nd"]
[ext_resource type="Resource" uid="uid://5emqdks7qgbm" path="res://resources/score_resource.tres" id="10_3mlgp"]
[ext_resource type="Script" path="res://scripts/actor/bots/NavigationVisualizer2D.gd" id="10_ouyos"]

[sub_resource type="ShaderMaterial" id="ShaderMaterial_mtjxa"]
resource_local_to_scene = true
Expand Down Expand Up @@ -100,9 +99,6 @@ _data = {
"walk": SubResource("Animation_d7gcn")
}

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

[node name="Zombie" instance=ExtResource("1_fkm2k")]
collision_layer = 4
collision_mask = 7
Expand All @@ -128,33 +124,14 @@ input_resource = ExtResource("6_o1vf4")
sound_resource_dead = ExtResource("7_uiw1f")
dead_vfx_scene = ExtResource("9_3xjhw")

[node name="PlayerDetector" type="Area2D" parent="." index="8"]

[node name="CollisionShape2D" type="CollisionShape2D" parent="PlayerDetector" index="0"]
shape = SubResource("RectangleShape2D_ixqjj")
debug_color = Color(0, 0.6, 0.701961, 0)

[node name="ZombieInput" type="Node2D" parent="." index="9" node_paths=PackedStringArray("mover", "player_detector", "weapon")]
script = ExtResource("2_yis6g")
[node name="ZombieInput" parent="." index="8" node_paths=PackedStringArray("mover") instance=ExtResource("9_4t8nd")]
mover = NodePath("../MoverTopDown2D")
player_detector = NodePath("../PlayerDetector")
minimal_distance = Vector2(12, 6)
weapon = NodePath("../SlashAttack")

[node name="RayCast2D" type="RayCast2D" parent="ZombieInput" index="0"]
enabled = false

[node name="NavigationAgent2D" type="NavigationAgent2D" parent="ZombieInput" index="1"]

[node name="NavigationVisualizer" type="Line2D" parent="ZombieInput/NavigationAgent2D" index="0" node_paths=PackedStringArray("navigation_agent")]
script = ExtResource("10_ouyos")
navigation_agent = NodePath("..")

[node name="SlashAttack" parent="." index="10" node_paths=PackedStringArray("mover") instance=ExtResource("7_tyqu3")]
[node name="SlashAttack" parent="." index="9" node_paths=PackedStringArray("mover") instance=ExtResource("7_tyqu3")]
collision_mask = 2
mover = NodePath("../MoverTopDown2D")

[node name="AddPoint" type="Node" parent="." index="11" node_paths=PackedStringArray("damage_receiver")]
[node name="AddPoint" type="Node" parent="." index="10" node_paths=PackedStringArray("damage_receiver")]
script = ExtResource("9_3rsmj")
damage_receiver = NodePath("../DamageReceiver")
score_resource = ExtResource("10_3mlgp")
68 changes: 0 additions & 68 deletions scripts/actor/ZombieInput.gd

This file was deleted.

29 changes: 29 additions & 0 deletions scripts/actor/bots/BotInput.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class_name BotInput
extends Node2D

signal input_update

@export var enabled:bool = true
## Commands the movement
@export var mover:MoverTopDown2D

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

## Not using automatic setter functions because they are called before _ready during initialization
func _ready()->void:
# Set to run before mover
process_physics_priority -= 1
axis_compensation = Vector2.ONE/mover.axis_multiplier
set_enabled(enabled)

## Toggle processing for animation state machine
func set_enabled(value:bool)->void:
enabled = value
set_physics_process(enabled)
if !enabled:
mover.input_resource.axis = Vector2.ZERO

## Inputs need to be manipulated here
## Modules use this to time their functions
func _physics_process(_delta:float)->void:
input_update.emit()
16 changes: 16 additions & 0 deletions scripts/actor/bots/ProximityAttack.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class_name ProximityAttack
extends Node

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

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

func on_target_update()->void:
if target_finder.closest == null:
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)
40 changes: 40 additions & 0 deletions scripts/actor/bots/TargetDirection.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class_name TargetDirection
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

var detected: = false
var last_target_position:Vector2
var local_direction:Vector2

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


func on_target_update()->void:
if target_finder.closest == null:
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)
return
navigation_update()

## Raycast checks if anything from environment is in the way
func line_of_sight()->bool:
raycast.target_position = local_direction
raycast.force_raycast_update()
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)
19 changes: 19 additions & 0 deletions scripts/actor/bots/TargetFinder.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class_name TargetFinder
extends Node

signal target_update

@export var area:Area2D
@export var bot_input:BotInput

var target_list:Array[Node2D]
var closest:Node2D

func _ready()->void:
bot_input.input_update.connect(on_input_update)

func on_input_update()->void:
target_list = area.get_overlapping_bodies()
closest = GameMath.get_closest_node_2d(bot_input.global_position, target_list)

target_update.emit()

0 comments on commit 3ac12a8

Please sign in to comment.