Godot Version
4.5
Question
I have some code in Godot 4.5 3D that will add a trail effect to an object when the object moves. The problem is, in some cases I don’t want to use the global movement, and instead want to use the movement relative to the parent object. For example on a sword object, I want the trail to follow the movement of the sword itself when it’s swinging, but not be affected by the movement of the player. Right now if I swing the sword while moving forward, the player will walk into the trail as it’s being created. If I walk to the right while swinging to the left, the trail left behind will almost be non-existent because the walking and swinging movement cancels each other out. I tried replacing code that uses get_global_transform( ) with get_transform( ) but that doesn’t seem to work. Is there an easy solution for this?
Here is the code:
class_name Trail3D extends MeshInstance3D
var _points = [] # Stores all 3D positions that will make up the trail
var _widths = [] # Stores all calculated widths using the positions of the points
var _lifePoints = [] # Stores all the trail points lifespans
@export var _trailEnabled : bool = true ## Set trail visibility
@export var _fromWidth : float = 0.5 ## Starting width of the trail
@export var _toWidth : float = 0.0 ## End width of the trail
@export_range(0.5, 1.5) var _scaleAcceleration : float = 1.0 ## Speed of the scaling
@export var _motionDelta : float = 0.1 ## Sets the smoothness of the trail, how long it will take for a new trail piece to be made
@export var _lifespan : float = 1.0 ## Sets the duration until this part of the trail is no longer used
@export var _startColor : Color = Color(1.0, 1.0, 1.0, 1.0) ## Starting color of the trail
@export var _endColor : Color = Color(1.0, 1.0, 1.0, 1.0) ## End color of the trail
var _oldPos : Vector3 # Get the previous position
func _ready(): # Set the current position and create the trail mesh
_oldPos = get_global_transform().origin
mesh = ImmediateMesh.new() # Mesh used for creating geometry manually
AppendPoint()
func _process(delta): # Update the trail
# If the distance between the previous position and the current position is more than the spawn threshhold
# trails are allowed to be made:
if (_oldPos - get_global_transform().origin).length() > _motionDelta and _trailEnabled:
AppendPoint() # Create a new point
_oldPos = get_global_transform().origin # Update the old position to the current position
# Update the lifespan of every point, remove points when lifespan ends
var p = 0
var max_points = _points.size()
while p < max_points: # Loop through every trail point
_lifePoints[p] += delta
if _lifePoints[p] > _lifespan: # If the lifespan of a point has ended, remove it from the list
RemovePoint(p)
p -= 1
if (p < 0): p = 0 # Make sure p isn't set to anything less than zero
max_points = _points.size()
p += 1
# Remove all surfaces from the mesh
mesh.clear_surfaces()
#If there are no more than 2 points, return and don't render the trail
if _points.size() < 2:
return
# Render a new mesh based on the positions of each point and their current width
mesh.surface_begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
for i in range(_points.size()):
var t = float(i) / (_points.size() - 1.0)
var currentColor = _startColor.lerp(_endColor, 1 - t)
mesh.surface_set_color(currentColor)
var currentWidth = _widths[i][0] - pow(1 - t, _scaleAcceleration) * _widths[i][1]
var t0 = i / _points.size()
var t1 = t
mesh.surface_set_uv(Vector2(t0,0))
mesh.surface_add_vertex(to_local(_points[i] + currentWidth))
mesh.surface_set_uv(Vector2(t1,1))
mesh.surface_add_vertex(to_local(_points[i] - currentWidth))
mesh.surface_end() # Set the mesh surface, making it visible
func AppendPoint(): # Set the trail points based on the set width, as well as the lifetime
_points.append(get_global_transform().origin)
_widths.append([
get_global_transform().basis.x * _fromWidth,
get_global_transform().basis.x * _fromWidth - get_global_transform().basis.x * _toWidth])
_lifePoints.append(0.0)
func RemovePoint(p): # Remove specified trail point
_points.remove_at(p)
_widths.remove_at(p)
_lifePoints.remove_at(p)