First-Person Tutorial
This tutorial builds a first-person camera you can fly around a scene with the mouse and WASD. It pulls together entities, components, the camera, input, and a script, which is the core of how a Lumina game fits together. It assumes the editor is running with a project open, see Your First Project.
1. Build a scene to move around in
Section titled “1. Build a scene to move around in”Before adding a player, give yourself a lit world to look at.
Add some geometry. In the Scene Outliner, click + and add a few
Cube primitives: flatten one into a floor (set its Scale to about
20, 0.2, 20 in the Details panel), and scatter a couple more as boxes.
Add lighting. A fresh scene has no light, so it would render dark. Create an
entity, name it Lighting, and in the Details panel add three components:
- Environment, the sky and image-based lighting.
- Directional Light, the sun that lights the scene and casts shadows.
- Sky Light, soft ambient fill so shadows are not pitch black.
Now you have a lit world. See Rendering for what each of these controls.
We will not put a mesh on the player itself, in first person you look through its eyes, so a body mesh would just fill the screen.
2. Create the player and give it a camera
Section titled “2. Create the player and give it a camera”- In the Outliner, click + and create an empty entity. Rename it
Player. - With
Playerselected, click + in the Details panel and add a Camera component.
A camera defines the view. Here is the component you just added:
struct SCameraComponent{ float FOV = 90.0f; // vertical field of view, degrees bool bAutoActivate = false; // become the active view when spawned SPostProcessSettings PostProcess; // per-camera grading + tone mapping
// callable from Lua: void SetFOV(float NewFOV); float GetFOV() const; FVector3 GetForwardVector() const; FVector3 GetRightVector() const;};A scene can have many cameras; exactly one is active at a time. We will make
this one active from the script. (Ticking bAutoActivate does the same without
code.)
3. Write the player script
Section titled “3. Write the player script”Create a Player.luau script in your project’s Game/Scripts folder and open it
in the script editor. Replace the contents with this:
local Script: EntityScript = EntityScript.new()
--@export(ClampMin = 0, Units = "m/s", Category = "Player")Script.MoveSpeed = 6.0
--@export(ClampMin = 0, Category = "Player")Script.LookSensitivity = 0.15
function Script:OnReady() self.Yaw = 0.0 self.Pitch = 0.0
self:EnableInput() -- let this entity read keyboard and mouse Camera.SetActive(self.Entity) -- look through this entity's camera Input.SetMouseMode("Captured") -- hide and lock the cursor for mouse-lookend
function Script:OnUpdate(DeltaTime: number) -- Look: the mouse turns us left and right (yaw) and up and down (pitch). self.Yaw = self.Yaw + self.Input:GetMouseDeltaX() * self.LookSensitivity self.Pitch = math.clamp(self.Pitch + self.Input:GetMouseDeltaY() * self.LookSensitivity, -89, 89) self.Transform:SetLocalRotationFromEuler(Vec3(self.Pitch, self.Yaw, 0))
-- Move: WASD along the direction we are facing. local Move = Vec3(0, 0, 0) if self.Input:IsKeyDown("W") then Move = Move + self.Transform:GetForward() end if self.Input:IsKeyDown("S") then Move = Move - self.Transform:GetForward() end if self.Input:IsKeyDown("D") then Move = Move + self.Transform:GetRight() end if self.Input:IsKeyDown("A") then Move = Move - self.Transform:GetRight() end
self.Transform:Translate(Move * (self.MoveSpeed * DeltaTime))end
return ScriptHow it works
Section titled “How it works”OnReadyruns once, after the scene graph is set up (see lifecycle order).self:EnableInput()adds an Input component so this entity can read input; without it,self.Inputis empty.Camera.SetActive(self.Entity)makes our camera the view.Input.SetMouseMode("Captured")locks the cursor so the mouse drives the look instead of moving a pointer.
OnUpdateruns every frame.DeltaTimeis the seconds since the last frame; multiplying movement by it keeps the speed the same on any machine.- We accumulate Yaw and Pitch from the mouse delta and write them back as the entity’s rotation. The
Vec3(Pitch, Yaw, 0)order is (pitch about X, yaw about Y, roll about Z), see Worlds & Coordinates. - We build a Move vector from WASD using the entity’s own
GetForwardandGetRight, thenTranslatealong it. The engine is+Zforward and+Xright, so “forward” is wherever you are looking.
- We accumulate Yaw and Pitch from the mouse delta and write them back as the entity’s rotation. The
- The two
--@exportvalues show up as editable fields on the Player in the editor, tune them without touching code.
4. Attach the script
Section titled “4. Attach the script”Select the Player entity, add a Script component in the Details panel, and
set its Script Path to your Player script. (You can also drag the script
onto the entity in the Outliner.)
5. Play
Section titled “5. Play”Press Play on the viewport toolbar. The cursor locks, the mouse looks around, and WASD moves you through the scene. Press Stop to return to the editor.
That is a complete gameplay loop: an entity, a camera, input, and a script moving it every frame.
Where to go next
Section titled “Where to go next”- This is a free-fly camera, it moves wherever you look. For a character that walks on the ground, jumps, and collides with walls, use the Character Controller.
- To go deeper on the camera (third-person follow, spring-arm booms, blending), see Cameras.
- For the full scripting API, see Lua Scripting.