How to change the 2D gravity direction?

Godot Version

4.2.1

Question

How do I change the direction of the gravity? I wanna make a precision platformer that involves shifting the gravity direction for navigation, but I can’t quite crack how to achieve it
I want a similar result as what happens in this video: https://youtu.be/mHeh-8xZvBo?si=x5Qssw0Xfk1Ndf_J&t=57

Thank you!

I would start with a unit vector that defines the gravity direction. Then walking and jumping directions need to be relative to gravity direction.

For example, the current walking/running speed is

gravity_dir.orthogonal().dot(velocity)

where positive values point to the right from the character’s perspective. Then you can update the value based on user input. Here’s one way to do it.

var right_vector := gravity_dir.orthogonal()
var speed := right_vector.dot(velocity)
# Remove left-right component from velocity
velocity -= speed * right_vector
# input is a float between -1.0 and 1.0
speed = move_toward(speed, input * MAX_SPEED, ACCELERATION * delta)
# Add left-right component back with an updated value for speed
velocity += speed * right_vector

Jumping can be done by setting the velocity in the up direction to a certain value.

velocity = velocity.slide(gravity_dir) + gravity_dir * JUMP_SPEED

And if you’re using a CharacterBody2D, its up_direction needs to be updated for floor detection.

So basically with a bunch of vector math you can make the code direction independent and then change the gravity direction to whatever you want.

1 Like

Thanks for the response

I’m trying to implement this into my own code, but I can’t figure out how to implement it right (code at end of this reply if you wanna take a look)

Like for example, do i have to have 2 speed variables or something? Because the speed variable needs a number value to make it so the character actually moves, and “var speed := right_vector.dot(velocity)” doesn’t include that.

(my code:
"
extends CharacterBody2D

const SPEED = 105.0
const JUMP_VELOCITY = -175.0

var max_fall_speed = 155
var gravity = 450
#just incase: ProjectSettings.get_setting(“physics/2d/default_gravity”)
var gravity_dir = Vector2.UP
var dTrue = true
const pushback = 500

func _physics_process(delta):
if not is_on_floor():
velocity.y += gravity * delta

var direction = Input.get_axis("ui_left", "ui_right")
if direction and dTrue:
	velocity.x = direction * SPEED
	$AnimatedSprite2D.play("Run")
else:
	velocity.x = move_toward(velocity.x, 0, SPEED)
	$AnimatedSprite2D.play("Idle")
if velocity.y > max_fall_speed:
	velocity.y = max_fall_speed

if Input.is_action_pressed("ui_left"):
	$AnimatedSprite2D.flip_h = true
if Input.is_action_pressed("ui_right"):
	$AnimatedSprite2D.flip_h = false

if is_on_wall() and direction:
	max_fall_speed = 50
else:
	max_fall_speed = 155
	
if is_on_wall_only() and direction and Input.is_action_just_pressed("ui_accept"):
	velocity.x = -450 * direction
	velocity.y = -120
	$WallTimer.start()
	$wallKick.play()
	dTrue = false
	

if Input.is_action_just_pressed("ui_accept") and is_on_floor():
	velocity.y = JUMP_VELOCITY
	$jumpSound.play()
if Input.is_action_just_released("ui_accept") and velocity.y < 0.0:
	velocity.y = 0.0


move_and_slide()

func _on_wall_timer_timeout():
dTrue = true
"
)

The trick is to change the code so that no part of it uses the x or y components of velocity directly.

Applying gravity

velocity += gravity * delta * gravity_dir

Limiting fall speed

var y_speed = gravity_dir.dot(velocity)
if y_speed > max_fall_speed:
    velocity += (max_fall_speed - y_speed) * gravity_dir

Running without acceleration

var right_vector = gravity_dir.orthogonal()
if direction and dTrue:
    velocity = velocity.slide(right_vector) + direction * SPEED * right_vector
else:
    velocity = velocity.slide(right_vector)

Jumping

if Input.is_action_just_pressed("ui_accept") and is_on_floor():
    velocity = velocity.slide(gravity_dir) + gravity_dir * JUMP_VELOCITY
if Input.is_action_just_released("ui_accept") and velocity.dot(gravity_dir) < 0.0:
    velocity = velocity.slide(gravity_dir)

Wall jump

if is_on_wall_only() and direction and Input.is_action_just_pressed("ui_accept"):
    velocity = -450.0 * direction * right_vector - 120 * gravity_dir

Moving the character

up_direction = -gravity_dir
move_and_slide()

I haven’t tested all this code, so I hope I didn’t make any mistakes.

1 Like

It worked!
Thank you so much! I sincerely appreciate it.

The wall jump seems to have stopped working a bit, but a bit of troubleshooting should solve that, but besides that it works perfectly!
(The max fall speed also seems to be a little funky, though i should be able to fix that too hopefully)

Thank you!

I’m trying to make the code able to cycle through the gravity directions, but it seems whenever I go from a Y direction (up, down) to a X direction (left, right) the gravity breaks (as in, it doesnt make the character fall, if you try to move and jump, youll walljump in thin air, etc) so I need to fix that somehow,

and alongside that, it seems the part of the code for the walljump which is “velocity = -450.0 * direction * right_vector - 120 * gravity_dir” is the cause of breaking the walljump by making it so when you walljump it teleports you away in the horizontal direction instead of rapidly making it to the designated horizontal spot for some reason

For the second issue I think I could fix it by maybe adding/removing a factor for moving away from the wall horizontally, but I don’t know what to do with the first one right now

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.