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.