Creating your own Projectile class
So far, we have only seen external ways to alternate your projectiles, however All Projectiles also allows you to create your own Projectile classes, Attack classes and more.
You can use the templates at the addons/all_projectiles/scripts/templates folder as a guide.


Creating custom projectile classes
To create your own custom projectile class, either duplicate the selected template or copy and paste its contents into a new script.

If you are using an external text editor, you can easily replace the placeholder name by using the Ctrl + H
shortcut.

Continuing with our previous project, this is what our projectile script would look like if it used its own custom class:
class_name WaveProjectile
extends AreaProjectile2D
var wall_layer: int
var amplitude: float
var frecuency: float
var local_time: float
var collision_object: CollisionObject2D
func _init(_resource: ProjectileBlueprint2D = null, _pi: PackedInfo = null) -> void:
if (_resource == null):
return
super(_resource, _pi)
wall_layer = _resource.global_properties.get("WALL_LAYER", 0)
amplitude = _resource.global_properties.get("AMPLITUDE", 1)
frecuency = _resource.global_properties.get("FRECUENCY", 1)
local_time = _resource.individual_properties.get("LOCAL_TIME", 0)
if (_pi != null):
assign(_pi)
# This is Your Projectile Start Method
func assign(pi: PackedInfo) -> void:
super(pi)
collision_object = null
func move(delta: float) -> void:
if !(can_move):
return
var angle: float
if (seeking):
if (target != null):
angle = seek_target(delta)
else:
try_retarget()
local_time += delta
var dir: Vector2 = direction + (direction.orthogonal() * cos(local_time * frecuency)) * amplitude
if (use_custom_movement):
dir += on_move.call(self, delta)
position += dir * speed * delta
if (look_at):
angle = transform.x.angle_to(dir)
transform = transform.rotated(angle)
transform.origin = position
func validate_collision(colliding_rid: RID, colliding_node: Node) -> bool:
if (wall_layer & collision_object.collision_layer != 0):
is_expired = true
return false
return super(colliding_rid, colliding_node)
func area_monitor_callback(status: int, area_rid: RID, instance_id: int, area_shape_index: int, self_shape_index: int) -> void:
collision_object = instance_from_id(instance_id)
super(status, area_rid, instance_id, area_shape_index, self_shape_index)
func body_monitor_callback(status: int, body_rid: RID, instance_id: int, body_shape_index: int, self_shape_index: int) -> void:
collision_object = instance_from_id(instance_id)
super(status, body_rid, instance_id, body_shape_index, self_shape_index)
func expire() -> void:
super()
func copy(base: Projectile2D, pi: PackedInfo = null) -> void:
super(base, pi)
if (base is WaveProjectile):
var buffer: WaveProjectile = base as WaveProjectile
wall_layer = buffer.wall_layer
amplitude = buffer.amplitude
frecuency = buffer.frecuency
local_time = buffer.local_time
if (pi != null):
assign(pi)
# Match the return types with your own 'class_name'
# Otherwise your 'custom projectile' will not work properly
func clone(_pi: PackedInfo = null) -> WaveProjectile:
var proj: WaveProjectile = WaveProjectile.new()
proj.copy(self)
if (_pi != null):
proj.assign(_pi)
return proj

Note: It is extremely important that you copy all your new variables in the "copy" method for your projectiles to work properly. All Projectiles is a tool based entirely on copying and cloning objects, so completing this step is absolutely crucial.
# Filling this method with your own variables is absolutely important.
func copy(base: Projectile2D, pi: PackedInfo = null) -> void:
super(base, pi)
if (base is WaveProjectile):
var buffer: WaveProjectile = base as WaveProjectile
wall_layer = buffer.wall_layer
amplitude = buffer.amplitude
frecuency = buffer.frecuency
local_time = buffer.local_time
if (pi != null):
assign(pi)
# Match the return types with your own 'class_name'
# Otherwise your 'custom projectile' will not work properly
func clone(_pi: PackedInfo = null) -> WaveProjectile:
var proj: WaveProjectile = WaveProjectile.new()
proj.copy(self)
if (_pi != null):
proj.assign(_pi)
return proj
Assigning custom projectile classes
Assigning a custom class to a projectile is very easy; you simply have to set it up through your ProjectileCaller2D.
extends Node2D
@onready var projectile_caller: ProjectileCaller2D = $ProjectileCaller2D
func _ready() -> void:
projectile_caller.set_projectile(0, WaveProjectile.new())
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())
By default, the ProjectileCaller2D will assign it the same ProjectileBlueprint2D stored at the specified index.
func _ready() -> void:
# These two statements are the same
projectile_caller.set_projectile(0, WaveProjectile.new())
projectile_caller.set_projectile(0, WaveProjectile.new(projectile_caller.get_projectile_blueprint(0)))
See more at set_projectile.