Godot Version
4.3 stable Windows 11
Question
I’m making a bullet hell game, and to improve performance I attempted to implement object pooling. This is what my bullet scene tree looks like:
This is the script attached to every bullet:
extends CharacterBody2D
@onready var player = get_tree().get_first_node_in_group("player")
@export var type : String
@onready var colour : int
@onready var speed : float
@onready var accel : float
@onready var target_speed : float
@onready var angle : float
@onready var angular_velocity : float
@onready var lifespan : float
@onready var damage : float
@onready var hitbox : Area2D
@export var can_rotate : bool
func _ready():
print(get_parent())
deactivate()
func activate():
$Sprite.frame = colour
$"Sprite/Hitbox/CollisionShape2D".set("disabled", false)
$AnimationPlayer.play(&"RESET")
add_to_group("bullet")
remove_from_group("bulletpool")
if not lifespan == null :
await(get_tree().create_timer(lifespan).timeout)
play_death_animation()
func play_death_animation():
$"Sprite/Hitbox/CollisionShape2D".set("disabled", true)
$AnimationPlayer.play("despawn")
func deactivate():
remove_from_group("bullet")
add_to_group("bulletpool")
BulletManager.bullet_pool[type].append(self)
BulletManager.active_bullets[type].erase(self)
global_position = Vector2(29999, 29999)
speed = 0
accel = 0
target_speed = 0
angle = 0
angular_velocity = 0
damage = 0
velocity = Vector2(0,0)
set_process_mode(PROCESS_MODE_DISABLED)
func move():
speed = move_toward(speed, target_speed, accel)
if not angular_velocity == 0 :
angle += angular_velocity
velocity = Vector2.from_angle(angle) * speed
rotation = angle
move_and_slide()
func _on_animation_player_animation_finished(despawn) -> void:
deactivate()
This is the code in my main scene that instantiates the bullets when the game runs:
func instantiate_bullets():
for i in 500 :
for type in BulletManager.bullet_pool :
var objShot = load(BulletManager.BULLET_DIRECTORY + type + ".tscn").instantiate()
BulletManager.bullet_pool[type].append(objShot)
get_tree().get_root().call_deferred("add_child", objShot)
This is the global BulletManager script:
extends Node
const BULLET_DIRECTORY = "res://Enemy/Bullets/"
var bullet_pool : Dictionary = {
"ball_M": [],
"knife": []
}
var active_bullets : Dictionary = {
"ball_M": [],
"knife": []
}
func _physics_process(delta):
for i in active_bullets :
for bullet in active_bullets[i] :
if bullet.has_method("move") :
bullet.move()
This is the function in my enemy class script to be called when I want to fire bullets:
func CreateShotA1(bullet_type : String, colour : int, pos : Vector2, speed : float, accel : float, target_speed : float, angle : float, angular_velocity : float, lifespan : float, dmg : float):
var objShot = get_available_bullet(bullet_type)
if not objShot == null :
objShot.colour = colour
objShot.global_position = pos
objShot.speed = speed
objShot.accel = accel
objShot.target_speed = target_speed
objShot.angle = angle
objShot.angular_velocity = angular_velocity
objShot.lifespan = lifespan
objShot.damage = dmg
objShot.set_process_mode(PROCESS_MODE_INHERIT)
objShot.activate()
BulletManager.active_bullets[bullet_type].append(objShot)
BulletManager.bullet_pool[bullet_type].erase(objShot)
return objShot
func get_available_bullet(bullet_type: String):
var avaliable_bullet_id = BulletManager.bullet_pool[bullet_type].pop_back()
if not BulletManager.active_bullets.get(bullet_type, false):
BulletManager.active_bullets[bullet_type] = []
BulletManager.active_bullets[bullet_type].append(avaliable_bullet_id)
return avaliable_bullet_id
And this is an example of an enemy behaviour that calls the function to fire bullets:
func attack1():
var howmany = clamp(roundi(WaveManager.difficulty / 10), 8, 24)
var angle = 0.00
for i in howmany :
CreateShotA1("knife", 0, global_position+Vector2(35*cos(angle), 1*sin(angle)), 40, 0, 40, angle, 0, 5, damage)
angle += TAU/howmany
await(get_tree().create_timer(3/howmany).timeout)
The problem is, everytime an enemy fires a bullet, I get the error Cannot call method 'create_timer' on a null value
pointing to the line await(get_tree().create_timer(lifespan).timeout)
in the bullet script. I’ve checked the remote tab whenever I run my game and confirmed the bullets are, indeed, part of the scene tree.
What could be the problem here? I’ve already asked about this issue before (x), but have not figured out the problem. Apologies for the long post, but at this point I’m desperate to solve this issue. Any help is appreciated.