Common Projectiles examples

Here is a collection of regularly requested Projectiles and their implementation in All Projectiles.

The list will be continuously updated as requested.


Variable Speed over Lifetime

extends  Node2D

@onready var projectile_caller: ProjectileCaller2D = $ProjectileCaller2D


func _ready() -> void:
	projectile_caller.get_projectile(0).set_on_move(custom_update)


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())


func custom_update(proj: Projectile2D, _delta: float) -> Vector2:
	var delta: float = (proj.resource.lifetime - proj.lifetime) / proj.resource.lifetime
	var curve: Curve = proj.global_properties.get("SPEED_CURVE")
	if (curve != null):
		proj.speed = proj.resource.linear_speed * curve.sample(delta)

	return proj.direction

Bouncing Projectile

extends  Node2D

@onready var projectile_caller: ProjectileCaller2D = $ProjectileCaller2D


func _ready() -> void:
	projectile_caller.get_projectile(0).set_on_collision(custom_collision)


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())


func custom_collision(proj: Projectile2D, area_rid: RID, area_node: Node2D, target_node: Node2D, area_shape_index: int, local_shape_index: int) -> void:
	if not proj.validate_collision(area_rid, area_node):
		return
	
	var wall_layer: int = proj.global_properties["WALL_LAYER"]
	if wall_layer & area_node.collision_layer != 0:

		var query: PhysicsRayQueryParameters2D = PhysicsRayQueryParameters2D.create(proj.position - (proj.direction * 10), target_node.global_position, proj.collision_mask)
		query.collide_with_areas = true;
		var result: Dictionary = proj.current_space.intersect_ray(query)
		if (result):
			var prev: float = proj.direction.angle()
			proj.direction = proj.direction.bounce(result.normal)
			if (proj.look_at):
				proj.transform = proj.transform.rotated(proj.direction.angle() - prev)
			return

		proj.is_expired = true
		return

	if target_node.has_method(proj.on_hit_call):
		target_node.call(proj.on_hit_call, proj)
	proj.on_pierced(area_rid)