Lift Platform System: Physics-Based Moving Platforms

How to create a moving platform that carries players and objects using Godot’s physics system

Godot Version: 4.5
Renderer: GL Compatibility
Project Type: 3D Game with Moving Platforms

Introduction

Moving platforms are a common feature in games—elevators, lifts, conveyor belts, and moving floors. The challenge is making objects on top of the platform move with it naturally, using physics rather than manual position manipulation.

This article explains how to create a lift platform that uses CharacterBody3D and move_and_slide() to automatically carry players and physics objects.

The Key Insight: Use CharacterBody3D

The secret is making the platform itself a CharacterBody3D instead of a Node3D. When a CharacterBody3D moves using move_and_slide(), objects on top automatically move with it due to friction and collision detection.

Why CharacterBody3D?

  • Automatic Physics Interaction: Bodies on top move naturally with the platform
  • No Manual Tracking: No need to track objects and manually apply velocities
  • Friction Works: High friction keeps objects from sliding off
  • Collision Detection: Proper collision with players and other bodies

Setup Steps

1. Create the Platform Node

In your scene, create a CharacterBody3D node for the platform:

Lift (Node3D)
  └─ Platform (CharacterBody3D)  ← This is the moving platform
      └─ CollisionShape3D
      └─ FloorTile (visual mesh)
      └─ LiftButton

2. Add Collision Shape

Add a CollisionShape3D child to the Platform with:

  • Shape: BoxShape3D (or appropriate shape)
  • Physics Material: High friction (e.g., friction = 10.0)

The collision shape defines what the platform physically is—this is what objects collide with.

3. Script the Movement

Create a script that moves the platform using velocity and move_and_slide():

extends Node3D
class_name Lift

@onready var platform: CharacterBody3D = get_node_or_null("Platform")
@export var start_point: Node3D
@export var end_point: Node3D
@export var move_duration: float = 2.0

var _target_position: Vector3 = Vector3.ZERO
var _is_moving: bool = false
var _fixed_y_position: float = 0.0  # Lock Y to prevent falling

func _ready() -> void:
	if platform and start_point:
		platform.global_position = start_point.global_position
		platform.velocity = Vector3.ZERO
		_fixed_y_position = start_point.global_position.y

func _move_to(target: Vector3) -> void:
	_target_position = target
	_is_moving = true

func _physics_process(delta: float) -> void:
	if not platform or not _is_moving:
		return
	
	# Calculate direction (only X and Z, ignore Y)
	var current_pos = platform.global_position
	var direction = Vector3(
		_target_position.x - current_pos.x,
		0.0,
		_target_position.z - current_pos.z
	).normalized()
	
	# Calculate speed
	var total_distance = start_point.global_position.distance_to(end_point.global_position)
	var speed = total_distance / move_duration
	
	# Set velocity and move
	platform.velocity = direction * speed
	platform.velocity.y = 0.0  # Lock Y axis
	
	platform.move_and_slide()  # This moves bodies on top automatically!
	
	# Restore fixed Y position (prevent gravity)
	platform.global_position.y = _fixed_y_position
	
	# Check if reached target
	var distance_to_target = Vector3(
		_target_position.x - current_pos.x,
		0.0,
		_target_position.z - current_pos.z
	).length()
	
	if distance_to_target < 0.1:
		var final_pos = _target_position
		final_pos.y = _fixed_y_position
		platform.global_position = final_pos
		platform.velocity = Vector3.ZERO
		_is_moving = false

Key Points

1. Lock the Y Axis

If your platform moves horizontally, lock the Y axis to prevent gravity from affecting it:

platform.velocity.y = 0.0  # No vertical movement
platform.global_position.y = _fixed_y_position  # Restore after movement

2. Use move_and_slide()

move_and_slide() is the magic function. It:

  • Moves the CharacterBody3D
  • Handles collisions
  • Automatically moves bodies on top due to friction

3. High Friction Material

Set a high friction value on the platform’s physics material. This keeps objects from sliding off during movement.

4. Calculate Speed from Duration

Instead of hardcoding speed, calculate it from the desired duration:

var speed = total_distance / move_duration

This ensures consistent timing regardless of distance.

How It Works

When move_and_slide() is called:

  1. The platform moves based on its velocity
  2. Godot’s physics engine detects collisions
  3. Objects on top (players, crates) are pushed along by friction
  4. No manual tracking or velocity application needed!

Comparison: Old vs New Approach

Old Approach (Doesn’t Work Well)

  • Platform is a Node3D moved by tween
  • Manual tracking of objects with Area3D
  • Manually applying velocities to each object
  • Complex and error-prone

New Approach (Works Great)

  • Platform is a CharacterBody3D
  • Uses move_and_slide() for movement
  • Physics handles everything automatically
  • Simple and reliable

Testing

To test your lift:

  1. Place a player on the platform
  2. Place a crate on the platform
  3. Trigger the lift to move
  4. Both should move smoothly with the platform

If objects slide off or don’t move, check:

  • Platform has a CollisionShape3D
  • Physics material has high friction
  • Platform is actually a CharacterBody3D (not Node3D)
  • Y axis is locked if moving horizontally

Summary

Moving platforms in Godot are simple when you use the right approach:

  1. Make the platform a CharacterBody3D
  2. Add a CollisionShape3D with high friction
  3. Use velocity + move_and_slide() for movement
  4. Lock Y axis if moving horizontally

That’s it! The physics engine handles the rest automatically.


Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *