Is there a way to preprocess AnimatableBody2D and/or Path2D?

Godot Version

4.4.1.stable

Question

Background:
The particle system in Godot has this awesome feature called “preprocess” which does exactly what it says: pre-processes the particles when the node is added to the SceneTree so that they appear to show up some amount of seconds into the effect/animation. This avoids the “particle startup” effect that may be undesirable at times (moving backgrounds, land assets like water, things you would stumble upon in a world rather than initiate yourself, etc.)

My issue:
I have some moving platforms which include in their scene:

  • AnimatableBody2D
    — Sprite2D
    — CollisionShape2D
  • AnimationPlayer
  • Path2D
    —PathFollow2D
    -----RemoteTransform2D (to AnimatableBody2D)

The AnimationPlayer controls how the AnimatableBody and its children move along the Path2D’s PathFollow2D by setting its progress_ratio at different time intervals, and I just loop them to move continuously. With this, I can control the position of the platform along its path at any given time, as long as I set it up that way in the animation timeline editor.

If I put a bunch of the same moving platforms in one scene with the same animation, they of course all move up/down with each other, and I’d often rather have them move differently. I could of course just set up multiple animations, but if there was an inspector function to just preprocess some of them to different amounts of time, I wouldn’t need to do that. I suppose I could think up a script function that sets the start time and assign that to an export variable, though I’d rather use the existing method if it exists.

You would have to use .seek on your AnimationPlayer, the export sounds like a good route

1 Like

I’d just code it. I’ve never played with moving platforms myself, but I would probably give them the ability to move themselves without an AnimationPlayer. Something like:

@export var starting_position: Vector2
@export var length_of_travel: float = 500.0 #pixels
@export var travel_time: float = 1.0 #seconds


func _ready() -> void:
	if not starting_position:
		starting_position = global_position
	start_animation()


func start_animation() -> void:
	var tween: Tween = create_tween() # Creates a new tween

	# Setup start animation if it's not the same as where you placed it.
	if  starting_position != global_position:
		var initial_position = global_position
		global_position = starting_position
		var initial_length_of_travel = initial_position.x + length_of_travel - starting_position.x
		var initial_travel_time = travel_time * (initial_length_of_travel / length_of_travel)
		tween.tween_property(self, "position:x", starting_position + initial_length_of_travel, initial_travel_time)
		starting_position = initial_position
		tween.tween_property(self, "position:x", starting_position, travel_time)	

	animate()


func animate() -> void:
	var tween: Tween = create_tween() # Creates a new tween
	
	# Change position.x + 500 over x seconds:
	tween.tween_property(self, "position:x", starting_position.x + length_of_travel, travel_time)
	tween.tween_property(self, "position:x", starting_position.x, travel_time)	
	animate()
1 Like

I got it with an export since the inspector function didn’t pre-exist.

## Constants
const VerticalPath = preload("res://vertical_path.tres")
const HorizontalPath = preload("res://horizontal_path.tres")


## Variables (export, normal, onready)
@export_enum("Vertical","Horizontal") var path_type = "Vertical"
@export_enum("One-way", "Two-way") var loop_type = "Two-way"
@export_range(0.0,4.0) var speed_scale = 0.4
@export_range(0.0,1.0) var preprocess = 0.0

@onready var animation_player = $AnimationPlayer
@onready var path_2d = $Path2D
@onready var path_follow_2d = $Path2D/PathFollow2D


## Functions
func _ready():
	if path_type == "Vertical":
		path_2d.curve = VerticalPath
	if path_type == "Horizontal":
		path_2d.curve = HorizontalPath
	
	animation_player.play(loop_type)
	
	animation_player.set_speed_scale(speed_scale)
	
	var animation_length = animation_player.get_current_animation_length()
	path_follow_2d.progress_ratio = preprocess
	animation_player.seek(animation_length * preprocess)

Works perfectly! I also added in some other parameters to mix things up.

1 Like