It is time to round out our laser beam’s code to use our new effects.
Go to the script editor and open the LaserBeam2D.gd script.
At the top of the code, add three new onready variables to reference our three particle nodes, CastingParticles2D, CollisionParticles2D, and BeamParticles2D.
Add these lines below the line onready var tween := $Tween
.
onready var casting_particles := $CastingParticles2D onready var collision_particles := $CollisionParticles2D onready var beam_particles := $BeamParticles2D
To control the particles’ emission, we are going to use the is_casting
property inside the set_is_casting()
method.
func set_is_casting(cast: bool) -> void: # [...] set_physics_process(is_casting) casting_particles.emitting = is_casting beam_particles.emitting = is_casting
The complete function should look like this:
func set_is_casting(cast: bool) -> void: is_casting = cast if is_casting: cast_to = Vector2.ZERO fill.points[1] = cast_to appear() else: collision_particles.emitting = false disappear() set_physics_process(is_casting) beam_particles.emitting = is_casting casting_particles.emitting = is_casting
Next, we are going to make the CollisionParticles2D emit when the beam is colliding. We also have to move the node to the collision point. For that, inside the cast_beam()
method, use the cast_point
as the particles’ position.
Also, change the particles’ global rotation based on the collision normal. You can do so using the get_collision_normal()
method from RayCast2d
. It gives us a normalized vector that we can convert to an angle, using the method Vector2.angle()
.
func cast_beam() -> void: # [...] collision_particles.emitting = is_colliding() if is_colliding(): cast_point = to_local(get_collision_point()) collision_particles.global_rotation = get_collision_normal().angle() collision_particles.position = cast_point # [...]
We finally need to extend the BeamParticles2D’s emission box to cover the entire beam’s length. The box works with extents, that is to say, half the size of a rectangle that expands in two directions. We have to always keep the particle system in the middle of the beam. Set its position to half of the cast_point
vector, and use half the beam’s length for the Emission Shape extents x
value.
Add these lines at the bottom of the cast_beam()
function.
beam_particles.position = cast_point * 0.5 beam_particles.process_material.emission_box_extents.x = cast_point.length() * 0.5
The function should look like this:
func cast_beam() -> void: var cast_point := cast_to force_raycast_update() collision_particles.emitting = is_colliding() if is_colliding(): cast_point = to_local(get_collision_point()) collision_particles.global_rotation = get_collision_normal().angle() collision_particles.position = cast_point fill.points[1] = cast_point beam_particles.position = cast_point * 0.5 beam_particles.process_material.emission_box_extents.x = cast_point.length() * 0.5
And with that, you can pat yourself in the back: you’re done. You have a terrifying laser to blast your enemies… or the player! It’s your choice.
Here is the complete LaserBeam2d.gd
class for reference:
extends RayCast2D export var cast_speed := 7000.0 export var max_length := 1400 export var growth_time := 0.1 var is_casting := false setget set_is_casting onready var fill := $FillLine2D onready var tween := $Tween onready var casting_particles := $CastingParticles2D onready var collision_particles := $CollisionParticles2D onready var beam_particles := $BeamParticles2D onready var line_width: float = fill.width func _ready() -> void: set_physics_process(false) fill.points[1] = Vector2.ZERO func _physics_process(delta: float) -> void: cast_to = (cast_to + Vector2.RIGHT * cast_speed * delta).clamped(max_length) cast_beam() func set_is_casting(cast: bool) -> void: is_casting = cast if is_casting: cast_to = Vector2.ZERO fill.points[1] = cast_to appear() else: collision_particles.emitting = false disappear() set_physics_process(is_casting) beam_particles.emitting = is_casting casting_particles.emitting = is_casting func cast_beam() -> void: var cast_point := cast_to force_raycast_update() collision_particles.emitting = is_colliding() if is_colliding(): cast_point = to_local(get_collision_point()) collision_particles.global_rotation = get_collision_normal().angle() collision_particles.position = cast_point fill.points[1] = cast_point beam_particles.position = cast_point * 0.5 beam_particles.process_material.emission_box_extents.x = cast_point.length() * 0.5 func appear() -> void: if tween.is_active(): tween.stop_all() tween.interpolate_property(fill, "width", 0, line_width, growth_time * 2) tween.start() func disappear() -> void: if tween.is_active(): tween.stop_all() tween.interpolate_property(fill, "width", fill.width, 0, growth_time) tween.start()