Demo: Improved AI for melee and ranged

This commit is contained in:
Serhii Snitsaruk 2024-02-01 15:45:11 +01:00
parent 92ecc17a44
commit f6ce516771
6 changed files with 121 additions and 35 deletions

View File

@ -21,7 +21,7 @@ func _generate_name() -> String:
# Called each time this task is ticked (aka executed). # Called each time this task is ticked (aka executed).
func _tick(_delta: float) -> Status: func _tick(_delta: float) -> Status:
var target := blackboard.get_var(target_var) as CharacterBody2D var target: Node2D = blackboard.get_var(target_var)
if not is_instance_valid(target): if not is_instance_valid(target):
return FAILURE return FAILURE
var dir: float = target.global_position.x - agent.global_position.x var dir: float = target.global_position.x - agent.global_position.x

View File

@ -10,11 +10,11 @@
#* #*
@tool @tool
extends BTAction extends BTAction
## Pursue target. ## Pursue: Move towards target until agent is flanking it.
## ##
## Returns RUNNING, while moving towards target but not yet at the desired distance. ## Returns RUNNING, while moving towards target but not yet at the desired position.
## Returns SUCCESS, when at the desired distance from target. ## Returns SUCCESS, when at the desired position from target (flanking it).
## Returns FAILURE, if target is not a valid instance. ## Returns FAILURE, if target is not a valid Node2D instance.
const TOLERANCE := 30.0 const TOLERANCE := 30.0
@ -23,33 +23,48 @@ const TOLERANCE := 30.0
@export var speed_var: String = "speed" @export var speed_var: String = "speed"
@export var approach_distance: float = 100.0 @export var approach_distance: float = 100.0
var _side: float #var _side: float
var _waypoint: Vector2
# Display a customized name (requires @tool). # Display a customized name (requires @tool).
func _generate_name() -> String: func _generate_name() -> String:
return "Pursue %s" % [LimboUtility.decorate_var(target_var)] return "Pursue %s" % [LimboUtility.decorate_var(target_var)]
# Called each time this task is entered.
func _enter() -> void: func _enter() -> void:
_side = 0.0 var target: Node2D = blackboard.get_var(target_var, null)
if is_instance_valid(target):
_select_new_waypoint(_get_desired_position(target))
# Called each time this task is ticked (aka executed). # Called each time this task is ticked (aka executed).
func _tick(_delta: float) -> Status: func _tick(_delta: float) -> Status:
var target := blackboard.get_var(target_var, null) as CharacterBody2D var target: Node2D = blackboard.get_var(target_var, null)
if not is_instance_valid(target): if not is_instance_valid(target):
return FAILURE return FAILURE
if _side == 0: var desired_pos: Vector2 = _get_desired_position(target)
var dir: Vector2 = agent.global_position - target.global_position if agent.global_position.distance_to(desired_pos) < TOLERANCE:
_side = signf(dir.x)
var target_pos := Vector2(
target.global_position.x + approach_distance * _side,
target.global_position.y)
if agent.global_position.distance_to(target_pos) < TOLERANCE:
return SUCCESS return SUCCESS
var speed: float = blackboard.get_var(speed_var, 200.0) var speed: float = blackboard.get_var(speed_var, 200.0)
agent.velocity = agent.global_position.direction_to(target_pos) * speed if agent.global_position.distance_to(_waypoint) < TOLERANCE:
_select_new_waypoint(desired_pos)
agent.velocity = agent.global_position.direction_to(_waypoint) * speed
agent.move_and_slide() agent.move_and_slide()
return RUNNING return RUNNING
## Get closest flanking position to target.
func _get_desired_position(target: Node2D) -> Vector2:
var side: float = signf(agent.global_position.x - target.global_position.x)
var desired_pos: Vector2 = target.global_position
desired_pos.x += approach_distance * side
return desired_pos
## Select an intermidiate waypoint towards the desired position.
func _select_new_waypoint(desired_position: Vector2) -> void:
var distance_vector: Vector2 = desired_position - agent.global_position
var angle_variation: float = randf_range(-0.2, 0.2)
_waypoint = agent.global_position + distance_vector.limit_length(150.0).rotated(angle_variation)

View File

@ -0,0 +1,23 @@
@tool
extends BTAction
## SelectRandomNearbyPos: Select a position nearby within specified range.
## Returns SUCCESS.
## Maximum distance to the desired position.
@export var range_min: float = 300.0
@export var range_max: float = 500.0
@export var position_var: String = "_pos"
# Display a customized name (requires @tool).
func _generate_name() -> String:
return "SelectRandomNearbyPos range: [%s, %s] ➜%s" % [
range_min, range_max,
LimboUtility.decorate_var(position_var)]
# Called each time this task is ticked (aka executed).
func _tick(_delta: float) -> Status:
var angle: float = randf() * TAU
var rand_distance: float = randf_range(range_min, range_max)
var pos: Vector2 = agent.global_position + Vector2(sin(angle), cos(angle)) * rand_distance
blackboard.set_var(position_var, pos)
return SUCCESS

View File

@ -1,4 +1,4 @@
[gd_resource type="BehaviorTree" load_steps=40 format=3 uid="uid://c2u6sljqkim0n"] [gd_resource type="BehaviorTree" load_steps=41 format=3 uid="uid://c2u6sljqkim0n"]
[ext_resource type="Script" path="res://demo/ai/tasks/get_first_in_group.gd" id="1_uvue5"] [ext_resource type="Script" path="res://demo/ai/tasks/get_first_in_group.gd" id="1_uvue5"]
[ext_resource type="Script" path="res://demo/ai/tasks/pursue.gd" id="2_aanv5"] [ext_resource type="Script" path="res://demo/ai/tasks/pursue.gd" id="2_aanv5"]
@ -28,8 +28,7 @@ animation_name = &"idle"
blend = 0.1 blend = 0.1
[sub_resource type="BTRandomWait" id="BTRandomWait_xlud8"] [sub_resource type="BTRandomWait" id="BTRandomWait_xlud8"]
min_duration = 0.7 max_duration = 3.0
max_duration = 1.5
[sub_resource type="BTAction" id="BTAction_c4cxo"] [sub_resource type="BTAction" id="BTAction_c4cxo"]
script = ExtResource("1_uvue5") script = ExtResource("1_uvue5")
@ -54,8 +53,8 @@ speed = 1.5
script = ExtResource("2_fl3fr") script = ExtResource("2_fl3fr")
target_var = "_target" target_var = "_target"
closest_side = false closest_side = false
range_min = 400.0 range_min = 400
range_max = 600.0 range_max = 600
position_var = "_flank_pos" position_var = "_flank_pos"
[sub_resource type="BTAction" id="BTAction_66hsk"] [sub_resource type="BTAction" id="BTAction_66hsk"]
@ -133,8 +132,10 @@ resource_name = "AnimationPlayer"
animation_player = SubResource("BBNode_s6vt4") animation_player = SubResource("BBNode_s6vt4")
animation_name = &"throw_prepare" animation_name = &"throw_prepare"
blend = 0.1 blend = 0.1
speed = 0.7
[sub_resource type="BTWait" id="BTWait_gbcyb"] [sub_resource type="BTWait" id="BTWait_gbcyb"]
duration = 1.3
[sub_resource type="BBNode" id="BBNode_qkfqt"] [sub_resource type="BBNode" id="BBNode_qkfqt"]
saved_value = NodePath("AnimationPlayer") saved_value = NodePath("AnimationPlayer")
@ -154,9 +155,12 @@ resource_name = "."
node = SubResource("BBNode_1yxc5") node = SubResource("BBNode_1yxc5")
method = &"throw_ninja_star" method = &"throw_ninja_star"
[sub_resource type="BTRandomWait" id="BTRandomWait_2pmoe"]
min_duration = 1.5
[sub_resource type="BTSequence" id="BTSequence_rgbq3"] [sub_resource type="BTSequence" id="BTSequence_rgbq3"]
custom_name = "Throw ninja star" custom_name = "Throw ninja star"
children = [SubResource("BTAction_8q20y"), SubResource("BTPlayAnimation_qa8jy"), SubResource("BTWait_gbcyb"), SubResource("BTPlayAnimation_0ktds"), SubResource("BTCallMethod_yx4fk")] children = [SubResource("BTAction_8q20y"), SubResource("BTPlayAnimation_qa8jy"), SubResource("BTWait_gbcyb"), SubResource("BTPlayAnimation_0ktds"), SubResource("BTCallMethod_yx4fk"), SubResource("BTRandomWait_2pmoe")]
metadata/_weight_ = 1.0 metadata/_weight_ = 1.0
[sub_resource type="BTProbabilitySelector" id="BTProbabilitySelector_rjsiq"] [sub_resource type="BTProbabilitySelector" id="BTProbabilitySelector_rjsiq"]

View File

@ -1,9 +1,10 @@
[gd_resource type="BehaviorTree" load_steps=29 format=3 uid="uid://cqluon1y1hnn5"] [gd_resource type="BehaviorTree" load_steps=40 format=3 uid="uid://cqluon1y1hnn5"]
[ext_resource type="Script" path="res://demo/ai/tasks/get_first_in_group.gd" id="1_3j1v8"] [ext_resource type="Script" path="res://demo/ai/tasks/get_first_in_group.gd" id="1_3j1v8"]
[ext_resource type="Script" path="res://demo/ai/tasks/select_flanking_pos.gd" id="2_vbh52"] [ext_resource type="Script" path="res://demo/ai/tasks/select_flanking_pos.gd" id="2_vbh52"]
[ext_resource type="Script" path="res://demo/ai/tasks/arrive_pos.gd" id="3_20ffh"] [ext_resource type="Script" path="res://demo/ai/tasks/arrive_pos.gd" id="3_20ffh"]
[ext_resource type="Script" path="res://demo/ai/tasks/face_target.gd" id="4_x8yor"] [ext_resource type="Script" path="res://demo/ai/tasks/face_target.gd" id="4_x8yor"]
[ext_resource type="Script" path="res://demo/ai/tasks/select_random_nearby_pos.gd" id="5_mexvv"]
[sub_resource type="BlackboardPlan" id="BlackboardPlan_46tbn"] [sub_resource type="BlackboardPlan" id="BlackboardPlan_46tbn"]
var/speed/name = "speed" var/speed/name = "speed"
@ -28,19 +29,55 @@ max_duration = 2.5
[sub_resource type="BTAction" id="BTAction_c4cxo"] [sub_resource type="BTAction" id="BTAction_c4cxo"]
script = ExtResource("1_3j1v8") script = ExtResource("1_3j1v8")
group = &"player" group = &"player"
output_var = "_target" output_var = "target"
[sub_resource type="BTSequence" id="BTSequence_yhjh1"] [sub_resource type="BTSequence" id="BTSequence_yhjh1"]
custom_name = "Take a break" custom_name = "Take a break"
children = [SubResource("BTPlayAnimation_qiw21"), SubResource("BTRandomWait_xlud8"), SubResource("BTAction_c4cxo")] children = [SubResource("BTPlayAnimation_qiw21"), SubResource("BTRandomWait_xlud8"), SubResource("BTAction_c4cxo")]
[sub_resource type="BTComment" id="BTComment_qhsko"]
custom_name = "He is bored attacking player all the time ;)"
[sub_resource type="BTAction" id="BTAction_lpk67"]
script = ExtResource("5_mexvv")
range_min = 300.0
range_max = 500.0
position_var = "pos"
[sub_resource type="BBNode" id="BBNode_ok0r5"]
saved_value = NodePath("AnimationPlayer")
resource_name = "AnimationPlayer"
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_unftu"]
animation_player = SubResource("BBNode_ok0r5")
animation_name = &"walk"
blend = 0.1
[sub_resource type="BTAction" id="BTAction_apfsn"]
script = ExtResource("3_20ffh")
target_position_var = "pos"
speed_var = "speed"
tolerance = 50.0
[sub_resource type="BTTimeLimit" id="BTTimeLimit_baob7"]
time_limit = 3.0
children = [SubResource("BTAction_apfsn")]
[sub_resource type="BTSequence" id="BTSequence_0gdqn"]
custom_name = "Random Walk"
children = [SubResource("BTComment_qhsko"), SubResource("BTAction_lpk67"), SubResource("BTPlayAnimation_unftu"), SubResource("BTTimeLimit_baob7")]
[sub_resource type="BTProbability" id="BTProbability_sat88"]
run_chance = 0.3
children = [SubResource("BTSequence_0gdqn")]
[sub_resource type="BTAction" id="BTAction_kuuw2"] [sub_resource type="BTAction" id="BTAction_kuuw2"]
script = ExtResource("2_vbh52") script = ExtResource("2_vbh52")
target_var = "_target" target_var = "target"
closest_side = true closest_side = true
range_min = 400.0 range_min = 400
range_max = 1000.0 range_max = 1000
position_var = "_shoot_pos" position_var = "shoot_pos"
[sub_resource type="BBNode" id="BBNode_kc64r"] [sub_resource type="BBNode" id="BBNode_kc64r"]
saved_value = NodePath("AnimationPlayer") saved_value = NodePath("AnimationPlayer")
@ -53,7 +90,7 @@ blend = 0.1
[sub_resource type="BTAction" id="BTAction_66hsk"] [sub_resource type="BTAction" id="BTAction_66hsk"]
script = ExtResource("3_20ffh") script = ExtResource("3_20ffh")
target_position_var = "_shoot_pos" target_position_var = "shoot_pos"
speed_var = "speed" speed_var = "speed"
tolerance = 50.0 tolerance = 50.0
@ -62,10 +99,10 @@ children = [SubResource("BTAction_66hsk")]
[sub_resource type="BTAction" id="BTAction_enw2m"] [sub_resource type="BTAction" id="BTAction_enw2m"]
script = ExtResource("4_x8yor") script = ExtResource("4_x8yor")
target_var = "_target" target_var = "target"
[sub_resource type="BTSequence" id="BTSequence_lhg7f"] [sub_resource type="BTSequence" id="BTSequence_lhg7f"]
custom_name = "Reposition" custom_name = "Get into position"
children = [SubResource("BTAction_kuuw2"), SubResource("BTPlayAnimation_panch"), SubResource("BTTimeLimit_24ath"), SubResource("BTAction_enw2m")] children = [SubResource("BTAction_kuuw2"), SubResource("BTPlayAnimation_panch"), SubResource("BTTimeLimit_24ath"), SubResource("BTAction_enw2m")]
metadata/_weight_ = 1.0 metadata/_weight_ = 1.0
@ -110,9 +147,16 @@ metadata/_weight_ = 1.0
times = 3 times = 3
children = [SubResource("BTSequence_rgbq3")] children = [SubResource("BTSequence_rgbq3")]
[sub_resource type="BTSequence" id="BTSequence_h2tm0"]
custom_name = "Align and shoot"
children = [SubResource("BTSequence_lhg7f"), SubResource("BTRepeat_g08ia")]
[sub_resource type="BTSelector" id="BTSelector_1rrya"]
children = [SubResource("BTProbability_sat88"), SubResource("BTSequence_h2tm0")]
[sub_resource type="BTSequence" id="BTSequence_pxl2k"] [sub_resource type="BTSequence" id="BTSequence_pxl2k"]
custom_name = "Main" custom_name = "Main"
children = [SubResource("BTSequence_yhjh1"), SubResource("BTSequence_lhg7f"), SubResource("BTRepeat_g08ia")] children = [SubResource("BTSequence_yhjh1"), SubResource("BTSelector_1rrya")]
[resource] [resource]
blackboard_plan = SubResource("BlackboardPlan_46tbn") blackboard_plan = SubResource("BlackboardPlan_46tbn")