How to make collidable moving platform using KinematicBody2D?

52 Views Asked by At

I'm trying to make a levitating platform using KinematicBody2D and when my character (also KinematicBody2D) falls on top the platform stops moving

enter image description here

Code for the platform:

extends KinematicBody2D

export var levitation_speed: float = 1000
var _velocity: Vector2

# for simplicity sake I've only shown the platform moving up
func _physics_process(delta):
    _velocity += levitation_speed * delta * Vector2.UP
    _velocity = move_and_slide(_velocity)

code for the player:

extends KinematicBody2D
class_name Humanoid

var velocity:Vector2
export var gravity_speed=3500
export var jump_speed=-1800
export var horizontal_speed=600

func _physics_process(delta):
    var direction=Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
    velocity.x=horizontal_speed * direction
    
    if(is_on_floor()):
        if(Input.is_action_just_pressed("ui_up")):
            velocity.y+=jump_speed
    
    velocity.y += gravity_speed * delta
    velocity=move_and_slide(velocity, Vector2.UP)

How do I make the player move along the platform no matter the direction it moves in? If this is not possible with KinematicBody2D I'm open to using RigidBody2D

Note: I'm intentionally using _physics_process() & not AnimationPlayer or Tween so if you suggest a work around please involve it


Edit:

Based on the provided answer by @Theraot it sort of works when the player falls on top of platform but fails when the player tries to enter from below as such:

enter image description here

as any good platform in a game, the player should be able to pass through it from underneath

2

There are 2 best solutions below

8
Theraot On BEST ANSWER

Using move_and_slide_with_snap instead of move_and_slide on the player, should keep it snap to the ground when the ground is moving.

It needs an snap vector, which you can imagine as a ray that checks for the platform.

The code would be like this:

velocity=move_and_slide_with_snap(velocity, Vector2.DOWN, Vector2.UP)

However, after testing this didn't solve the problem at hand.


global_position

What we can do is move the platoform without move_and_slide nor move_and_slide_with_snap, but by changing its global_position instead, like this:

global_position += _velocity * delta

This means that the platform will move ignoring any obstacles (to be clear: it will go through static obstacles), however it can still push the player.

This solution also allows to enable motion/sync_to_physics however that is not necesary since we would be moving in _physics_process.


one_way_collision

I found another way: We can set one_way_collision to true on the collider of the player.

The player and platform can then still use move_and_slide or move_and_slide_with_snap and both will collide with static obstacles.

Thus this solution does not require any code change.

0
M. R. M. On

Finally got the perfect platform which uses _physics_process(delta)

Features:

  1. Doesn't squish the player
  2. Allows player jump on it
  3. Allows player to pass from underneath it
  4. Platform collides with the world!

Structure:

enter image description here

Platform code:

extends KinematicBody2D

onready var base = $Base
onready var base_offset=global_position - base.global_position
var velocity:Vector2

func _physics_process(delta):

    # manipulate velocity
    ...
    
    velocity=move_and_slide(velocity)
    base.global_position=self.global_position - base_offset

In this demo the platform is moving in a circular direction but is obstructed by obstacles:

enter image description here

demo platform code:

extends KinematicBody2D

onready var base = $Base
onready var base_offset=global_position - base.global_position
export var radius: float = 400
export var angular_speed: float = 0.5

var angle: float = 0
onready var initial_position: Vector2 = global_position

func _physics_process(delta):
    angle += angular_speed * delta
    
    var new_x = initial_position.x + radius * cos(angle)
    var new_y = initial_position.y + radius * sin(angle)
    var velocity = Vector2(new_x, new_y) - global_position
    
    move_and_slide(velocity)
    
    base.global_position=self.global_position - base_offset