What I'm Trying to Do
I'm creating a 2D game with a spaceship (RigidBody2D) that has a thruster trail using a Trail2D node. I'm trying to make the trail behave a certain way.
Expected Result
The trail should :
- Appear at the back of the ship.
- Show only when thrusting forward (ui_up pressed).
- Fade from opaque blue (#0080FFFF) to transparent (#0080FF00) using a GradientTexture1D.
- Follow the ship's rotation (trail points emit behind based on orientation).
- Scale in length with ship speed (longer at higher speeds).
- Shouldn't clip through the ship nor overlap with itself.
What’s Happening Instead
If I reposition the trail to the side, the trail doesn't stick to the ship and instead floats next to it. Also, the trail clips through the ship and overlaps with itself which is not intended.
What I’ve Tried
I've played around with the settings of Trail2D, trying to change it's position but it never sticks to the ship (rather I had to make the ship stick to the trail as a 'workaround'). Am I even using the right tool?
Spaceship code:
extends RigidBody2D
@export var rotation_speed: float = 2.0 # Radians per second for rotation
@export var thrust_force: float = 500.0 # Force for thrust (in Newtons)
@export var max_speed: float = 400.0 # Maximum speed in pixels per second
@export var acceleration: float = 200.0 # Acceleration rate for thrust
@export var friction: float = 50.0 # Friction to slow down when not thrusting
var velocity: Vector2 = Vector2.ZERO # Track velocity for clamping
@onready var thruster_trail: Line2D = $ThrusterTrail # Reference to Line2D node (rename if different)
func _ready() -> void:
body_entered.connect(_on_body_entered)
if thruster_trail == null:
push_error("ThrusterTrail node not found! Check scene tree.")
else:
thruster_trail.active = false # Off by default
func _physics_process(delta: float) -> void:
# Rotation
if Input.is_action_pressed("ui_left"):
angular_velocity = -rotation_speed
elif Input.is_action_pressed("ui_right"):
angular_velocity = rotation_speed
else:
angular_velocity = 0.0
# Thrust (forward/backward)
var direction: Vector2 = Vector2(cos(rotation - PI/2), sin(rotation - PI/2))
var thrust_input: float = 0.0
if Input.is_action_pressed("ui_up"):
thrust_input += 1.0 # Forward thrust
if Input.is_action_pressed("ui_down"):
thrust_input -= 1.0 # Backward thrust
# Apply thrust force
if thrust_input != 0.0:
var force: Vector2 = direction * thrust_input * acceleration
apply_force(force)
# Clamp speed
velocity = linear_velocity
if velocity.length() > max_speed:
linear_velocity = velocity.normalized() * max_speed
else:
# Apply friction
velocity = linear_velocity
var speed: float = velocity.length()
if speed > 0:
var friction_force: float = friction * delta
if friction_force > speed:
linear_velocity = Vector2.ZERO
else:
linear_velocity -= velocity.normalized() * friction_force
# Trail activation for forward thrust
if thruster_trail != null:
thruster_trail.active = Input.is_action_pressed("ui_up")
func _on_body_entered(body: Node) -> void:
print("Collision detected with: ", body.name)
Trail code:
extends Line2D
@export_category('Trail')
@export var length : = 10
@onready var parent : Node2D = get_parent()
var offset : = Vector2.ZERO
func _ready() -> void:
offset = position
top_level = true
func _physics_process(_delta: float) -> void:
global_position = Vector2.ZERO
var point : = parent.global_position + offset
add_point(point, 0)
if get_point_count() > length:
remove_point(get_point_count() - 1)