GRAVITON

Godot 4.6 · Physics Platformer

Aashrith Attelli
GODOT ENGINE 4.6
STATEIDLE
ORBS0 / 0
TIME0.00s
GRAVITY↓ NORMAL
AD Move
Space Jump
F Flip Gravity
R Restart
Click canvas to focus

// Systems

// Architecture

LevelGenerator
level_generated →
Player
state_changed →
player_died →
collected_orb →
GameManager
orchestrates all
Camera
← gravity_flipped
HUD
← state / score / time

All connections via Godot signals — zero direct node references across boundaries

// Source Code

SNIPPETS · Full files in repository
# Excerpt from Player.gd — full file ~280 lines
# Showing: FSM core, transitions, and gravity flip

enum State {
    IDLE, RUN, JUMP, FALL, WALL_SLIDE, GRAVITY_FLIP, DEAD
}

var current_state: State = State.IDLE

func _transition_to(new_state: State) -> void:
    if new_state == current_state:
        return
    var old_name := State.keys()[current_state]
    var new_name := State.keys()[new_state]
    _exit_state(current_state)
    current_state = new_state
    _enter_state(new_state)
    emit_signal("state_changed", old_name, new_name)

func _transition_states() -> void:
    match current_state:
        State.JUMP:
            if (velocity.y * gravity_direction) >= 0:
                _transition_to(State.FALL)
            elif is_on_wall() and not is_on_floor():
                _transition_to(State.WALL_SLIDE)
        State.FALL:
            if is_on_floor():
                _transition_to(State.IDLE)
            elif is_on_wall():
                _transition_to(State.WALL_SLIDE)

# Gravity flip: one float drives all physics
func _flip_gravity() -> void:
    gravity_inverted = not gravity_inverted
    gravity_direction = -1.0 if gravity_inverted else 1.0
    velocity.y = JUMP_VELOCITY * gravity_direction * 0.7
    emit_signal("gravity_flipped", gravity_inverted)
# Excerpt from LevelGenerator.gd — full file ~220 lines
# Showing: terrain generation via constrained random walk

func _generate_terrain() -> void:
    var total_cols := level_width_chunks * chunk_width
    var floor_h := chunk_height / 2

    # Constrained random walk for floor height profile
    for col in range(total_cols):
        platform_heights.append(floor_h)

        # Gap probability scales with level progress
        var gap_chance := 0.1 + (float(col) / total_cols) * 0.15
        if col > 4 and rng.randf() < gap_chance:
            var gap_len := rng.randi_range(min_gap, max_gap)
            for _g in range(gap_len):
                platform_heights.append(-1)  # void
        else:
            floor_h = clamp(floor_h + rng.randi_range(-1, 1), 3, chunk_height - 3)

    # Paint tiles at runtime
    for col in range(platform_heights.size()):
        var h: int = platform_heights[col]
        if h < 0: continue
        tilemap.set_cell(0, Vector2i(col, h), SOURCE_ID, TILE_TOP)
        for row in range(h + 1, chunk_height + 2):
            tilemap.set_cell(0, Vector2i(col, row), SOURCE_ID, TILE_SOLID)
# Excerpt from CameraController.gd — full file ~80 lines
# Showing: smooth follow, look-ahead, screen shake, dynamic zoom

func _process(delta: float) -> void:
    if not _target: return

    # Look-ahead: offset based on velocity direction
    var vel: Vector2 = _target.velocity
    var desired_offset := Vector2(vel.x, 0.0).normalized() * look_ahead_dist
    _look_ahead_offset = _look_ahead_offset.lerp(desired_offset, look_ahead_speed * delta)

    # Smooth follow
    var target_pos := _target.global_position + _look_ahead_offset
    global_position = global_position.lerp(target_pos, follow_speed * delta)

    # Screen shake via FastNoiseLite
    if shake_strength > 0.1:
        var t := Time.get_ticks_msec() / 1000.0
        _shake_offset = Vector2(
            _noise.get_noise_2d(t * 20.0, 0.0),
            _noise.get_noise_2d(0.0, t * 20.0)
        ) * shake_strength
        shake_strength = lerpf(shake_strength, 0.0, shake_decay * delta)
        offset = _shake_offset

    # Dynamic zoom: proportional to speed
    var speed_ratio := clamp(_target.velocity.length() / 500.0, 0.0, 1.0)
    var target_zoom := lerpf(base_zoom, zoom_out_max, speed_ratio)
    zoom = zoom.lerp(Vector2(target_zoom, target_zoom), zoom_speed * delta)
# Excerpt from GameManager.gd — full file ~130 lines
# Showing: signal wiring and gravity-flip camera tween

func _connect_signals() -> void:
    player.state_changed.connect(_on_player_state_changed)
    player.gravity_flipped.connect(_on_gravity_flipped)
    player.collected_orb.connect(_on_orb_collected)
    player.player_died.connect(_on_player_died)
    level_gen.level_generated.connect(_on_level_generated)

func _on_gravity_flipped(is_inverted: bool) -> void:
    # Tween camera rotation for dramatic visual effect
    var target_rot := PI if is_inverted else 0.0
    var tween := create_tween()
    tween.tween_property(camera, "rotation", target_rot, 0.35)\
        .set_ease(Tween.EASE_IN_OUT)\
        .set_trans(Tween.TRANS_CUBIC)

func _check_win_condition(orbs: int) -> void:
    if orbs >= orbs_needed:
        var elapsed := Time.get_ticks_msec() / 1000.0 - run_start_time
        if elapsed < best_time:
            best_time = elapsed
        _set_state(GameState.WIN)