Demo: Attack state and simple melee AI

This commit is contained in:
Serhii Snitsaruk 2024-01-30 21:34:07 +01:00
parent 4719c77f94
commit 67d86214f0
15 changed files with 320 additions and 20 deletions

View File

@ -270,10 +270,22 @@ tracks/20/keys = {
"update": 0,
"values": [Vector2(1, 1)]
}
tracks/21/type = "value"
tracks/21/imported = false
tracks/21/enabled = true
tracks/21/path = NodePath("Root/Hitbox/CollisionShape2D:disabled")
tracks/21/interp = 1
tracks/21/loop_wrap = true
tracks/21/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [true]
}
[sub_resource type="Animation" id="Animation_g7a0r"]
resource_name = "attack"
length = 0.3
length = 0.35
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
@ -503,6 +515,18 @@ tracks/18/keys = {
"update": 1,
"values": [false]
}
tracks/19/type = "value"
tracks/19/imported = false
tracks/19/enabled = true
tracks/19/path = NodePath("Root/Hitbox/CollisionShape2D:disabled")
tracks/19/interp = 1
tracks/19/loop_wrap = true
tracks/19/keys = {
"times": PackedFloat32Array(0, 0.15, 0.3),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 1,
"values": [true, false, true]
}
[sub_resource type="Animation" id="Animation_b0ub6"]
resource_name = "attack_ranged"
@ -996,7 +1020,7 @@ tracks/20/keys = {
[sub_resource type="Animation" id="Animation_gowr5"]
resource_name = "hurt"
length = 0.15
length = 0.25
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
@ -1018,7 +1042,7 @@ tracks/1/interp = 2
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 0.1),
"transitions": PackedFloat32Array(1, 1),
"transitions": PackedFloat32Array(1, 2),
"update": 0,
"values": [0.0, -0.459832]
}
@ -1036,7 +1060,7 @@ tracks/2/keys = {
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
tracks/3/enabled = false
tracks/3/path = NodePath("Root/Rig/Body/Hat:position")
tracks/3/interp = 2
tracks/3/loop_wrap = true
@ -1226,6 +1250,18 @@ tracks/18/keys = {
"update": 1,
"values": [false]
}
tracks/19/type = "value"
tracks/19/imported = false
tracks/19/enabled = true
tracks/19/path = NodePath("Root/Hitbox/CollisionShape2D:disabled")
tracks/19/interp = 1
tracks/19/loop_wrap = true
tracks/19/keys = {
"times": PackedFloat32Array(0, 0.25),
"transitions": PackedFloat32Array(1, 1),
"update": 1,
"values": [true, true]
}
[sub_resource type="Animation" id="Animation_gnqgt"]
resource_name = "idle"
@ -2214,6 +2250,7 @@ script = ExtResource("5_taq6b")
[node name="CollisionShape2D" type="CollisionShape2D" parent="Root/Hitbox"]
shape = SubResource("RectangleShape2D_2k81i")
disabled = true
debug_color = Color(0.933131, 0.0801983, 0.605982, 0.42)
metadata/_edit_lock_ = true

View File

@ -0,0 +1,2 @@
extends "res://demo/agents/scripts/agent_base.gd"

View File

@ -0,0 +1,38 @@
[gd_scene load_steps=6 format=3 uid="uid://sjans5psq6pg"]
[ext_resource type="PackedScene" uid="uid://ooigbfhfy4wa" path="res://demo/agents/agent_base.tscn" id="1_uffd7"]
[ext_resource type="BehaviorTree" uid="uid://bpdm5jnegi38" path="res://demo/ai/trees/enemy_melee_simple.tres" id="2_tmu02"]
[ext_resource type="Script" path="res://demo/agents/agent_base_enemy.gd" id="2_w7f5y"]
[ext_resource type="Texture2D" uid="uid://cw8s50856x8ct" path="res://demo/assets/agent_melee_simple.png" id="3_c30et"]
[sub_resource type="BlackboardPlan" id="BlackboardPlan_4e1gs"]
var/speed/name = "speed"
var/speed/type = 3
var/speed/value = 400.0
var/speed/hint = 0
var/speed/hint_string = ""
[node name="AgentEnemy" instance=ExtResource("1_uffd7")]
script = ExtResource("2_w7f5y")
[node name="LegL" parent="Root/Rig" index="0"]
texture = ExtResource("3_c30et")
[node name="LegR" parent="Root/Rig" index="1"]
texture = ExtResource("3_c30et")
[node name="Body" parent="Root/Rig" index="2"]
texture = ExtResource("3_c30et")
[node name="Hat" parent="Root/Rig/Body" index="0"]
texture = ExtResource("3_c30et")
[node name="HandL" parent="Root/Rig/Body" index="1"]
texture = ExtResource("3_c30et")
[node name="HandR" parent="Root/Rig/Body" index="2"]
texture = ExtResource("3_c30et")
[node name="BTPlayer" type="BTPlayer" parent="." index="4"]
behavior_tree = ExtResource("2_tmu02")
blackboard_plan = SubResource("BlackboardPlan_4e1gs")

View File

@ -2,17 +2,26 @@ extends "res://demo/agents/scripts/agent_base.gd"
## Player.
@onready var limbo_hsm: LimboHSM = $LimboHSM
@onready var hsm: LimboHSM = $LimboHSM
@onready var idle_state: LimboState = $LimboHSM/IdleState
@onready var move_state: LimboState = $LimboHSM/MoveState
@onready var attack_state: LimboState = $LimboHSM/AttackState
func _ready() -> void:
super._ready()
_init_state_machine()
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("attack") and event.is_echo() == false:
hsm.dispatch("attack!")
func _init_state_machine() -> void:
limbo_hsm.add_transition(idle_state, move_state, idle_state.EVENT_FINISHED)
limbo_hsm.add_transition(move_state, idle_state, move_state.EVENT_FINISHED)
limbo_hsm.initialize(self)
limbo_hsm.set_active(true)
hsm.add_transition(idle_state, move_state, idle_state.EVENT_FINISHED)
hsm.add_transition(move_state, idle_state, move_state.EVENT_FINISHED)
hsm.add_transition(hsm.ANYSTATE, attack_state, "attack!")
hsm.add_transition(attack_state, move_state, attack_state.EVENT_FINISHED)
hsm.initialize(self)
hsm.set_active(true)

View File

@ -1,21 +1,27 @@
[gd_scene load_steps=5 format=3 uid="uid://d07ag5dcje13i"]
[gd_scene load_steps=6 format=3 uid="uid://d07ag5dcje13i"]
[ext_resource type="PackedScene" uid="uid://ooigbfhfy4wa" path="res://demo/agents/agent_base.tscn" id="1_mswd4"]
[ext_resource type="Script" path="res://demo/agents/player/player.gd" id="2_24nyi"]
[ext_resource type="Script" path="res://demo/agents/player/idle_state.gd" id="2_moi60"]
[ext_resource type="Script" path="res://demo/agents/player/move_state.gd" id="3_bxpc0"]
[ext_resource type="Script" path="res://demo/agents/player/states/idle_state.gd" id="3_ekb12"]
[ext_resource type="Script" path="res://demo/agents/player/states/move_state.gd" id="4_paikn"]
[ext_resource type="Script" path="res://demo/agents/player/states/attack_state.gd" id="5_mpgu6"]
[node name="Player" instance=ExtResource("1_mswd4")]
[node name="Player" groups=["player"] instance=ExtResource("1_mswd4")]
script = ExtResource("2_24nyi")
[node name="LimboHSM" type="LimboHSM" parent="." index="4"]
[node name="IdleState" type="LimboState" parent="LimboHSM" index="0" node_paths=PackedStringArray("animation_player")]
script = ExtResource("2_moi60")
script = ExtResource("3_ekb12")
animation_player = NodePath("../../AnimationPlayer")
animation = &"idle"
[node name="MoveState" type="LimboState" parent="LimboHSM" index="1" node_paths=PackedStringArray("animation_player")]
script = ExtResource("3_bxpc0")
script = ExtResource("4_paikn")
animation_player = NodePath("../../AnimationPlayer")
animation = &"walk"
[node name="AttackState" type="LimboState" parent="LimboHSM" index="2" node_paths=PackedStringArray("animation_player")]
script = ExtResource("5_mpgu6")
animation_player = NodePath("../../AnimationPlayer")
animation = &"attack"

View File

@ -0,0 +1,13 @@
extends LimboState
## Idle state.
@export var animation_player: AnimationPlayer
@export var animation: StringName
func _enter() -> void:
animation_player.play(animation)
await animation_player.animation_finished
if is_active():
get_root().dispatch(EVENT_FINISHED)

View File

@ -7,7 +7,7 @@ extends LimboState
func _enter() -> void:
animation_player.play(animation)
animation_player.play(animation, 0.1)
func _update(_delta: float) -> void:

View File

@ -9,7 +9,7 @@ const VERTICAL_FACTOR := 0.8
@export var speed: float = 500.0
func _enter() -> void:
animation_player.play(animation)
animation_player.play(animation, 0.1)
func _update(_delta: float) -> void:

View File

@ -6,6 +6,7 @@ extends CharacterBody2D
@onready var health: Health = $Health
@onready var root: Node2D = $Root
var _frames_since_facing_update: int = 0
func _ready() -> void:
health.damaged.connect(_damaged)
@ -17,14 +18,34 @@ func _physics_process(_delta: float) -> void:
func _update_facing() -> void:
if velocity.x > 0.0 and root.scale.x < 0.0:
_frames_since_facing_update += 1
if _frames_since_facing_update > 3:
face_dir(velocity.x)
func face_dir(dir: float) -> void:
if dir > 0.0 and root.scale.x < 0.0:
root.scale.x = 1.0;
if velocity.x < 0.0 and root.scale.x > 0.0:
_frames_since_facing_update = 0
if dir < 0.0 and root.scale.x > 0.0:
root.scale.x = -1.0;
_frames_since_facing_update = 0
func _damaged(_amount: float) -> void:
animation_player.play(&"hurt")
var btplayer := get_node_or_null(^"BTPlayer") as BTPlayer
if btplayer:
btplayer.set_active(false)
var hsm := get_node_or_null(^"LimboHSM")
if hsm:
hsm.set_active(false)
await animation_player.animation_finished
if btplayer:
btplayer.restart()
if hsm:
hsm.set_active(true)
func _die() -> void:

View File

@ -0,0 +1,20 @@
@tool
extends BTAction
## FaceTarget
@export var target_var: String = "target"
# Display a customized name (requires @tool).
func _generate_name() -> String:
return "FaceTarget " + LimboUtility.decorate_var(target_var)
# Called each time this task is ticked (aka executed).
func _tick(delta: float) -> Status:
var target := blackboard.get_var(target_var) as CharacterBody2D
if not is_instance_valid(target):
return FAILURE
var dir: float = target.global_position.x - agent.global_position.x
agent.velocity = Vector2.ZERO
agent.face_dir(dir)
return SUCCESS

View File

@ -0,0 +1,19 @@
@tool
extends BTAction
## Get first node in group and save it to the blackboard.
@export var group: StringName
@export var output_var: String = "target"
func _generate_name() -> String:
return "GetFirstNodeInGroup \"%s\" -> %s" % [
group,
LimboUtility.decorate_var(output_var)
]
func _tick(_delta: float) -> Status:
var node = agent.get_tree().get_first_node_in_group(group)
blackboard.set_var(output_var, node)
return SUCCESS

View File

@ -0,0 +1,30 @@
@tool
extends BTAction
const TOLERANCE := 30.0
@export var target_var: String = "target"
@export var speed_var: String = "speed"
@export var distance: float = 100.0
func _generate_name() -> String:
return "Pursue %s" % [LimboUtility.decorate_var(target_var)]
func _tick(_delta: float) -> Status:
var target := blackboard.get_var(target_var, null) as CharacterBody2D
if not is_instance_valid(target):
return FAILURE
var dir: Vector2 = target.global_position - agent.global_position
var target_pos := Vector2(
target.global_position.x - distance * signf(dir.x),
target.global_position.y)
if agent.global_position.distance_to(target_pos) < TOLERANCE:
return SUCCESS
var speed: float = blackboard.get_var(speed_var, 200.0)
agent.velocity = agent.global_position.direction_to(target_pos) * speed
agent.move_and_slide()
return RUNNING

View File

@ -0,0 +1,94 @@
[gd_resource type="BehaviorTree" load_steps=24 format=3 uid="uid://bpdm5jnegi38"]
[ext_resource type="Script" path="res://demo/ai/tasks/get_first_in_group.gd" id="1_2jpsu"]
[ext_resource type="Script" path="res://demo/ai/tasks/pursue.gd" id="2_h5db5"]
[ext_resource type="Script" path="res://demo/ai/tasks/face_target.gd" id="3_bpmfp"]
[sub_resource type="BlackboardPlan" id="BlackboardPlan_46tbn"]
var/speed/name = "speed"
var/speed/type = 3
var/speed/value = 400.0
var/speed/hint = 0
var/speed/hint_string = ""
[sub_resource type="BBNode" id="BBNode_nrd4b"]
resource_name = "AnimationPlayer"
saved_value = NodePath("AnimationPlayer")
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_qiw21"]
animation_player = SubResource("BBNode_nrd4b")
animation_name = &"idle"
blend = 0.1
[sub_resource type="BTRandomWait" id="BTRandomWait_xlud8"]
min_duration = 0.1
max_duration = 0.8
[sub_resource type="BTSequence" id="BTSequence_yhjh1"]
custom_name = "Chilling"
children = [SubResource("BTPlayAnimation_qiw21"), SubResource("BTRandomWait_xlud8")]
[sub_resource type="BTComment" id="BTComment_6tjy8"]
custom_name = "Approach player"
[sub_resource type="BTAction" id="BTAction_ulbrf"]
script = ExtResource("1_2jpsu")
group = &"player"
output_var = "_target"
[sub_resource type="BBNode" id="BBNode_wpj6d"]
resource_name = "AnimationPlayer"
saved_value = NodePath("AnimationPlayer")
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_olf37"]
animation_player = SubResource("BBNode_wpj6d")
animation_name = &"walk"
blend = 0.1
[sub_resource type="BTAction" id="BTAction_a4jqi"]
script = ExtResource("2_h5db5")
target_var = "_target"
speed_var = "speed"
distance = 100.0
[sub_resource type="BTComment" id="BTComment_i1svx"]
custom_name = "Attack player"
[sub_resource type="BTAction" id="BTAction_kidxn"]
script = ExtResource("3_bpmfp")
target_var = "_target"
[sub_resource type="BTWait" id="BTWait_tadkc"]
duration = 0.1
[sub_resource type="BBNode" id="BBNode_s8evu"]
resource_name = "AnimationPlayer"
saved_value = NodePath("AnimationPlayer")
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_ppmxd"]
await_completion = 2.0
animation_player = SubResource("BBNode_s8evu")
animation_name = &"attack"
[sub_resource type="BBNode" id="BBNode_4jcut"]
resource_name = "AnimationPlayer"
saved_value = NodePath("AnimationPlayer")
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_wgf32"]
animation_player = SubResource("BBNode_4jcut")
animation_name = &"idle"
blend = 0.1
[sub_resource type="BTWait" id="BTWait_lcs3i"]
duration = 0.5
[sub_resource type="BTSequence" id="BTSequence_pxl2k"]
custom_name = "Melee attack"
children = [SubResource("BTSequence_yhjh1"), SubResource("BTComment_6tjy8"), SubResource("BTAction_ulbrf"), SubResource("BTPlayAnimation_olf37"), SubResource("BTAction_a4jqi"), SubResource("BTComment_i1svx"), SubResource("BTAction_kidxn"), SubResource("BTWait_tadkc"), SubResource("BTPlayAnimation_ppmxd"), SubResource("BTPlayAnimation_wgf32"), SubResource("BTWait_lcs3i")]
[sub_resource type="BTSelector" id="BTSelector_mcdtk"]
children = [SubResource("BTSequence_pxl2k")]
[resource]
blackboard_plan = SubResource("BlackboardPlan_46tbn")
root_task = SubResource("BTSelector_mcdtk")

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=11 format=3 uid="uid://bsig1usigbbuy"]
[gd_scene load_steps=12 format=3 uid="uid://bsig1usigbbuy"]
[ext_resource type="Texture2D" uid="uid://b3g14elmg0m36" path="res://demo/assets/env_rocks.png" id="1_145kx"]
[ext_resource type="PackedScene" uid="uid://dt2jlrqffpyw" path="res://demo/scenes/clouds.tscn" id="1_gsxmp"]
@ -6,6 +6,7 @@
[ext_resource type="Texture2D" uid="uid://4kw2ks8doc0w" path="res://demo/assets/env_plants.png" id="2_kesm7"]
[ext_resource type="PackedScene" uid="uid://bpd1wmw2f7bvg" path="res://demo/props/gong.tscn" id="3_nbto3"]
[ext_resource type="PackedScene" uid="uid://d07ag5dcje13i" path="res://demo/agents/player/player.tscn" id="5_cmgoj"]
[ext_resource type="PackedScene" uid="uid://sjans5psq6pg" path="res://demo/agents/agent_base_enemy.tscn" id="5_knyss"]
[ext_resource type="PackedScene" uid="uid://comfxjrcylgb" path="res://demo/agents/agent_melee_simple.tscn" id="7_ruy6b"]
[sub_resource type="Animation" id="Animation_gwtgs"]
@ -2875,6 +2876,9 @@ metadata/_edit_lock_ = true
[node name="AgentMeleeSimple" parent="YSort/Agents" instance=ExtResource("7_ruy6b")]
position = Vector2(1212, 333)
[node name="AgentEnemy" parent="YSort/Agents" instance=ExtResource("5_knyss")]
position = Vector2(1527.4, 690.665)
[node name="Player" parent="YSort/Agents" instance=ExtResource("5_cmgoj")]
position = Vector2(633, 256)
@ -4910,5 +4914,6 @@ position = Vector2(1059, -29)
metadata/_edit_lock_ = true
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Limits"]
visible = false
polygon = PackedVector2Array(-814, 535, -380, 190, -306, -21, 87, -46, 346, -330, 870, -373, 1115, -418, 1363, -334, 1613, -324, 1854, -225, 2208, -197, 2675, 309, 2642, 741, 2244, 1140, 1618, 1271, 1360, 1193, 865, 1423, 706, 1377, -113, 1192, -452, 1035, -488, 902, -625, 803, -734, 680, -1660, 719, -1331, 2205, 3170, 2304, 3988, 404, 2999, -1433, 131, -1364, -1715, 264, -1659, 716, -737, 676)
metadata/_edit_lock_ = true

View File

@ -42,6 +42,12 @@ move_down={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null)
]
}
attack={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"echo":false,"script":null)
]
}
[layer_names]