Help needed with rotating platform

Godot Version

Godot 4.5 dev 4

Question

Hi everyone. I’m currently adding some platforms to my game, and i’ve encountered a pretty annoying issue with rotating platforms.

When my player stands on it, it is slightly offseted back and forth depending on its position on the platform, which means that if it stands near the edge of the platform, it’ll fall.
My platform is an AnimatableBody2D, it rotates using tweens, and my player is a CharacterBody2D

This video showcases my issue :

At 00:40, i’m trying to jump at full extent, but my player seems to be stuck a few frames, that causes the jump to be shortened. My velocity.x seems also to be broken.

This my player physics settings :

Here is a comparison with New Super Mario Bros U, we can see that player(s) stay fixed on the platforms :

Help me please !

i think you need to play aounrd with the properties of the characterbody, like: “stop on slope” “motion_mode” “max_angle” etc.

1 Like

Ok so I have changed the AnimatableBody2D to StaticBody2D, and the result is better. Nonetheless, despite having a pretty high floor_snap_value, and a max_angle at 50°, while platform rotation does not exceed 45°, my player does not stick to the platform.

Maybe you have better results with a rectangle collisionshape for the player

OK, so that is the result using RectangleShape2D :

I’m going to create a blank project with a very simple kinematic player, with the same platform, and i’ll communicate you the results :smiley:

I created a blank project, in which i’ve recreated the same pattern as found in my main project.
So that is the result with a RectangleShape2D :

And with CircleShape2D :

And I’ve found the snap issue in my main project. I have changed earlier the up_direction on my player, but while it displayed vector2(0.0, -1.0), i still had weird transitions between grounded and airborne states. So I resetted the values using the arrow and now everything runs well, as shown in the video down below :

As for the rotating platform, I’m almost there, but I cannot figure out how to fix the player on the platform.

What I would like to have is having the position.x of the player staying fixe relative to the platform like the little white circle, when I do not apply any movement to the player :

1 Like

I have not a lot of experience with physics-based games, so i cant really help you further here. Maybe someone else can help.
Good luck with your game though

1 Like

@herrspaten, thank you very much for your help. I’ll credit you in my game when it’ll be completed, If God wills it.
Thanks for giving me some of your precious time :smiley:

1 Like

Maybe a triangle like shape could help. Something like this:


Although it could have problematic behaviour down the line if you’ll be using more complex physics methods.

1 Like

That looks very interesting. I’m going to test that and i’ll share the results.

After some tests, the triangle shape collider works well when character is idling, but not when player moves on slope :

Yeah, I was afraid something like that would be happening. I should’ve also specified to try experimenting with the shape: e.g. making the sharp end flat, different angles and so on. Furthermore, you could also swap shapes in-process, so, for example, when a player character is landing on rotating level geometry, you could swap it for a very thin triangle (this is me theorizing without actually testing, so take it with a grain of salt).

Unfortunately, if you want to have full control over such behaviours, you might have to abandon physics methods altogether, excluding maybe collision detection.

1 Like

What you’ve said is very pertinent. Hmm, i’m going to experiment different things on my side and i’ll come back if I manage to get something … or not !

Hi I’ve just found the reason why my player didn’t walk properly on slopes while using a triangle shape.
I have created a Raycast, located at the feet of the player, that detects if player is encountering a vertical obstacle. If the raycast collision returns true, then player enter idle state. The problem is that when i used triangle shape, the raycast was colliding with the slope, preventing me from walking on it.

So I have disabled it and now, I can walk freely on the platform :

3 Likes

After some experiments about parenting the player to the platform, I’ve figured out that my problem is not a physic one, but a pivot one.
As you can see in the video down below, if I attach my player to the little white collision shape and set its position.x = 0.0 in the _physics_process, set the global_rotation of the CS2D to 0.0, then I achieve the desired result, as the pivot point becomes the collision shape, although I cannot move anymore.

What should I do to keep my player controllable on the platform, but keep it attached to an invisible point when no input applied ?

1 Like

Well, it sort of still is a physics problem, since the physical body node has to be on top of the object hierarchy for the physics methods to work as intended.

As per your question, it’s a bit contradictory: “How can I move and be attached to a point at the same time”, you can’t really :). The idea behind the triangle shape was about that issue, since its lowest point is a point, so it should’ve acted as a pivot of sorts, but I think the sides of the triangle interfered with colliders. That’s why I suggested swapping player collision shapes - you can still do that here, one shape for moving, another shape for idling.

Sorry for the contradiction … :face_with_open_eyes_and_hand_over_mouth:
As for the pivot point, is it posible to “fake” the pivot point, if you see what I mean ?

Well, the most reliable way is parenting (as you already discovered), since the engine does all the heavy lifting for you, and efficiently as well. What you can always do is create spatial relationships, suitable for a case: e.g. copy location with an offset and/or rotate around a node or a point. I’m not exactly sure what your plan is, so I’m generalizing here.

1 Like

The idea here is to have the pivot point of my player, originally located at the center of my player, to be at the bottom of the latter when he stands on the rotating platform.

What you have suggested is pretty interesting. I’m going to delve into it.

I’ve finally found a way to solve the problem I’m currently facing :

And this is the code :

class_name RotaryPlatform extends Node2D

@export_range(-45.0, 45.0, 1, "suffix:°") var start_angle: float
@export_range(-45.0, 45.0, 1, "suffix:°") var end_angle: float

@export_range(0, 10.0, 0.1, "suffix:s") var rotation_time: float
@export_range(0, 10.0, 0.1, "suffix:s") var wait_time: float

@export var easing: Tween.EaseType

var position_on_platform: float
var player: PlayerBT

@onready var area_2d: Area2D = %Area2D
@onready var foot_position: Marker2D = %FootPosition


func _ready() -> void:
	set_physics_process(false)
	rotation_degrees = start_angle
	tween_rotation()


func tween_rotation() -> void:
	var tween = get_tree().create_tween()
	tween.tween_property(self, ^"rotation_degrees", end_angle, rotation_time).set_ease(easing).set_delay(wait_time)
	tween.tween_property(self, ^"rotation_degrees", start_angle, rotation_time).set_ease(easing).set_delay(wait_time)
	await tween.finished
	tween_rotation()


func _physics_process(_delta: float) -> void:
	foot_position.global_rotation = 0.0

	if player:
		player.global_rotation = 0.0

	if area_2d.get_overlapping_bodies().size() > 0 and player:
		if player.get_parent() == self && player.is_on_floor():
			if player.action.move().x == 0:
				player.global_position.x = foot_position.global_position.x
			else:
				foot_position.global_position.x = player.global_position.x

	foot_position.position.y = -24.0


func _on_area_2d_body_entered(body: CharacterBody2D) -> void:
	if body is PlayerBT:
		player = body

	_attach_player_to_platform.call_deferred()

	set_physics_process(true)


func _on_area_2d_body_exited(_body: Node2D) -> void:
	if player && player.get_parent() == self:
		_detach_player.call_deferred()


func _attach_player_to_platform() -> void:
	player.global_rotation = 0.0

	player.reparent(self)
	foot_position.global_position.x = player.global_position.x
	foot_position.position.y = -24.0


func _detach_player() -> void:
	player.reparent(owner)
	player.global_rotation = 0.0

	set_physics_process(false)

It’s a good starting point for further improvements, but for now, I’m pretty satisfied with it.
I want to thank you all for your support, for helping me going through this maze, and I’ll credit all of you in my game :smiley: .

If someone has a better idea on how to implement this behavior, he is welcome.