Add Line2D showing a RigidBody2D's intended travel path

Godot Version

3.5.3 stable

Question

I am trying to draw a line showing the path that a RigidBody2D will travel once forces are applied to it. The idea is that the player character is clicked on and dragged in the opposite direction (like pulling back a slingshot), then released; based on how far the mouse was dragged away from the player, the further they will travel.
The player already travels in an arc, but I would like to add a line showing where they will land once the player releases it.
Right now I am trying the code from this post but the values it is setting are too extreme and don’t correlate to an arc. Is there a better way of doing this?

var pos = position
		var velocity = Vector2.UP.rotated(rotation) * ($StrengthMeter.get_cast_to().y * 10)
		for i in range(20):
			$SwordThrowIndicator.add_point(pos - global_position, i) 
			velocity *= 0.1
			pos += velocity

Right now it draws lines over and over again until it hits the limit and clears it out, but I’m just trying to get them to arc according to the player’s velocity for now.

Velocity is just a direction. It will change over time due to gravity. So in order to curve a line you need to calculate physics over a distance and time.

You got to add a gravity factor to your for loop. You only slow the velocity down but it will not curve like that.

velocity += gravity*change_in_time

I’ve tried to adapt what you suggested but now it’s just drawing a long straight line in a single direction regardless of where it’s rotated.

var pos = position
var startTime = OS.get_unix_time()
var currentTime = 0
var velocity = Vector2.UP.rotated(rotation) * ($StrengthMeter.get_cast_to().y * 10)
for i in range(20):
	$SwordThrowIndicator.add_point(pos - global_position, i) 
	velocity.x += (ProjectSettings.get_setting("physics/2d/default_gravity") * (currentTime - startTime))
	velocity.y += (ProjectSettings.get_setting("physics/2d/default_gravity") * (currentTime - startTime))
	pos += velocity
	currentTime = OS.get_unix_time()

Juding by these points it’s adding I don’t think using UNIX time is the best method of getting elapsed time.

(0, 0)
(-167276560384, -167276560384)
(-334553120768, -334553120768)
(-501829664768, -501829664768)
(-669106241536, -669106241536)
(-836382818304, -836382818304)
(-1003659395072, -1003659395072)
(-1170935906304, -1170935906304)
(-1338212483072, -1338212483072)
(-1505489059840, -1505489059840)
(-1672765636608, -1672765636608)
(-1840042213376, -1840042213376)
(-2007318790144, -2007318790144)
(-2174595366912, -2174595366912)
(-2341871812608, -2341871812608)
(-2509148258304, -2509148258304)
(-2676424704000, -2676424704000)
(-2843701149696, -2843701149696)
(-3010977595392, -3010977595392)
(-3178254041088, -3178254041088)

I would use the Node function get_process_delta_time or get_physics_process_delta_time this will return a float typically seen in the _process(delta) and _physics_process(delta) respectively. if your frame rate is set to 60 fps, then this function will return a value of around 0.016, and that should be enough to run through your for loop to quickly simulate the physics and draw the trajectory line in one frame.

Using get_physics_process_delta_time still returns high values for the line, and they’re still straight lines.

for i in range(20):
	$SwordThrowIndicator.add_point(pos - global_position, i) 
	velocity.x += (ProjectSettings.get_setting("physics/2d/default_gravity") * get_physics_process_delta_time())
	velocity.y += (ProjectSettings.get_setting("physics/2d/default_gravity") * get_physics_process_delta_time())
	pos += velocity
(0, 0)
(184.273804, -157.248047)
(370.180908, -312.862793)
(557.721375, -466.844208)
(746.895142, -619.192261)
(937.702271, -769.907043)
(1130.1427, -918.988464)
(1324.216553, -1066.436523)
(1519.923706, -1212.251343)
(1717.26416, -1356.432739)
(1916.238037, -1498.980835)
(2116.845215, -1639.89563)
(2319.085693, -1779.177002)
(2522.959473, -1916.825195)
(2728.466553, -2052.839844)
(2935.606934, -2187.221436)
(3144.380615, -2319.969482)
(3354.787842, -2451.084229)
(3566.828369, -2580.565674)
(3780.502197, -2708.413818)

Mathematics are correct.

The general equation is like this

pos_new = pos_initial + gravity * delta_time

So what is the velocity?

Looking at the relative changes the velocity.y step is ~ -155. Doing the inverse math we should expect to see around -980. Which is the default gravity for 2d projects.

Doing the same for velocity.x step we I see a step of +187 is the x velocity. ( update: the inverse math ends up being 1168 I missed this)

Both seem like expected outcomes.

I think now is a scaling problem. Maybe you should modify the velocity to fit your game view or change the view to fit your velocities.

Oh I missed the other issue.

tldr

remove the x axis gravity calculation.

explanation

The problem I see is that you apply gravity per axis. First to x then to y. It technically should only apply to y axis. But maybe you want gravity to change directions. In order to do that you should use vector math. It’s also cleaner.

Godot has a good write up on this:

velocity += Vector2(0.0, ProjectSettings.get_setting("physics/2d/default_gravity")) * get_physics_process_delta_time()

And instead of using ProjectSettings.get_setting("physics/2d/default_gravity") every where, you can cache it in a global class variable

var project_gravity_scalar:float = ProjectSettings.get_setting("physics/2d/default_gravity")
var gravity_velocity :Vector2 = Vector2.DOWN * project_gravity_scalar

Then it could look like this:

velocity += gravity_velocity * get_physics_process_delta_time()

Hi, i made a small project by my side to try something.
I manage to do it with an approximation like this :

Scene screenshot

And 2 script for world and bird :

World.gd
extends Node2D


@onready var bird = $Bird

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravityv = ProjectSettings.get_setting("physics/2d/default_gravity_vector")


var point = []

func readraw_all():
	point = [];
	queue_redraw()


func _draw():
	var ori : Vector2 = bird.original_position;
	for i in range(300):
		var neo : Vector2 = ori + (bird.original_velocity*0.948 + gravityv * gravity * i * get_physics_process_delta_time()) * get_physics_process_delta_time()
		draw_line(ori, neo,Color.RED);
		ori = neo
	if (point.size() > 1):
		for i in range(point.size()-1):
			draw_line(point[i], point[i+1],Color.GREEN);

		
Bird.gd
extends RigidBody2D


var reset_state = false
@onready var original_position = position
@onready var parent = get_parent();

var original_velocity


# Called when the node enters the scene tree for the first time.
func _ready():
	restart()


func _integrate_forces(state):
	if reset_state:
		state.transform = Transform2D(0.0, original_position)
		reset_state = false
		restart()

func restart():
	original_velocity = Vector2(randi_range(50, 600), randi_range(-700,-200))
	linear_velocity = original_velocity
	parent.readraw_all()


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta):
	if (sleeping):
		reset_state = true
	if (position.x > 1200):
		reset_state = true
	if (position.y > 650):
		reset_state = true
	parent.point.append(position)
	parent.queue_redraw();

The approximate path is draw on red while in green is draw the actual path take by the RigidBody2D

I don’t know why but i need a coefficient for the velocity in the calculation made in World.gd . With 0.948 it look pretty close to the original travel path.
You can see it here :

The problem with this is that I don’t have a way of getting the velocity of the sword before it’s thrown. You’re putting in random variables each time for the velocity, which doesn’t work in my case.
Assuming the very simply equation of velocity = distance / time, what would distance and time be in this scenario? Would time be delta? Would distance be the linear_velocity equation I posted earlier?

I also found this page on KidsCanCode which is exactly what I need, but trying to apply it to what I have doesn’t seem to be working. The problem is in the example it’s changing the position of the muzzle for some reason, and applying that to my example makes the sword move when the player is interacting with it which shouldn’t happen.

What information have you got in this case ? How do you calculate the velocity from the mouse position ?

Velocity is set like this:

linear_velocity = Vector2.UP.rotated(rotation) * ($StrengthMeter.get_cast_to().y * 10)

where StrengthMeter is a raycast to the mouse’s y position relative to the sword. The further it is from the sword, the futher it flies.

So you got all you need. In my equation you just need the initial position and initial velocity. The curve equation look like this :
y = x * gravity * delta² + start_velocity * delta * 0.948 + start_pos
where delta is a constant value given by get_physics_process_delta_time()

I’m not understanding any of this. This gives error "Invalid operated 'int
and ‘Vector2’ in operator ‘+’.

var pos = position
var vel = linear_velocity
	for i in range(20):
		$SwordThrowIndicator.add_point(position, i)
		vel.y = vel.x * ProjectSettings.get_setting("physics/2d/default_gravity") * get_physics_process_delta_time()^2 + vel * get_physics_process_delta_time() * 0.948 + pos
		pos += vel * delta

My bad I write to fast my equation who’s not totally correct. You can try this :

	var pos = position
	var vel = linear_velocity
	var step_size = 1 # reduce points counts
	var points = []
	var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") * ProjectSettings.get_setting("physics/2d/default_gravity_vector")
	var delta = get_physics_process_delta_time()
	for i in range(0,20):
		points.append(pos)
		for j in range(step_size):
			pos = (i * step_size + j) * gravity * delta * delta + vel * delta * 0.948 + pos

That is still not working, and points.append is not valid. Are you sure you have this typed out correctly?

var pos = position
var vel = linear_velocity
var stepSize = 1
for i in range(20):
	$SwordThrowIndicator.add_point(pos, i)
	for j in range(20):
		pos = (i * stepSize + j) * ProjectSettings.get_setting("physics/2d/default_gravity") * delta * delta + vel * delta * 0.948 + pos

You forgot the delta variable and to set stepsize in the second loop range.
You also need to have a gravity as a vector that’s why I multiply the gravity by the vector in the gravity variable declaration.

var pos = position
var vel = linear_velocity
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") * ProjectSettings.get_setting("physics/2d/default_gravity_vector")
var stepSize = 1
var delta = get_physics_process_delta_time()
for i in range(20):
	$SwordThrowIndicator.add_point(pos, i)
	for j in range(stepSize):
		pos = (i * stepSize + j) * gravity * delta * delta + vel * delta * 0.948 + pos