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
- Modifying Projectiles through code
- Dealing damage and other interactions
- Creating your own Projectile class
Properties
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
ProjectileDirectionality ADAPTATIVE = 1
ProjectileDirectionality MODIFIABLE = 2
The projectile will head only towards its destination "y" value.
The projectile will head only towards its destination "y" value. It will also change its position to match its destination "y" value.
The projectile will head towards its destination vector.
enum ProjectileSpread:
ProjectileSpread NONE = 0
ProjectileSpread LINEAR = 1
ProjectileSpread ANGULAR = 2
ProjectileSpread CIRCULAR = 3
ProjectileSpread EVENLY = 4
Projectile instances will not spread.
Projectile instances will evenly spread in the “y” axis by the vertical_spread size.
Projectile instances will evenly spread inside the angular_spread angle.
Projectile instances will evenly spread in a radial manner.
Projectile instances will evenly distribute to its set targets.
enum ProjectileType:
ProjectileType AREA = 0
ProjectileType INSTANTIATED = 2
The most basic type of projectile. AreaProjectile2Ds are created, processed and manipulated entirely through the PhysicServer2D and are batch drawn by the ProjectileProcessor2D.
AreaProjectile2Ds are extremely performance efficient but are restricted to a single Texture2D and cannot have individual components.
InstancedProjectile2Ds are instanced copies of your given Scenes. These contain all their original components and scripts, but follow the rules of normal projectiles.
InstancedProjectile2Ds are more performance intensive than AreaProjectile2Ds but not significantly, you would have to generate thousands to see a clear difference.
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
- void set_transform_2D(value: Transform2D)
- Transform2D get_transform_2D()
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.
void link_to_processor(processor_instance: ProjectileProcessor2D)
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 returnvoid
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 returnvoid
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 returnvoid
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 aVector2
(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 returnvoid
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"