Creating Attack patterns
As we saw previously, your Projectiles can spawn inactive to re-activate after a certain amount of time.
Fortunately, you can alter this behavior by modifying the wait_time property of your projectiles.
extends Node2D
@onready var projectile_caller: ProjectileCaller2D = $ProjectileCaller2D
func _ready() -> void:
projectile_caller.get_projectile(0).set_on_start(custom_start)
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
projectile_caller.request_projectile(0, position, get_global_mouse_position())
# We will modify the "wait_time" property right at the start of the projectile lifecycle.
func custom_start(proj: Projectile2D) -> void:
proj.wait_time = 0.5

Here is a collection of different attack patterns for both ALL_AT_ONCE and ONE_BY_ONE instances.
ALL_AT_ONCE patterns
We will continue working on our previous project under the next Player script, only modifying the “custom_start” method:
extends Node2D
@onready var projectile_caller: ProjectileCaller2D = $ProjectileCaller2D
func _ready() -> void:
projectile_caller.get_projectile(0).set_on_start(custom_start)
func _input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
projectile_caller.request_projectile(0, position, get_global_mouse_position())
# We will be modifying this method.
func custom_start(proj: Projectile2D) -> void:
proj.wait_time = 0.5
- Forwards (Default)
func custom_start(proj: Projectile2D) -> void:
pass

- Backwards
func custom_start(proj: Projectile2D) -> void:
var index: int = (proj.instances - proj.index) - 1
proj.wait_time = index * proj.spawn_interval

- Center to extremes
func custom_start(proj: Projectile2D) -> void:
var index: int = proj.instances / 2
index = abs(proj.index - index)
proj.wait_time = index * proj.spawn_interval

- Extremes to center
func custom_start(proj: Projectile2D) -> void:
var index: int = proj.instances / 2
index = abs(abs(proj.index - index) - index)
proj.wait_time = index * proj.spawn_interval

- n Divided
func custom_start(proj: Projectile2D) -> void:
var n: int = 4
var div: int = proj.instances / n
var rem: int = proj.index % div
var index: int = (rem * n) + (proj.index / div)
proj.wait_time = index * proj.spawn_interval

- n Divided (Reverse)
func custom_start(proj: Projectile2D) -> void:
var n: int = 4
var div: int = proj.instances / n
var rem: int = proj.index % div
var index: int = ((div - rem - 1) * n) + (proj.index / div)
proj.wait_time = index * proj.spawn_interval

- n Divided (Centered)
func custom_start(proj: Projectile2D) -> void:
var n: int = 4
var div: int = proj.instances / n
var rem: int = proj.index % div
var index: int = (abs(rem - (div / 2)) * n) + (proj.index / div)
proj.wait_time = index * proj.spawn_interval

- n Divided (Extremes)
func custom_start(proj: Projectile2D) -> void:
var n: int = 4
var div: int = proj.instances / n
var rem: int = proj.index % div
var index: int = (abs(abs(rem - (div / 2)) - (div / 2)) * n) + (proj.index / div)
proj.wait_time = index * proj.spawn_interval

ONE_BY_ONE patterns
We will continue working on our previous project under the next Player script, only modifying the “on_main_enter” method:
extends Node2D
@export var separation: int = 30
@onready var projectile_manager: ProjectileManager2D = $ProjectileManager2D
@onready var bow: Sprite2D = $Bow
@onready var animation_player: AnimationPlayer = $AnimationPlayer
func _ready() -> void:
animation_player.play("idle")
projectile_manager.get_attack(0).set_on_charge_enter(on_charge_enter).set_on_charge_exit(on_charge_exit).set_on_main_enter(on_main_enter)
func _process(_delta: float) -> void:
var direction: Vector2 = (get_global_mouse_position() - position).normalized()
bow.position = direction * separation
bow.rotation = direction.angle()
projectile_manager.position = bow.position
projectile_manager.rotation = bow.rotation
if (Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT)):
projectile_manager.request_execution(0, 0, position + (direction * separation), get_global_mouse_position())
func on_charge_enter(attack: Attack2D) -> void:
animation_player.play("charge")
attack.charge_enter()
func on_charge_exit(attack: Attack2D) -> void:
animation_player.play("idle")
attack.charge_exit()
# We will be modifying this method.
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
attack.pi.instance_id = attack.remaining_shots - 1
- Forwards (Default)
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()

- Backwards
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var index: int = attack.remaining_shots - 1
attack.pi.instance_id = index

- Center to extremes
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var index: int = attack.instances / 2
var div: int = (attack.pi.instance_id + 1) / 2
var sig: int = attack.pi.instance_id % 2
sig = -1 if (sig == 1) else 1
index += div * sig
attack.pi.instance_id = index

- Extremes to center
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var index: int = attack.instances / 2
var div: int = (attack.pi.instance_id + 1) / 2
var rem: int = attack.pi.instance_id % 2
index = div if (rem == 0) else attack.instances - div
attack.pi.instance_id = index

- n Divided
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var n: int = 4
var div: int = attack.pi.instance_id / n
var rem: int = attack.pi.instance_id % n
var sep: int = attack.instances % n
sep = attack.instances / n if (sep == 0) else (attack.instances / n) + 1
var index: int = (sep * rem) + div
attack.pi.instance_id = index

- n Divided (Reverse)
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var n: int = 4
var div: int = attack.pi.instance_id / n
var rem: int = attack.pi.instance_id % n
var sep: int = attack.instances % n
sep = attack.instances / n if (sep == 0) else (attack.instances / n) + 1
div = abs(sep - div - 1)
var index: int = (sep * rem) + div
attack.pi.instance_id = index

- n Divided (Centered)
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var n: int = 4
var div: int = attack.pi.instance_id / n
var rem: int = attack.pi.instance_id % n
var sep: int = attack.instances % n
sep = attack.instances / n if (sep == 0) else (attack.instances / n) + 1
var sem: int = (div + 1) / 2
var sig: int = div % 2
sig = -1 if (sig == 0) else 1
div = (sep / 2) + (sem * sig)
var index: int = (sep * rem) + div
attack.pi.instance_id = index

- n Divided (Extremes)
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var n: int = 4
var div: int = attack.pi.instance_id / n
var rem: int = attack.pi.instance_id % n
var sep: int = attack.instances % n
sep = attack.instances / n if (sep == 0) else (attack.instances / n) + 1
var sem: int = (div + 1) / 2
div = sem if (div % 2 == 0) else (sep - sem)
var index: int = (sep * rem) + div
attack.pi.instance_id = index

- n By n
func on_main_enter(attack: Attack2D) -> void:
attack.main_enter()
var n: int = 4
var div: int = attack.pi.instance_id / n
var rem: int = attack.pi.instance_id % n
var sep: int = attack.instances % n
sep = attack.instances / n if (sep == 0) else (attack.instances / n) + 1
var index: int = (sep * rem) + (div * n)
attack.pi.instance_id = index
