Skip to content
Back to Blog

Your First Game in 5 Minutes

By Rawframe Team
tutorial game-dev beginner

What You'll Build

By the end of this tutorial, you'll have a working Rawframe game that spawns a rotating cube in the world. No prior game development experience needed — just a text editor and a Rawframe installation.

Step 1: Create the Place

Every game lives in a Place — a project folder with a place.json manifest. Create one:

my-first-game/
  place.json
  scripts/
    server/
      init.lua

Your place.json tells the engine about your game:

{
  "name": "my-first-game",
  "version": "1.0.0",
  "title": "My First Game",
  "author": "YourName",
  "description": "A spinning cube to learn the basics.",
  "scripts": {
    "server": ["scripts/server/init.lua"]
  }
}

Tip: You can also run bash tools/create_game.sh MyFirstGame "YourName" to scaffold this automatically, or create a new Place in Rawframe Studio.

Step 2: Write Your First Script

Open scripts/server/init.lua and add:

-- Spawn a cube 5 units above the ground
local cube = entity.create("my_cube")
entity.set_position(cube, Vec3.new(0, 5, 0))
entity.set_mesh(cube, "primitive:cube")

-- Track rotation angle
local angle = 0

-- Rotate the cube every frame
hook.Add("Think", "SpinCube", function(dt)
    angle = angle + dt * 90  -- 90 degrees per second
    entity.set_rotation(cube, Quat.from_euler(0, angle, 0))
end)

print("My first game loaded!")

That's it — 10 lines of code. Let's break it down:

  • entity.create() spawns a new entity with a name
  • entity.set_position() places it in the world using a Vec3
  • entity.set_mesh() gives it a visual mesh (built-in primitives start with primitive:)
  • hook.Add("Think", ...) registers a function that runs every server tick
  • Quat.from_euler() creates a rotation from pitch, yaw, and roll angles

Step 3: Run and Test

Start a local server with your game loaded:

./bin/rawframe_server -game my-first-game -port 27015

You should see My first game loaded! in the console output.

Now connect with the client:

./bin/rawframe_client -connect 127.0.0.1:27015

You'll see a cube floating in the air, spinning smoothly at 90 degrees per second.

Step 4: Add Networking

Let's make it interactive. When a player presses a key, the cube changes color. Add a client script at scripts/client/input.lua:

-- Client-side: detect key press and send to server
hook.Add("Think", "CheckInput", function()
    if input.is_key_pressed("E") then
        net.send("ChangeColor")
    end
end)

Update your place.json to include the client script:

{
  "scripts": {
    "server": ["scripts/server/init.lua"],
    "client": ["scripts/client/input.lua"]
  }
}

Then handle the message on the server in init.lua:

-- Server-side: receive the message and change color
local colors = {
    Vec3.new(1, 0, 0),  -- red
    Vec3.new(0, 1, 0),  -- green
    Vec3.new(0, 0, 1),  -- blue
}
local color_index = 1

net.receive("ChangeColor", function(peer)
    color_index = (color_index % #colors) + 1
    entity.set_color(cube, colors[color_index])
end)

Now when any player presses E, the cube cycles through red, green, and blue.

What's Next?

You've just built a game with entity spawning, physics-driven updates, and client-server networking. From here, you can:

  • Add physics — use physics.set_body() to give entities rigidbodies
  • Build UI — the ui.* API has 250+ functions for creating in-game interfaces
  • Create weapons — the weapon.* API supports hitscan, projectile, and melee weapons
  • Store datastorage.* provides per-game SQLite persistence
  • Skip the busywork — grab models, sounds, and ready-made systems from the Asset Store

Check out the API documentation for the full list of 583+ functions. When your game is ready, publish it on your own server or export it standalone.

Happy building!