Projectile2D

Inherits: RefCounted < Object

Inherited By: AreaProjectile2D, InstancedProjectile2D

Base class for all 2D projectile objects.


Description

The Projectile2D base class manages the internal projectile lifecycle, from initialization to expiration. It directs:

  • Movement calculations (standard and custom).
  • Target acquisition, seeking and validation.
  • Collision processing and pierce management.
  • Lifetime countdown.
  • Cleanup, event propagation and projectile chaining.

Note: Projectile instances don't have a Node base and need help from a ProjectileProcessor2D to work properly.


Tutorials


Properties

float after_hit_angular_speed 0
bool allow_rehit false
float angular_speed 0
float angular_spread 0
bool area_monitoreable false
Attack2D attack
bool can_move false
float cast_radius 0
RID cast_shape
int collision_layer 0
int collision_mask 0
PhysicsDirectSpaceState2D current_space
int damage 0
Vector2 destination
Vector2 deviation_vector
Vector2 direction
Array[RID] excluded_targets []
bool first_hit false
Dictionary[StringName, Variant] global_properties {}
float headstart 0
int index 0
Dictionary[StringName, Variant] individual_properties {}
bool instance_projectile_modifiers false
int instances 0
bool is_active false
bool is_expired false
bool is_linked false
float lifetime 0
float linear_deviation 0
bool lock_to_target false
bool look_at false
Callable on_area_collision
Callable on_body_collision
StringName on_expired_projectile_id
Callable on_expired
StringName on_hit_call
Callable on_move
Callable on_start
int pierce 0
Vector2 position
ProjectileDirectionality proj_directionality 0
ProjectileSpread proj_spread 0
Dictionary[StringName, Projectile2DModifier] projectile_modifiers {}
PhysicsShapeQueryParameters2D query
bool randomize_positions false
bool randomize_spread false
float rehit_cooldown 0
float rehit_lifetime 0
ProjectileBlueprint2D resource
int seeking_mask 0
bool seeking false
float size 0
float spawn_interval 0
float speed 0
Node2D target
Transform2D transform
bool use_custom_area_collision false
bool use_custom_body_collision false
bool use_custom_movement false
float vertical_spread 0
float wait_time 0


Methods



Signals

modifier_requested(_projectile_modifier: Projectile2DModifier)

Emitted when a Projectile2D request the application of a Projectile2DModifier.

Directly requests modifier addition to the ProjectileProcessor2D.


projectile_requested(_projectile: Projectile2D, _position: Vector2, _destination: Vector2, _target: Node2D = null, move_method: Callable = Callable(), start_method: Callable = Callable(), collision_method: Callable = Callable(), expired_method: Callable = Callable())

Emitted when a Projectile2D with secondary projectiles expires.

Directly requests the creation of new projectiles to the ProjectileProcessor2D.



Enumerations

enum ProjectileDirectionality:

  • ProjectileDirectionality STRICT = 0

  • The projectile will head only towards its destination "y" value.

  • ProjectileDirectionality ADAPTATIVE = 1

  • The projectile will head only towards its destination "y" value. It will also change its position to match its destination "y" value.

  • ProjectileDirectionality MODIFIABLE = 2

  • The projectile will head towards its destination vector.


enum ProjectileSpread:


enum ProjectileType:



Property Descriptions

float after_hit_angular_speed = 0

Angular speed used after the first hit (Useful for ricochet behaviors).


bool allow_rehit = false

If true the projectile will be able to hit already hit targets.


float angular_speed = 0

Current rotation speed (in deg/sec) for seeking projectiles.


float angular_spread = 0

Fixed radial extension in which angular spread projectiles will be evenly distributed (in degrees).


bool area_monitoreable = false

If true the projectile can interact with other areas.


Attack2D attack

Current manager (Null if not created by one).


bool can_move = false

Defines if the projectile can move at all.


float cast_radius = 0

Radius of the circle cast shape searching for secondary targets on seeking projectiles.


RID cast_shape

RID used to search for new targets on seeking projectiles.


int collision_layer = 0

Physics layers of the projectile itself.


int collision_mask = 0

Physics layers that can interact with the projectile.


static PhysicsDirectSpaceState2D current_space

Shared physics space state for collision queries.


int damage = 0

Sum of health points that this projectile removes from targets on collision.


Vector2 destination

Fixed projectile destination.


Vector2 deviation_vector

Current projectile direction deviation vector.


Vector2 direction

Current movement direction vector (normalized).


Array[RID] excluded_targets = []

List of already hit RID's for piercing and rehit avoidance.


bool first_hit = false

Flag for first hit behavior tracking.


Dictionary[StringName, Variant] global_properties = {}

This dictionary is shared between all projectiles created by its original blueprint.

If you change its values, you will change them in all their projectiles.


float headstart = 0

Movement advantage applied at the start (in secons).

Simulates projectiles that have already been moving before creation.


int index = 0

Creation identifier for projectiles with multiple instances (0 otherwise).


Dictionary[StringName, Variant] individual_properties = {}

This dictionary is independent in all projectiles created by its original blueprint.

Modifying it will only affect this projectile instance.


bool instance_projectile_modifiers = false

If true the projectile_modifiers dictionary will be independent in all projectile copies made by your ProjectileCaller2Ds instead of being shared among all of them.

This may seem like the optimal behavior but it is recommended to set it to false.


int instances = 0

Amount of projectiles instantiated on creation.


bool is_active = false

Variable state for projectile update validation.


bool is_expired = false

Variable state for projectile lifetime & pierce validation.


bool is_linked = false

If true the projectile itself is connected with the ProjectileProcessor2D and can request and modify projectiles.


float lifetime = 0

Remaining duration in seconds.


float linear_deviation = 0

Random amount of deviation the velocity vector of the projectile can differ once instantiated (in pixels).


bool lock_to_target = false

If true the projectile will ignore all potential collisions in the way to hit its assigned target.


bool look_at = false

If true the projectile will actively look at the direction it travels.


Callable on_area_collision

Projectile custom area collision callback.

Will be called whenever the projectile collides with available areas.


Callable on_body_collision

Projectile custom body collision callback.

Will be called whenever the projectile collides with available bodies.


StringName on_expired_projectile_id

Name of the Secondary projectile on the Global Database to be instantiated once the main projectile runs out of pierce or lifetime.


Callable on_expired

Projectile custom expiration callback.

Will be called once the projectile runs out of pierce or lifetime.


StringName on_hit_call

Name of the method that the projectile will try to call upon collision.


Callable on_move

Projectile custom move callback.

Will be called every physics tick and will actively change the direction the projectile is heading to.


Callable on_start

Projectile custom start callback.

Will be called at projectile creation.


int pierce = 0

Remaining pierces before expiration.


Vector2 position

Current world position.


ProjectileDirectionality proj_directionality = 0

Type of direction the projectile will take once instantiated.


ProjectileSpread proj_spread = 0

Type of spread the projectile instances will have once instantiated.


Dictionary[StringName, Projectile2DModifier] projectile_modifiers = {}

Dictionary of all modifiers affecting this projectile.


PhysicsShapeQueryParameters2D query

Parameters used to search for new targets on seeking shape casting.


bool randomize_positions = false

If true linear and angular projectiles positions will be randomly spread.


bool randomize_spread = false

If true the spread of angular and circular projectiles will be randomized exclusively between its edges.


float rehit_cooldown = 0

Fixed time between consecutive collisions on identical targets.


float rehit_lifetime = 0

Current wait time until next allowed rehit.


ProjectileBlueprint2D resource

Fixed projectile blueprint.


int seeking_mask = 0

The projectile will actively seek targets on these Collision layers. Use it so your projectiles don't seek into walls.


bool seeking = false

If true the projectile will actively follow its targets.


float size = 0

Applied uniform Scale of the projectile.


float spawn_interval = 0

Time between each instance generation.


float speed = 0

Current movement speed in pixels/second.


Node2D target

Current assigned target (Null if no target).


Transform2D transform

Current projectile transform.


bool use_custom_area_collision = false

If true the projectile uses a custom area collision method.


bool use_custom_body_collision = false

If true the projectile uses a custom body collision method.


bool use_custom_movement = false

If true the projectile uses a custom move method.


float vertical_spread = 0

Fixed amount of linear extension in which linear and angular projectiles will be evenly distributed (in pixels).


float wait_time = 0

Time before the projectile enters in action.



Method Descriptions

void _init(_resource: ProjectileBlueprint2D = null, _pi: PackedInfo = null)

Initializes a new projectile instance from the values of the given ProjectileBlueprint2D and PackedInfo parameters.


void activate_waiting_projectile()

Re-enables drawing, collision and movement of waiting projectiles.

Waiting projectiles are automatically activated once their wait_time property is less than zero.


Projectile2D add_global_property(property: StringName, value: Variant)

Returns itself for method chaining.

Adds a new element at the global_properties dictionary, with the given value at the given key property.

var proj_1: Projectile2D = projectile_manager.get_projectile(0)
var proj_2: Projectile2D = projectile_manager.get_projectile(0).clone()

proj_1.add_global_property("KNOCKBACK", 100.0)
print(proj_1.global_properties["KNOCKBACK"]) # Prints (100.0)
print(proj_2.global_properties["KNOCKBACK"]) # Also prints (100.0)

Projectile2D add_individual_property(property: StringName, value: Variant)

Returns itself for method chaining.

Adds a new element at the individual_properties dictionary, with the given value at the given key property.

var proj_1: Projectile2D = projectile_manager.get_projectile(0)
var proj_2: Projectile2D = projectile_manager.get_projectile(0).clone()

proj_1.add_individual_property("ACCELERATION", 100.0)
print(proj_1.individual_properties.get("ACCELERATION", -1)) # Prints (100.0)
print(proj_2.individual_properties.get("ACCELERATION", -1)) # Prints (-1.0) because "ACCELERATION" does not exist here

bool add_modifier(modifier: Projectile2DModifier)

Returns true if modifier was successfully added.

If valid, modifier gets added to projectile_modifiers and emits the modifier_requested signal.

It is strongly recommended to use your ProjectileCaller2D add_projectile_modifier method instead.

var proj: Projectile2D = projectile_manager.get_projectile(0)
proj.add_modifier(Projectile2DModifier.new("PROJECTILE_BUFF", 1.0))


void assign(_pi: PackedInfo)

Starts a projectile with the parameters from the given PackedInfo structure.


Projectile2D clone(_pi: PackedInfo = null)

Returns an identical copy of the projectile.

Optionally initializes it with new PackedInfo parameters.


void copy(base: Projectile2D, _pi: PackedInfo = null)

Copies all properties from the base projectile.

Optionally assigns new PackedInfo parameters to it.


void determine_initial_direction()

Sets the initial projectile direction based on its configuration.


void determine_initial_position()

Sets the initial projectile position based on its configuration.


void disable_collisions()

Cease projectile collisions.


void disable_graphics()

Stops projectile drawing.


void disable_movement()

Makes the projectile unable to move.


void disable()

Disables the projectile and cleans up physics resources.


void draw_on_canvas(_canvas: CanvasItem)

Used for batch drawing.


void enable_collisions()

Allows projectile collisions.


void enable_graphics()

Starts projectile drawing.


void enable_movement()

Makes the projectile able to move.


void expire()

Manages final actions before projectile disposal.

A projectile is considered expired when at least one of these conditions is met:

  • The projectile runs out of lifetime.
  • The projectile runs out of pierce.

Connects both projectile signals to the given ProjectileProcessor2D instance.


void move(delta: float)

Handles projectile movement and seeking rotation logic.

Uses delta as the time elapsed since last frame.


void on_pierced(pierced_rid: RID)

Handles pierce count management and target exclusion logic.

Appends pierced_RID to the list of excluded_targets.


bool remove_modifier(modifier_name: StringName)

Returns true if the modifier with key modifier_name was successfully removed.

Removes a modifier by name from projectile_modifiers and calls its exit() method.

It is strongly recommended to use your ProjectileCaller2D remove_projectile_modifier method instead.

var proj: Projectile2D = projectile_manager.get_projectile(0)
proj.remove_modifier("PROJECTILE_BUFF")

void request_projectile(_projectile: Projectile2D, _position: Vector2, _destination: Vector2, _target: Node2D = null, _move_method: Callable = Callable(), _start_method: Callable = Callable(), _collision_method: Callable = Callable(), _expired_method: Callable = Callable())

Requests the creation of new projectiles through the projectile_requested signal.

Allows custom callbacks to modify projectile behavior.


void reset()

Resets the projectile to its initial state using its blueprint.

Clears all runtime modifications and custom properties.


float seek_target(_delta: float)

Returns the applied and constraint seeking rotation angle. It also adjusts the projectile direction towards the current target.


Projectile2D set_on_area_collision(method: Callable)

Returns itself for method chaining.

Sets on_area_collision callback.

  • The given method must follow a define pattern. It must return void and take the following parameters:

    proj: A representation of the projectile object to be changed.

    area_rid: The RID of the area that entered the projectile.

    area_node: The Node of the area that entered the projectile.

    target_node: The main processor Node of the object that collided with the projectile. Learn more here.

    area_shape_index: Index of the interacting shape from the collidiong area.

    local_shape_index: Index of the interacting shape from the projectile area.

    extends Node2D
    
    # The projectiles will immediately expire upon collision with objects in this layer.
    @export_flags_2d_physics var wall_layer: int
    @onready var projectile_caller = $ProjectileCaller2D
    						
    
    # In this example the "custom_collision" function is set at the projectile with index (0).
    func _ready() -> void:
    	projectile_caller.get_projectile(0).set_on_area_collision(custom_collision)
    
    
    # This function will change the way projectiles collide.
    func custom_collision(proj: Projectile2D, area_rid: RID, area_node: Node2D, target_node: Node2D, area_shape_index: int, local_shape_index: int) -> void:
    	# The "validate_collision" method makes sure that:
    	# -> The projectile is not expired.
    	# -> A lock projectile will only collide with its assigned target.
    	# -> The projectile is not colliding with areas it already collided.
    	if not proj.validate_collision(area_rid, area_node):
    		return
    	
    	# Custom code snippet for immediate expiration on collision with "wall_layer"
    	if wall_layer & area_node.collision_layer != 0:
    		proj.is_expired = true
    		return
    	
    	# The "on_pierced" method makes sure that:
    	# -> The projectile refreshes its rehit cooldown and angular speed.
    	# -> The projectile appends colliding areas to its excluded targets.
    	# -> The projectile instance reduces its remaining pierce.
    	proj.on_pierced(area_rid)

Projectile2D set_on_body_collision(method: Callable)

Returns itself for method chaining.

Sets on_body_collision callback.

  • The given method must follow a define pattern. It must return void and take the following parameters:

    proj: A representation of the projectile object to be changed.

    body_rid: The RID of the body that entered the projectile.

    body_node: The Node of the body that entered the projectile.

    target_node: The main processor Node of the object that collided with the projectile. Learn more here.

    body_shape_index: Index of the interacting shape from the collidiong body.

    local_shape_index: Index of the interacting shape from the projectile area.

    extends Node2D
    
    # The projectiles will immediately expire upon collision with objects in this layer.
    @export_flags_2d_physics var wall_layer: int
    @onready var projectile_caller = $ProjectileCaller2D
    						
    
    # In this example the "custom_collision" function is set at the projectile with index (0).
    func _ready() -> void:
    	projectile_caller.get_projectile(0).set_on_body_collision(custom_collision)
    
    
    # This function will change the way projectiles collide.
    func custom_collision(proj: Projectile2D, body_rid: RID, body_node: Node2D, target_node: Node2D, body_shape_index: int, local_shape_index: int) -> void:
    	# The "validate_collision" method makes sure that:
    	# -> The projectile is not expired.
    	# -> A lock projectile will only collide with its assigned target.
    	# -> The projectile is not colliding with bodies it already collided.
    	if not proj.validate_collision(body_rid, body_node):
    		return
    	
    	# Custom code snippet for immediate expiration on collision with "wall_layer"
    	if wall_layer & body_node.collision_layer != 0:
    		proj.is_expired = true
    		return
    	
    	# The "on_pierced" method makes sure that:
    	# -> The projectile refreshes its rehit cooldown and angular speed.
    	# -> The projectile appends colliding bodies to its excluded targets.
    	# -> The projectile instance reduces its remaining pierce.
    	proj.on_pierced(body_rid)

Projectile2D set_on_collision(method: Callable)

Returns itself for method chaining.

Sets the same callback for both on_area_collision and on_body_collision properties.

See set_on_area_collision and set_on_body_collision.


Projectile2D set_on_expired(method: Callable)

Returns itself for method chaining.

Sets on_expired callback.

  • The given method must follow a define pattern. It must return void and take the following parameter:

    proj: A representation of the projectile object to be changed.

    extends Node2D
    
    @onready var projectile_caller = $ProjectileCaller2D
    						
    
    # In this example the "custom_expired" function is set at the projectile with index (0).
    func _ready() -> void:
    	projectile_caller.get_projectile(0).set_on_expired(custom_expired)
    
    
    # This function will print the projectile "damage" once it runs out of pierce or lifetime.
    func custom_expired(proj: Projectile2D) -> void:
    	print(proj.damage)

Projectile2D set_on_move(method: Callable)

Returns itself for method chaining.

Sets on_move callback.

  • The given method must follow a define pattern. It must return a Vector2 (as an element representing the direction in which the projectile will move) and take the following two parameters:

    proj: A representation of the projectile object to be changed.

    delta: The time elapsed in seconds since the previous call to move_method.

    extends Node2D
    
    @onready var projectile_caller = $ProjectileCaller2D
    						
    
    # In this example the "custom_movement" function is set at the projectile with index (0).
    func _ready() -> void:
    	projectile_caller.get_projectile(0).set_on_move(custom_movement)
    
    
    # This function will cause the projectile to always move in the UP direction.
    func custom_movement(proj: Projectile2D, _delta: float) -> Vector2:
    	return Vector2.UP

Projectile2D set_on_start(method: Callable)

Returns itself for method chaining.

Sets on_start callback.

  • The given method must follow a define pattern. It must return void and take the following parameter:

    proj: A representation of the projectile object to be changed.

    extends Node2D
    
    @onready var projectile_caller = $ProjectileCaller2D
    						
    
    # In this example the "custom_start" function is set at the projectile with index (0).
    func _ready() -> void:
    	projectile_caller.get_projectile(0).set_on_start(custom_start)
    
    
    # This function will change the projectile "speed" property at projectile creation.
    func custom_start(proj: Projectile2D) -> void:
    	proj.speed = 50

Projectile2D set_property(property: StringName, value: Variant)

Returns itself for method chaining.

Assigns value to the given projectile property. If the projectile property does not exist or the given value's type doesn't match, nothing happens.

var proj: Projectile2D = projectile_manager.get_projectile(0)
proj.set_property("speed", 1000)
print(proj.speed) # Prints (1000.0)

void try_retarget()

Attempts to find new targets using shape casting when the current target is lost.


bool update_lifetime(delta: float)

Returns true if the projectile remains active, false if expired.


bool validate_collision(colliding_rid: RID, colliding_node: Node)

Returns true if the collision is valid.

A collision is valid if:

  • The projectile is not expired.
  • The projectile is not colliding with areas/bodies it already collided.
  • A lock target projectile is colliding with its assigned target.

Node validate_target(collider: Node)

Returns the validated target node (may be a parent of the original collider).

Seeking projectiles target the global position of the Collision Objects they home in. If you want to seek a parent position, add your Body/Area nodes into the Group "step"