In this part, we’re going to set up the scenes we need to design the lightning effect. By the end of the lesson, we’ll have a skeleton for the effect. We need three scenes:
We’ll build on these as we go, but let’s create stripped-down versions of them first so we can see how they interact with each other.
Create a new scene using a Line2D as the scene root and rename it LightningJolt. Add an AnimationPlayer and a Particles2D as children. I renamed the Particles2D to Sparks and added a script to LightningJolt. Be sure to save the scene. I saved it in res://LightningBeam/LightningJolt.tscn
.
We’ll revisit the LightningJolt.gd
script later, but let’s cache our Sparks particles and define a blank create
function for now:
extends Line2D onready var sparks := $Sparks func create(start : Vector2, end : Vector2) -> void: pass
A lightning bolt is going to be a collection of jolts. We’ll call the above create()
function in the LightningBeam.gd
script, so let’s move onto the LightningBeam scene.
Create a new scene with a RayCast2D
as the root and attach a script. I renamed it LightningBeam.
There’s not too much to change in the settings: make sure it’s enabled and have it cast to the right. The cast_to
is the range of the weapon. I set it to 600 pixels.
We need the lightning beam script to update the target point of the effect and create as many jolts we desire. To that end, we define several variables:
flashes
dictates how many jolts instances we create when firing the weapon.flash_time
is the time interval between flashes.lightning_jolt
is a reference to the LightningJolt.tscn
packed scene. We load it at compile time for efficiency.target_point
is the jolt’s end position.We’ll be using the _physics_process()
and shoot()
functions in the future, so we add them here.
extends RayCast2D export (int, 1, 10) var flashes := 3 export (float, 0.0, 3.0) var flash_time := 0.1 export var lightning_jolt: PackedScene = preload(_res://LightningBeam/LightningJolt.tscn_) var target_point := Vector2.ZERO func _physics_process(delta) -> void: pass func shoot() -> void: pass
For demonstration and testing, create a new scene with a Node2D
as the root and call it “PlayerShip”. Add a script and a Sprite
as a child.
Add a suitable ship sprite and rotate it so it points to the right to align it with the lightning beam’s direction.
Speaking of the lightning beam scene, we add an instance of it as a child by clicking the chain symbol below the Scene tab.
In the PlayerShip.gd
script, we cache the lightning beam and update the ship’s rotation so it looks at the mouse cursor in the _process()
function. Note that this also rotates the lightning beam because children inherit their parent’s rotation and position.
In the _unhandled_input()
function we check if the ui_accept
action is pressed. By default, this action corresponds to space or the return key. If it’s pressed, we call the shoot function of the LightningBeam.
extends Node2D onready var lightning := $LightningBeam func _process(_delta: float) -> void: look_at(get_global_mouse_position()) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("ui_accept"): lightning.shoot()
You can view other inputs by going to Project > Project Settings… > Input Map.
Move the player ship away from the viewport’s top-left corner and run the scene. If you move your cursor, you’ll see the ship follows it. However, pressing space does nothing yet because the functions are empty.
We’ve set up all our scenes and boilerplate code to handle interactions between them. In the next lesson, we’ll dive into the LightningJolt.gd
script to generate the shape of the effect and eventually fire it!