Godot Version
4.2.2
Question
Hello,
I’ve been following the below tutorial and created a boss enemy for my game.
The boss_enemy uses a Finite State Machine to switch between states.
I made slight modifications to fit my game. (Added gravity, additional states, etc.)
As per the tutorial, the _physics_process is disabled initially. And is enabled on whichever states that require it.
In the tutorial and my original code, _physics_process is enabled only in the “Follow” state. It is disabled in all other states.
Afterward, I introduced 2 new states.
- Death_Fall (_physics_process enabled)
- Death_Collapse
When the boss_enemy dies, the states change in the following order.
→ Death_Fall (Till is_on_floor == true, has gravity) → Death-Collapse (No gravity)
The issue I’m facing is as follows.
When moving from a state where _physics_process is disabled (Ex: “RangedAttack” state), the gravity is applied after moving to the “Death_Fall” state without any issue.
But when the current_state is the “Follow” state (where _physics_process is enabled), and when moving to the “Death_Fall” state, the gravity is not applied.
Pasted the relevant code snippets below.
Scene Tree of the Boss_Enemy node:
Boss Enemy Main Script (Only included the code snippets relevant to this question):
extends CharacterBody2D
@onready var player = get_tree().get_nodes_in_group(Gamemanager.GROUP_PLAYER)[0].marker_2d
@onready var sprite = $Sprite2D
@onready var finite_state_machine = $FiniteStateMachine
@onready var animation_player_damaged = $AnimationPlayer_Damaged
@onready var progress_bar = $ProgressBar
@onready var progress_bar_timer = $ProgressBar_Timer
@onready var stagger_timer = $StaggerTimer
@onready var audio_stream_player_2d = $AudioStreamPlayer2D
@onready var hitbox_collision_shape_2d = $Hitbox/CollisionShape2D
@onready var laser_collision_shape_2d = $Pivot/Area2D/CollisionShape2D
@onready var hitbox = $Hitbox
@export var death_fall_state: State
@export var death_collapse_state: State
var direction: Vector2
var speed: float = 60
var _gravity: float = ProjectSettings.get_setting("physics/2d/default_gravity")
var _dying: bool = false
var is_staggered: bool = false
var invincible: bool = false
var can_move: bool = true
var points: int = 1000
var enemy_health: int = 2
func _ready():
set_physics_process(false)
progress_bar.max_value = enemy_health
func _process(delta):
progress_bar.value = enemy_health
direction = player.global_position - global_position
if direction.x < 10:
sprite.flip_h = true
else:
sprite.flip_h = false
func _physics_process(delta):
if finite_state_machine.current_state == death_fall_state or finite_state_machine.current_state == death_collapse_state:
velocity.x = 0
velocity.y += _gravity * delta
move_and_slide()
else:
if position.distance_to(player.position) > 100:
velocity = direction.normalized() * speed
move_and_collide(velocity * delta)
func get_damaged_by_bullet():
enemy_health -= 1
if enemy_health <= 0:
die()
else:
return
func die():
if _dying == true:
return
_dying = true
Signalmanager.on_enemy_hit.emit(points)
finite_state_machine.change_state("Death_Fall")
func _on_hitbox_area_entered(area):
if invincible == true:
return
else:
invincible = true
progress_bar.visible = true
is_staggered = true
progress_bar_timer.start()
stagger_timer.start()
animation_player_damaged.play("damaged")
if area.is_in_group("player_bullet"):
get_damaged_by_bullet()
Finite State Machine script (Attached to the FiniteStateMachine node) :
extends Node2D
var current_state: State
var previous_state: State
func _ready():
current_state = get_child(0) as State
previous_state = current_state
current_state.enter()
func change_state(state):
current_state = find_child(state) as State
current_state.enter()
previous_state.exit()
previous_state = current_state
State.gd:
extends Node
class_name State
@onready var player = get_tree().get_nodes_in_group(Gamemanager.GROUP_PLAYER)[0].player_hitbox
@onready var animation_player = owner.find_child("AnimationPlayer")
@onready var debug = owner.find_child("Label")
@onready var debug2 = owner.find_child("Label2")
func _ready():
set_physics_process(false)
func enter():
set_physics_process(true)
func exit():
set_physics_process(false)
func transition():
pass
func _physics_process(delta):
transition()
debug.text = name
debug2.text = "Player position :" + str(player.global_position)
Follow.gd (Script for the “Follow” state. Child of the FiniteStateMachine node. Extended from State.gd script) :
extends State
@onready var boss_enemy_level_01 = $"../.."
func enter():
super.enter()
owner.set_physics_process(true)
animation_player.play("idle")
func exit():
super.exit()
owner.set_physics_process(false)
func transition():
var distance = owner.direction.length()
if boss_enemy_level_01._dying == true:
owner.set_physics_process(true)
get_parent().change_state("Death_Fall")
else:
if distance < 30:
get_parent().change_state("MeleeAttack")
elif distance > 130:
var chance = randi() % 2
match chance:
0:
get_parent().change_state("RangedAttack")
1:
get_parent().change_state("LaserBeam")
Death_Fall.gd (Script for the “Death_Fall” state. Child of the FiniteStateMachine node. Extended from State.gd script) :
extends State
@onready var boss_enemy_level_01 = $"../.."
func enter():
super.enter()
owner.set_physics_process(true)
animation_player.play("death_fall")
func exit():
super.exit()
owner.set_physics_process(false)
func transition():
if boss_enemy_level_01.is_on_floor():
get_parent().change_state("Death_Collapse")
Any help on this would be much appreciated.