Gravity is not applying to a Boss Enemy, when it's in a certain state

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.

Used the State Charts plugin to create states, instead of using purely code.
The below tutorial was very helpful in implementing this.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.