How to apply a temporary change to an object's local / relative position?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Ophiolith

I am trying to implement a ‘lean’ mechanic in my game, and to do so, I want to have my character move slightly left or right relative to its position when a button is held, and then return to its original position when the button is released.

In loose pseudocode this would be roughly:

if action "lean_right" is pressed, set local x position to 0.2
if action "lean_left" is pressed, set local x position to -0.2
else set local x position to 0.0

All of the position functions I have been able to find in GDScript pertain to global position. I am assuming I need to use a vector3 to move my character relative to itself, but I can only find information about setting a velocity value, which applies constant motion which in this case isn’t desirable. Can someone please kindly explain how to modify an object’s relative position in the manner specified?

:bust_in_silhouette: Reply From: Inces

Just use mathematic in relation to global_position instead of setting it.
global_postition.x += 10 or global_position += Vector3(10,0,0) and so on

If You need more complicated positioning in 3D, take a look at Transform property
However, editor will not memorize original location for You, You need to code it yourself.

Unfotunately, setting global_position in this manner results in continuous motion that breaks collision. It also cannot be clamped, as clamp() can apparently only be set as an absolute value. I also experimented with transform.origin, in the manner described, but this causes constant co-ordinate fighting and crashes Godot after a few seconds.

I feel like I am missing something here. Surely moving a character along a single axis a set amount when a button is pressed, and returning to original position when the button is released cannot be too complicated?

Ophiolith | 2022-09-23 22:07

:bust_in_silhouette: Reply From: SweetPie

Assuming you’re not using a rigid body as your player, the following should work if I understand what you’re trying to do:

if Input.is_action_just_pressed("lean_right") or Input.is_action_just_released("lean_left"):
global_position += 0.2
if Input.is_action_just_pressed("lean_left") or Input.is_action_just_released("lean_right"):
global_position -= 0.2

Alternatively, you could just set the positions of all the player’s children (like the sprite, the hitbox, whatever else should be attached to the position of the player).

I genuinely hadn’t considered just using the child objects instead of the CharacterBody3D itself. That makes a lot more sense, and if I declare in physics_process, then it still respects collision. I could probably make my code a lot more concise, but here is what I ended up using, if anybody has the same query as me:

func _physics_process(delta):
	#handle leaning
	rotation.z = lerp(rotation.z, 0.0, lean_transition_speed * delta)
	player_capsule.position.x = lerp(player_capsule.position.x, 0.0, lean_transition_speed * delta)
	head.position.x = lerp(head.position.x, 0.0, lean_transition_speed * delta)
	if Input.is_action_pressed("Lean_left"):
		rotation.z = lerp(rotation.z, 0.4, lean_transition_speed * delta)
		player_capsule.position.x = lerp(player_capsule.position.x, -2.0, lean_transition_speed * delta)
		head.position.x = lerp(head.position.x, -2.0, lean_transition_speed * delta)
	if Input.is_action_pressed("Lean_right"):
		rotation.z = lerp(rotation.z, -0.4, lean_transition_speed * delta)
		player_capsule.position.x = lerp(player_capsule.position.x, 2.0, lean_transition_speed * delta)
		head.position.x = lerp(head.position.x, 2.0, lean_transition_speed * delta)
		
	move_and_slide()

Thank you!

Ophiolith | 2022-09-24 13:17