Custom sandbox game made in C# and Raylib

Build weird stuff in Physics Mod

Physics Mod is a custom sandbox game inspired by Garry's Mod, but made to feel easier, faster, and more flexible to create with. Mess with physics, make your own scripts, build custom mods, and prototype cool ideas without fighting the engine.

Downloads

Put your game build, launcher, GitHub, itch.io page, or patch downloads here.

This section is ready for your real links. Replace the buttons below with your latest build, older builds, changelog, Discord, or wherever you host Physics Mod.

About Us

Physics Mod is built around freedom, fast iteration, and making sandbox content without loads of setup.

Made Custom

Built in C# with Raylib instead of relying on a giant engine, so the game can grow its own systems, tools, and workflow.

Easy Scripting

Use simple Lua hooks and helper functions to make menus, tools, chat features, player tweaks, and gameplay ideas quickly.

Sandbox Focus

Spawn things, test ideas, mess with physics, and create fun scenes like a proper sandbox should let you.

Made for Modding

The goal is to make content creation simpler, so adding your own stuff feels fun instead of annoying.

Lua Cheat Sheet

Quick reference for Physics Mod Lua. This includes lifecycle hooks, API groups, button hook behavior, ADS helpers, and ready-to-copy examples.

Lifecycle

  • init()
  • update(dt)
  • on_chat(playerName, text)

Game

  • game.get_mod_name()
  • game.get_mod_folder()
  • game.get_mod_root()
  • game.get_player_name()
  • game.get_time()
  • game.get_player_count()
  • game.is_multiplayer()
  • game.is_host()
  • game.is_client()
  • game.set_status(text)
  • game.print(text)

Chat and Input

  • chat.say(text)
  • input.key_down("q")
  • input.key_pressed("q")
  • input.key_released("q")
  • input.mouse_down("left")
  • input.mouse_pressed("left")
  • input.mouse_released("left")
  • input.mouse_x()
  • input.mouse_y()
  • input.set_cursor_visible(true)
  • input.is_cursor_visible()
  • input.screen_width()
  • input.screen_height()

Player

  • player.get_x()
  • player.get_y()
  • player.get_z()
  • player.teleport(x, y, z)
  • player.set_position(x, y, z)
  • player.get_yaw()
  • player.get_pitch()
  • player.set_view(yawDegrees, pitchDegrees)
  • player.get_move_speed()
  • player.set_move_speed(value)
  • player.get_sprint_multiplier()
  • player.set_sprint_multiplier(value)
  • player.get_jump_force()
  • player.set_jump_force(value)
  • player.get_gravity()
  • player.set_gravity(value)

Weapons

  • weapons.get_active()
  • weapons.select(name)
  • weapons.reload()
  • weapons.set_reserve_ammo(name, value)
  • weapons.add_reserve_ammo(name, amount)
  • weapons.set_magazine_ammo(name, value)
  • weapons.add_magazine_ammo(name, amount)
  • Put AdsOffsetX / AdsOffsetY / AdsOffsetZ in weapon.json for per-weapon ADS position
  • weapons.set_ads_pose(x, y, z, rotX, rotY, rotZ, speed)
  • weapons.set_ads_position(x, y, z, speed)
  • weapons.set_ads_rotation(x, y, z, speed)
  • weapons.set_ads_active(true)
  • weapons.is_ads_active()
  • weapons.get_ads_blend()
  • weapons.clear_ads()

HUD / UI

  • hud.text(x, y, text, fontSize, color, centered)
  • hud.panel(x, y, width, height, color, borderColor)
  • hud.button(x, y, width, height, text, hook, fontSize, color, hoverColor, borderColor, textColor)
  • hud.screen_width()
  • hud.screen_height()
  • hud.set_cursor_visible(true)
  • hud.is_cursor_visible()
  • ui is an alias of hud

Button Hooks

  • hook can be a function name string like "my_hook"
  • or a direct function like my_hook
  • ui.button(...) returns true on the frame it was clicked

1. Toggle a menu with Q

This flips a boolean when you press Q and shows or hides the cursor.

local menu_open = false

function update(dt)
    if input.key_pressed("q") then
        menu_open = not menu_open
        input.set_cursor_visible(menu_open)
    end
end

2. Clickable button

This puts a button on screen, and clicking it sends a hello message into chat.

function say_hello()
    chat.say("Hello from button")
end

function update(dt)
    if input.key_pressed("q") then
        input.set_cursor_visible(true)
    end

    hud.button(40, 120, 180, 42, "Say Hello", "say_hello", 20, "#3A5DAE", "#5B7DD4", "#FFFFFF40", "#FFFFFF")
end

3. Hold right click for ADS

This turns aim-down-sights on only while the right mouse button is being held.

function update(dt)
    weapons.set_ads_active(input.mouse_down("right"))
end

4. Print mod and player info

This prints useful basic info when the script starts.

function init()
    game.print("Mod: " .. game.get_mod_name())
    game.print("Folder: " .. game.get_mod_folder())
    game.print("Player: " .. game.get_player_name())
    game.print("Players online: " .. game.get_player_count())
end

5. Teleport to a new position

This moves the player to a set spot when E is pressed.

function update(dt)
    if input.key_pressed("e") then
        player.teleport(0, 10, 0)
        game.print("Teleported")
    end
end

6. Change movement settings

This sets custom movement values when the mod loads.

function init()
    player.set_move_speed(7)
    player.set_sprint_multiplier(1.8)
    player.set_jump_force(8)
    player.set_gravity(18)
    game.print("Movement updated")
end

7. Show player position on HUD

This draws the player's current X, Y, and Z on screen every frame.

function update(dt)
    local x = player.get_x()
    local y = player.get_y()
    local z = player.get_z()

    hud.panel(20, 20, 260, 80, "#101722CC", "#8FB4FF40")
    hud.text(30, 32, "Position", 20, "#FFFFFF", false)
    hud.text(30, 60, "X: " .. x .. "  Y: " .. y .. "  Z: " .. z, 16, "#CFE0FF", false)
end

8. Change ADS pose

This applies a custom ADS position and rotation while holding right click.

function update(dt)
    if input.mouse_down("right") then
        weapons.set_ads_pose(-0.08, -0.04, 0.12, 0, 0, 0, 10)
        weapons.set_ads_active(true)
    else
        weapons.clear_ads()
    end
end

9. Button using direct function

This shows that button hooks can use the function itself instead of a string name.

function open_chat_message()
    chat.say("Direct function hook worked")
end

function update(dt)
    ui.button(40, 180, 240, 42, "Direct Hook Button", open_chat_message, 20, "#3A5DAE", "#5B7DD4", "#FFFFFF40", "#FFFFFF")
end

10. Detect button click return value

This uses the return value from ui.button(...) and prints when it was clicked.

function update(dt)
    local clicked = ui.button(40, 240, 220, 42, "Click Me", nil, 20, "#3A5DAE", "#5B7DD4", "#FFFFFF40", "#FFFFFF")

    if clicked then
        game.print("Button was clicked this frame")
    end
end