How to make KinematicBody be able to walk around a StaticBody using raycasting (Super Mario Galaxy style)?

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

Hi!

I am trying to make a project that implements physics that resembles Super Mario Galaxy, where someone can walk around a planet perpendicular to where they are standing and, and they can jump to other planets. As research, I read this article and this video, but I have no idea how to actually implement the system. Below is an image of a sample level that I want to make. I eventually want to add non-sphere planets. The code I have for the ray (called “Gravity Calculator”) and the Player are shown below. I don’t know if I have to use look_at() or something, but I honestly have no idea. Any help is appreciated :slight_smile:

Level:
Two StaticBodies with a KinematicBody
Raycast (Gravity Calculator) Code:

extends RayCast

onready var player = get_node("../../Player")

#func _ready():
#	pass # Replace with function body.

func _process(delta):
	ray_to_planet()

func nearest_planet():
	#Get all planets and assume the first one is the closest
	var planets = get_node("../../Planets").get_children()
	var nearest_planet = planets[0]
	
	# Compare the distance from the assumed nearest to the other planet(s)
	for planet in planets:
		if planet.get_translation().distance_to(player.get_translation()) > nearest_planet.get_translation().distance_to(player.get_translation()):
			nearest_planet = planet
	# Return the nearest planet
	return nearest_planet

func ray_to_planet():
	#Get the relative distance from the player to the nearest planet and set the ray to cast there
	var raydestination = nearest_planet().get_translation() - player.get_translation()
	set_cast_to(raydestination)
	return get_cast_to()

func ray_normal():
	return get_collision_normal()

Player Code:

extends KinematicBody

onready var ray = get_node("Gravity Calculator")

var velocity = Vector3(0,0,0)

var gravity = 3

func _ready():
	pass # Replace with function body.
	
func _process(delta):
	_move_by_input(delta)
	move_and_slide(velocity, ray.get_collision_normal(), true)

func _physics_process(delta):
	velocity.y = velocity.y - gravity * delta
	_move_by_input(delta)
	velocity = move_and_slide(velocity, Vector3.UP, true)

func _move_by_input(delta):
	if Input.is_action_pressed("move_forwards"):
		velocity.z -= 1 
	if Input.is_action_pressed("move_backwards"):
		velocity.z += 1 
	if Input.is_action_pressed("move_right"):
		velocity.x += 1 
	if Input.is_action_pressed("move_left"):
		velocity.x -= 1 

Try looking on github. I downloaded a couple of demos when researching the same ideas, I know one of them used raycast to get the normals of the objects. I’ll check if I can find them.

https://github.com/LeonardMeagher2/godot-gravity-aligned-character
I saw this one, but haven’t tried it.

GitHub - BlenderSleuth/GravityPrototypes: Testing gravity mechanics in the Godot game engine
I downloaded this one, I think it’s FPS, haven’t opened in a long time.

GitHub - BlenderSleuth/godot_gravity_tests: Testing interesting 2D/3D gravity with the Godot game engine
Downloaded and checked, moves a character model around a planet, has jump and collision.

Hope they help you figure out how it works, I’ve checked the codes, but it was a while ago.

DamonR | 2020-05-23 00:22

Hey! Thanks for the links. The third one seemed to be the closest to what I was trying to do. When I added the code, it got the “walking around the planet” mechanic working, but whenever the player tried to switch planets, it stopped working, even after editing the code to make the ray point towards the nearest planet. You can see the code in action here. Do you have any ideas?

Dgoat | 2020-05-23 20:57

:bust_in_silhouette: Reply From: DamonR

I put together a small demo scene based on the third link I suggested to you.

You change planets by jumping on the pads and pressing “c”.

To stop the player getting stuck on a planet, I disable the player rays and colliders. To point to a new planet, I set planet_position to the Vector3 of the target planet.

I added a cube planet to test extra shapes.

Transitions between planets and sides of the cube are not smooth and polished, but the essentials are there.

Hope it helps.