It’s weird that particles don’t work for you, CPUParticles2D should work on all platforms.
Anyway, wanted to try achieve this effect in pure GDScript with elongating trails and dynamic opacity, so here my code:
extends Node2D
@onready var screen_size : Vector2 = get_viewport_rect().size
const SCR_MARGIN = 32 #how far away from screen borders to generate stars on x-axis
const STAR_COUNT = 64
const STAR_TRAIL = 300 #lenght of trail will be multiplied by star_pos.z
const MAX_SPEED : float = 1600.0
var offscreen_y : float #value for checking if star is offscreen on y axis, needs to be recalculated if viewport size changes
var star_pos : PackedVector3Array #array of stars position and speed modifier "z" value
var current_speed : float = 0.0
var delta_accumulator : float = 0.0
const ACCELERATION_TIME : float = 2.0 #time for full acceleration/deceleration in seconds
var speed_ease : float #calculated eased speed modifier
var acceleration_state : float = 1.0 #1.0 accelerating, -1.0 decelerating
func _ready() -> void:
star_pos.resize(STAR_COUNT)
offscreen_y = screen_size.y + (STAR_TRAIL * 1.5) #1.5 must be equal or more than max value of star_pos.z
stars_init()
func _process(delta: float) -> void:
if Input.is_action_just_pressed("ui_accept"): acceleration_toggle()
if Input.is_action_just_pressed("ui_cancel"): stars_init()
delta_accumulator += (delta * acceleration_state)
speed_ease = ease((delta_accumulator / ACCELERATION_TIME), 2.4) #2.4 feels like best fit
current_speed = MAX_SPEED * speed_ease
if !current_speed: return #no need to calculate positions if speed is zero
for i in star_pos.size():
var pos_y = star_pos[i].y + (current_speed * star_pos[i].z) * delta
if pos_y > offscreen_y:
star_pos[i].y -= offscreen_y
star_pos[i].x = randf_range(SCR_MARGIN, screen_size.x -SCR_MARGIN)
else: star_pos[i].y = pos_y
queue_redraw()
func _draw() -> void:
if !current_speed: return # no need to draw if speed is zero
for i in star_pos.size():
var pos_start = Vector2(star_pos[i].x, star_pos[i].y)
var pos_end = Vector2(star_pos[i].x, star_pos[i].y - STAR_TRAIL*(speed_ease*star_pos[i].z))
draw_line( pos_start, pos_end, Color(1.0,1.0,1.0,current_speed/MAX_SPEED), 1.0, false) #antialiasing disabled for performance
func stars_init():
delta_accumulator = 0.0
current_speed = 0.0
acceleration_state = 1.0
for i in star_pos.size():
star_pos[i].x = randf_range(SCR_MARGIN, screen_size.x -SCR_MARGIN)
star_pos[i].y = randf_range(screen_size.y * -1.0, 0.0)
star_pos[i].z = randf_range(0.5, 1.5)
func acceleration_toggle() -> void:
if delta_accumulator > ACCELERATION_TIME: delta_accumulator = ACCELERATION_TIME
elif delta_accumulator < 0.0: delta_accumulator = 0.0
acceleration_state *= -1.0
Controls with default input layout:
ENTER - accelerate/decelerate
ESCAPE- reinitialize stars position