Finish Hiro AI state machine

This commit is contained in:
Mario Martínez 2024-09-11 01:02:09 +02:00
parent ebef60e8ee
commit 3016e63b23
5 changed files with 213 additions and 23 deletions

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=76 format=3 uid="uid://dd2km6qhpdicx"]
[gd_scene load_steps=80 format=3 uid="uid://dd2km6qhpdicx"]
[ext_resource type="Script" path="res://Scripts/hiro_ai.gd" id="1_inmen"]
[ext_resource type="Texture2D" uid="uid://cm7tgomrcvq30" path="res://Hiro/Hiro Walk Sprite Cycle Line Up.png" id="2_ctdbh"]
@ -7,6 +7,7 @@
[ext_resource type="Script" path="res://addons/godot_state_charts/state_chart.gd" id="6_i4c04"]
[ext_resource type="Script" path="res://addons/godot_state_charts/compound_state.gd" id="7_j4kqa"]
[ext_resource type="Script" path="res://addons/godot_state_charts/atomic_state.gd" id="8_pqyhf"]
[ext_resource type="Script" path="res://addons/godot_state_charts/expression_guard.gd" id="9_ak1rc"]
[ext_resource type="Script" path="res://addons/godot_state_charts/transition.gd" id="9_la16b"]
[sub_resource type="Animation" id="Animation_04piu"]
@ -519,7 +520,6 @@ tracks/3/keys = {
[sub_resource type="Animation" id="Animation_ji461"]
resource_name = "kick_east"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -609,7 +609,6 @@ tracks/6/keys = {
[sub_resource type="Animation" id="Animation_cuaji"]
resource_name = "kick_north"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -675,7 +674,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_hqga6"]
resource_name = "kick_south"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -741,7 +739,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_s8xim"]
resource_name = "kick_west"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -807,7 +804,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_o0diw"]
resource_name = "punch_east_left"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -873,7 +869,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_ar6r3"]
resource_name = "punch_east_rigth"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -939,7 +934,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_1e0v6"]
resource_name = "punch_north_left"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -1005,7 +999,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_bwm5c"]
resource_name = "punch_north_rigth"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -1071,7 +1064,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_qmuik"]
resource_name = "punch_south_left"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -1137,7 +1129,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_bw220"]
resource_name = "punch_south_rigth"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -1203,7 +1194,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_wjl2t"]
resource_name = "punch_west_left"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -1269,7 +1259,6 @@ tracks/4/keys = {
[sub_resource type="Animation" id="Animation_e4rxs"]
resource_name = "punch_west_rigth"
length = 0.266673
loop_mode = 1
step = 0.0666667
tracks/0/type = "value"
tracks/0/imported = false
@ -1740,8 +1729,8 @@ advance_mode = 2
advance_condition = &"melee"
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_l17k0"]
switch_mode = 2
advance_mode = 2
advance_expression = "get(\"parameters/conditions/melee\") == false"
[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_ln3pc"]
switch_mode = 2
@ -1759,7 +1748,7 @@ states/melee/position = Vector2(329, 224)
states/walk/node = SubResource("AnimationNodeBlendSpace2D_vunsg")
states/walk/position = Vector2(520, 108)
transitions = ["Start", "idle", SubResource("AnimationNodeStateMachineTransition_akmuq"), "idle", "walk", SubResource("AnimationNodeStateMachineTransition_sn1tm"), "walk", "idle", SubResource("AnimationNodeStateMachineTransition_5i3fu"), "idle", "melee", SubResource("AnimationNodeStateMachineTransition_y8p74"), "melee", "idle", SubResource("AnimationNodeStateMachineTransition_l17k0"), "hurt", "idle", SubResource("AnimationNodeStateMachineTransition_ln3pc")]
graph_offset = Vector2(-222, -37)
graph_offset = Vector2(-228, 51)
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_vm050"]
radius = 8.0
@ -1770,6 +1759,18 @@ size = Vector2(13, 8)
[sub_resource type="CircleShape2D" id="CircleShape2D_md645"]
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_hxosv"]
radius = 12.0
height = 44.0
[sub_resource type="Resource" id="Resource_ri8lv"]
script = ExtResource("9_ak1rc")
expression = "melee_stage == 0"
[sub_resource type="Resource" id="Resource_gtfv8"]
script = ExtResource("9_ak1rc")
expression = "melee_stage == 1"
[node name="HiroAI" type="CharacterBody2D"]
y_sort_enabled = true
collision_layer = 5
@ -1830,6 +1831,14 @@ shape = SubResource("CircleShape2D_md645")
disabled = true
debug_color = Color(0.745098, 0.462745, 0, 0.419608)
[node name="Meleebox" type="Area2D" parent="."]
collision_layer = 4
[node name="DetectionArea" type="CollisionShape2D" parent="Meleebox"]
position = Vector2(0, -13)
shape = SubResource("CapsuleShape2D_hxosv")
debug_color = Color(0.784314, 0.423529, 0.27451, 0.419608)
[node name="StateMachine" type="Node" parent="."]
script = ExtResource("6_i4c04")
@ -1846,15 +1855,83 @@ to = NodePath("../../Hurt")
event = &"been_hurt"
delay_in_seconds = "0.0"
[node name="ToMelee" type="Node" parent="StateMachine/Root/Think"]
script = ExtResource("9_la16b")
to = NodePath("../../Melee")
event = &"will_melee"
delay_in_seconds = "0.0"
[node name="ToApproach" type="Node" parent="StateMachine/Root/Think"]
script = ExtResource("9_la16b")
to = NodePath("../../Approach")
event = &"wants_tomelee"
guard = SubResource("Resource_ri8lv")
delay_in_seconds = "0.0"
[node name="ToChase" type="Node" parent="StateMachine/Root/Think"]
script = ExtResource("9_la16b")
to = NodePath("../../Chase")
event = &"wants_tomelee"
guard = SubResource("Resource_gtfv8")
delay_in_seconds = "0.0"
[node name="Approach" type="Node" parent="StateMachine/Root"]
script = ExtResource("8_pqyhf")
[node name="ToHurt" type="Node" parent="StateMachine/Root/Approach"]
script = ExtResource("9_la16b")
to = NodePath("../../Hurt")
event = &"been_hurt"
delay_in_seconds = "0.0"
[node name="ToMelee" type="Node" parent="StateMachine/Root/Approach"]
script = ExtResource("9_la16b")
to = NodePath("../../Melee")
event = &"will_melee"
delay_in_seconds = "0.0"
[node name="ToThink" type="Node" parent="StateMachine/Root/Approach"]
script = ExtResource("9_la16b")
to = NodePath("../../Think")
event = &"approach_complete"
delay_in_seconds = "0.0"
[node name="Chase" type="Node" parent="StateMachine/Root"]
script = ExtResource("8_pqyhf")
[node name="ToHurt" type="Node" parent="StateMachine/Root/Chase"]
script = ExtResource("9_la16b")
to = NodePath("../../Hurt")
event = &"been_hurt"
delay_in_seconds = "0.0"
[node name="ToMelee" type="Node" parent="StateMachine/Root/Chase"]
script = ExtResource("9_la16b")
to = NodePath("../../Melee")
event = &"will_melee"
delay_in_seconds = "0.0"
[node name="ToThink" type="Node" parent="StateMachine/Root/Chase"]
script = ExtResource("9_la16b")
to = NodePath("../../Think")
event = &"melee_aborted"
delay_in_seconds = "0.0"
[node name="Melee" type="Node" parent="StateMachine/Root"]
script = ExtResource("8_pqyhf")
[node name="ToHurt" type="Node" parent="StateMachine/Root/Melee"]
script = ExtResource("9_la16b")
to = NodePath("../../Hurt")
event = &"been_hurt"
delay_in_seconds = "0.0"
[node name="ToThink" type="Node" parent="StateMachine/Root/Melee"]
script = ExtResource("9_la16b")
to = NodePath("../../Think")
event = &"melee_complete"
delay_in_seconds = "0.0"
[node name="Hurt" type="Node" parent="StateMachine/Root"]
script = ExtResource("8_pqyhf")
@ -1889,6 +1966,21 @@ delay_in_seconds = "0.0"
script = ExtResource("8_pqyhf")
[connection signal="animation_finished" from="AnimationTree" to="." method="_on_animation_tree_animation_finished"]
[connection signal="body_entered" from="Meleebox" to="." method="_on_meleebox_body_entered"]
[connection signal="body_exited" from="Meleebox" to="." method="_on_meleebox_body_exited"]
[connection signal="state_entered" from="StateMachine/Root/Think" to="." method="_on_think_state_entered"]
[connection signal="state_processing" from="StateMachine/Root/Think" to="." method="_on_think_state_processing"]
[connection signal="state_entered" from="StateMachine/Root/Approach" to="." method="_on_approach_state_entered"]
[connection signal="state_exited" from="StateMachine/Root/Approach" to="." method="_on_approach_state_exited"]
[connection signal="state_processing" from="StateMachine/Root/Approach" to="." method="_on_approach_state_processing"]
[connection signal="taken" from="StateMachine/Root/Approach/ToThink" to="." method="_on_approach_to_think_taken"]
[connection signal="state_entered" from="StateMachine/Root/Chase" to="." method="_on_chase_state_entered"]
[connection signal="state_exited" from="StateMachine/Root/Chase" to="." method="_on_chase_state_exited"]
[connection signal="state_processing" from="StateMachine/Root/Chase" to="." method="_on_chase_state_processing"]
[connection signal="taken" from="StateMachine/Root/Chase/ToThink" to="." method="_on_chase_to_think_taken"]
[connection signal="state_entered" from="StateMachine/Root/Melee" to="." method="_on_melee_state_entered"]
[connection signal="state_exited" from="StateMachine/Root/Melee" to="." method="_on_melee_state_exited"]
[connection signal="state_processing" from="StateMachine/Root/Melee" to="." method="_on_melee_state_processing"]
[connection signal="state_entered" from="StateMachine/Root/Hurt" to="." method="_on_hurt_state_entered"]
[connection signal="state_exited" from="StateMachine/Root/Hurt" to="." method="_on_hurt_state_exited"]
[connection signal="state_entered" from="StateMachine/Root/HurtReposition" to="." method="_on_hurt_reposition_state_entered"]

View file

@ -2019,7 +2019,7 @@ states/melee/position = Vector2(329, 224)
states/walk/node = SubResource("AnimationNodeBlendSpace2D_vunsg")
states/walk/position = Vector2(520, 108)
transitions = ["Start", "idle", SubResource("AnimationNodeStateMachineTransition_akmuq"), "idle", "walk", SubResource("AnimationNodeStateMachineTransition_sn1tm"), "walk", "idle", SubResource("AnimationNodeStateMachineTransition_5i3fu"), "idle", "melee", SubResource("AnimationNodeStateMachineTransition_y8p74"), "melee", "idle", SubResource("AnimationNodeStateMachineTransition_l17k0"), "hurt", "idle", SubResource("AnimationNodeStateMachineTransition_ln3pc")]
graph_offset = Vector2(-222, -37)
graph_offset = Vector2(-254, -71)
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_vm050"]
radius = 8.0

View file

@ -1,20 +1,31 @@
class_name AICharacter
extends CharacterBody2D
@export var player_character: PlayableCharacter
@export var health = 100
@export var KNOCKBACK_POWER = 50
@export var KNOCKBACK_REPOSITION_POWER = 75
@export var KNOCKBACK_RANDOMNESS = 50
@export var MELEE_RADIUS = 75
@onready var anim_tree: AnimationTree = $AnimationTree
@onready var anim_playback: AnimationNodeStateMachinePlayback = anim_tree["parameters/playback"]
@onready var state_machine: StateChart = $StateMachine
var timer = 0
# Getting hurt variables
var attacked_from = Vector2.ZERO
var received_damage = 0
var hit_counter = 0
var wants_to_melee = false
signal _got_hurt(damage: int, attack_direction: Vector2)
#signal defeated
func _ready():
state_machine.set_expression_property("melee_stage", 0)
func set_animation_direction(facing_direction: Vector2):
anim_tree.set("parameters/idle/blend_position", facing_direction)
anim_tree.set("parameters/walk/blend_position", facing_direction)
@ -22,6 +33,11 @@ func set_animation_direction(facing_direction: Vector2):
anim_tree.set("parameters/melee/1/blend_position", facing_direction)
anim_tree.set("parameters/melee/2/blend_position", facing_direction)
anim_tree.set("parameters/hurt/blend_position", facing_direction)
func follow_player(speed: float):
var approach_direction = position.direction_to(player_character.position)
velocity = speed * approach_direction
set_animation_direction(approach_direction)
func _physics_process(_delta):
move_and_slide()
@ -36,6 +52,27 @@ func _on_ai_got_hurt(attack_power: int, attack_direction: Vector2):
received_damage = attack_power
state_machine.send_event("been_hurt")
## Thinking state
## Once some time passes out, do_thinking is called
func _on_think_state_entered():
timer = randf_range(3.0/60.0, 1.5)
if timer <= 88.0/90.0:
timer = timer * 0.75
func _on_think_state_processing(delta):
timer -= delta
if timer <= 0:
do_thinking()
## This function actually determines what to do
func do_thinking():
var distance = position.distance_to(player_character.position)
if distance > MELEE_RADIUS:
state_machine.set_expression_property("melee_stage", 0)
# melee attacks here
else:
state_machine.send_event("wants_tomelee")
func _on_hurt_state_entered():
var facing_direction = attacked_from * -1
health = health - received_damage
@ -72,7 +109,8 @@ func _on_hurt_state_entered():
elif attacked_from.y != 0:
knockback_variance.x = randf_range(-1, 1) * KNOCKBACK_RANDOMNESS
velocity = attacked_from * KNOCKBACK_POWER + knockback_variance
print(velocity, knockback_variance)
state_machine.set_expression_property("melee_stage", 0)
func _on_hurt_state_exited():
velocity = Vector2.ZERO
@ -92,7 +130,68 @@ func _on_hurt_reposition_state_exited():
func _on_animation_tree_animation_finished(anim_name):
if anim_name.contains("hurt"):
state_machine.send_event("hurt_complete")
elif (anim_name.contains("kick") or anim_name.contains("punch")):
if wants_to_melee:
anim_tree.set("parameters/conditions/melee", true)
else:
state_machine.send_event("melee_complete")
func _on_defeat_state_entered():
queue_free()
## Aproaching
func _on_approach_state_entered():
follow_player(25)
anim_tree.set("parameters/conditions/is_moving", true)
timer = 1
func _on_approach_state_processing(delta):
timer -= delta
if timer < 0:
state_machine.send_event("approach_complete")
func _on_approach_state_exited():
anim_tree.set("parameters/conditions/is_moving", false)
velocity = Vector2.ZERO
func _on_approach_to_think_taken():
state_machine.set_expression_property("melee_stage", 1)
## Chasing
func _on_chase_state_entered():
anim_tree.set("parameters/conditions/is_moving", true)
func _on_chase_state_processing(_delta):
follow_player(65)
var distance = position.distance_to(player_character.position)
if distance >= MELEE_RADIUS:
state_machine.send_event("melee_aborted")
func _on_chase_state_exited():
anim_tree.set("parameters/conditions/is_moving", false)
velocity = Vector2.ZERO
func _on_chase_to_think_taken():
state_machine.set_expression_property("melee_stage", 0)
## Melee
func _on_meleebox_body_entered(body):
if body == player_character:
wants_to_melee = true
state_machine.send_event("will_melee")
func _on_melee_state_entered():
state_machine.set_expression_property("melee_stage", 0)
anim_tree.set("parameters/conditions/melee", true)
func _on_meleebox_body_exited(body):
if body == player_character:
wants_to_melee = false
func _on_melee_state_processing(_delta):
follow_player(0)
func _on_melee_state_exited():
anim_tree.set("parameters/conditions/melee", false)

View file

@ -1,3 +1,4 @@
class_name PlayableCharacter
extends CharacterBody2D
@export var speed = 100

View file

@ -41,10 +41,8 @@ offset_bottom = 1099.0
scale = Vector2(0.25, 0.25)
initial_node_to_watch = NodePath("../HiroAI")
[node name="HiroAI" parent="." instance=ExtResource("5_r7nw4")]
position = Vector2(135, 216)
health = 500
KNOCKBACK_POWER = 40
KNOCKBACK_RANDOMNESS = 40
[node name="HiroAI" parent="." node_paths=PackedStringArray("player_character") instance=ExtResource("5_r7nw4")]
position = Vector2(135, 215)
player_character = NodePath("../HiroPlayer")
[connection signal="attack_did_hit" from="HiroPlayer" to="HiroAI" method="_on_player_attack_did_hit"]