Steam, touch and local multiplayer
Steam achievements, stats and cloud files; touch input and on-screen controls; local multiplayer players and split screen.
Files: steam.odin, touch.odin, players.odin.
File overviews
Section titled “File overviews”steam.odin
Section titled “steam.odin”Steam: achievements, stats and cloud saves, through one API whatever runs them.
- Achievements are listed in project.xml (
<achievement name="FIRST_BLOOD" title="First Blood" description="Defeat an enemy"/>; the editor’s Project Settings > Steam): scripts unlock them by name (Steam_UnlockAchievement("FIRST_BLOOD"); the editor writesACHIEVEMENT_Xconstants into scripts/assets.odin). Names are Steam’s API names, as set up on Steamworks. - Stats are numbers by name (
Steam_SetStat,Steam_AddStat,Steam_GetStat): kills, distance walked.Steam_StoreStatssends them (and unlocked achievements), as Steam wants. - Cloud files (
Steam_CloudWrite/Read/Delete/Exists): files that follow the player between computers. With the project’ssteamCloud="true",Save_GameandLoad_Gamego through them.
The backend: the calls go to a SteamBackend (Game.steam.backend). Until the Steamworks SDK
is part of the engine, every game runs the stub (STEAM_STUB_BACKEND): achievements and stats
are kept in the game’s save storage (steam-stub; runs from the editor apart, like saves), cloud
files are save files named steam-cloud-<file>, and each unlock is logged (“Steam (stub):
achievement FIRST_BLOOD unlocked”). Games written against the API need no change when the real
backend arrives. Steam_SetBackend replaces it (tests, or a backend of your own).
onAchievementUnlocked (Game.steam) is notified on each new unlock (data: the name as a
string), so a game can show its own toast.
Example
if !e.Steam_IsAchievementUnlocked("FIRST_BLOOD") do e.Steam_UnlockAchievement("FIRST_BLOOD")e.Steam_AddStat("ENEMIES_DEFEATED", 1)e.Steam_StoreStats()touch.odin
Section titled “touch.odin”Touch input and on-screen controls: phones, tablets, Steam Deck and touch-screen laptops.
- Touches (
Input_GetTouches,Input_GetTouchCount): every finger on the screen this frame, in game coordinates (the app converts SDL’s finger positions like the mouse’s), with where it started, how far it moved and whether it just went down or up. A finger keeps its index while it’s down; lifted fingers are listed (released) for one frame. - The mouse from touches: SDL also turns the first finger into mouse presses, so menus, buttons and anything else that reads the mouse work with touches already.
- VirtualJoystick nodes: a stick drawn on the screen, moved by a finger (or the mouse, to
try it on a desktop). It pushes up to four input actions (
touchActions="move_left,move_right, move_up,move_down"), so a game readingInput_GetVectormoves the same with keys, a gamepad or the stick.VirtualJoystick_GetVectorreads it directly. Floating joysticks appear where the thumb lands inside their zone. - TouchButton nodes: a round button pressing an action while touched (
touchActions="jump"), with a label, andonPressed/onReleasedevents.
Each control takes one finger: the first that lands in its zone, until it lifts (it can move anywhere meanwhile). A finger taken by a control isn’t taken by another. Controls hidden in the tree, or in scenes not updating, take none.
Where to put them: on a layer the camera doesn’t move (a scene’s screen layers), so they stay
put on the screen; their position is the middle of the stick or button. They draw unturned and
unscaled (like Labels). By default they show only once the screen has been touched, or in
development runs (Game.devKeys: Play from the editor), so desktop players don’t see them
(TouchControlSettings.visibility).
Example
stick := e.CreateVirtualJoystick(scene, {"move_left", "move_right", "move_up", "move_down"})stick.position = {120, 600}stick.layer = .Layer8jump := e.CreateTouchButton(scene, "jump", "A")jump.position = {1160, 620}jump.layer = .Layer8players.odin
Section titled “players.odin”Local multiplayer: players joining with a device (a gamepad, or the keyboard and mouse), each reading the project’s input actions from their own device only.
- Joining (
Players_StartJoining): while joining is on (a lobby), a press of the join input on a device no player has adds a player with that device: by default a gamepad’s South (Xbox A) or Start button, or the keyboard’s Enter or Space; with the project’sjoinActionset, that action on the device.Players_Joinadds one from code (a single-player game can make player 0 the keyboard at start). At mostmaxPlayers. - Per-player input:
Player_IsActionPressed(1, "jump"),Player_GetVector(1, ...)read the same actions asInput_IsActionPressed, from that player’s device: a gamepad player’s gamepad bindings (any device number in them is ignored: the player’s own pad), the keyboard player’s key and mouse bindings (and touch controls).Input_IsActionPressedstill reads every device. - Disconnects: a player whose gamepad is unplugged stays (
connectedfalse,onDisconnected); the next gamepad plugged in while one is missing goes back to the first player waiting (onReconnected).Players_Leaveremoves a player; the later ones move down a number. - Events (
Game.input.players):onJoined(data: the player’s number, an int),onLeft,onDisconnected,onReconnected. - Split screen: a CameraView per player (camera_tools.odin) placed by
SplitScreen_Rect.
Players are numbered from 0 in the order they joined.
Example
// A lobby: everyone presses A (or Enter) to joine.Players_StartJoining()// Later, in a player's script (its `player` field is exported: set per node)// move := e.Player_GetVector(self.player, "left", "right", "up", "down")SteamAchievementData
Section titled “SteamAchievementData”SteamAchievementData :: struct { // Steam's API name ("FIRST_BLOOD") name: string, // What players see, and how to get it (tools show them; Steam shows its own) title: string, description: string, // Hidden until unlocked (on Steam's pages) hidden: bool,}An achievement the project lists: <achievement name title description hidden/>
struct. Source: steam.odin:38
SteamSettings
Section titled “SteamSettings”SteamSettings :: struct { // The game's Steam app ID (0: not set) appId: u32, // Saves (`Save_Game`) go to Steam Cloud files cloudSaves: bool,}The project’s Steam settings (project.xml’s steamAppId, steamCloud)
struct. Source: steam.odin:49
SteamBackend
Section titled “SteamBackend”SteamBackend :: struct { // Shown in logs ("stub") name: string, // Starts it (the project loaded); returns whether Steam is reachable start: proc(steam: ^SteamState) -> bool, // Writes what's pending and stops (the game ends) stop: proc(steam: ^SteamState), // Achievements by name unlock: proc(steam: ^SteamState, name: string) -> bool, clear: proc(steam: ^SteamState, name: string) -> bool, isUnlocked: proc(steam: ^SteamState, name: string) -> bool, // Stats by name setStat: proc(steam: ^SteamState, name: string, value: f64) -> bool, getStat: proc(steam: ^SteamState, name: string) -> (f64, bool), // Sends stats and achievements store: proc(steam: ^SteamState) -> bool, // Cloud files by name cloudWrite: proc(steam: ^SteamState, file: string, data: []byte) -> bool, cloudRead: proc(steam: ^SteamState, file: string) -> ([]byte, bool), cloudDelete: proc(steam: ^SteamState, file: string) -> bool, cloudExists: proc(steam: ^SteamState, file: string) -> bool,}What runs the Steam calls. Every proc gets the game’s Steam state. The stub
(STEAM_STUB_BACKEND) keeps everything locally; the Steamworks one will call Steam.
struct. Source: steam.odin:60
SteamState
Section titled “SteamState”SteamState :: struct { // The game (for its save names) game: ^Game, // The project's settings and achievements (in the game's arena) settings: SteamSettings, achievements: [dynamic]SteamAchievementData, // What runs the calls (the stub by default) backend: SteamBackend, // Started (Game_LoadProject), and whether Steam answered started, running: bool, // A new unlock (data: the name, a string) onAchievementUnlocked: EventSubject, // The stub's own state: unlocked achievements and stats (heap) stub: SteamStubData,}A game’s Steam state (Game.steam)
struct. Source: steam.odin:84
SteamStubData
Section titled “SteamStubData”SteamStubData :: struct { unlocked: map[string]bool, stats: map[string]f64, dirty: bool,}What the stub keeps (written to save storage as JSON)
struct. Source: steam.odin:101
Touch :: struct { // SDL's id for the finger (the same while it's down) id: sdl.FingerID, // Where it is, where it went down, and how far it moved this frame, in game coordinates position, start, motion: [2]f32, // How hard it presses (0 to 1; 1 on screens that can't tell) pressure: f32, // It went down this frame; it was lifted this frame (it's gone next frame) pressed, released: bool, // A VirtualJoystick or TouchButton took it claimed: bool,}A finger on the screen
struct. Source: touch.odin:45
TouchVisibility
Section titled “TouchVisibility”TouchVisibility :: enum { // Once the screen has been touched, or in development runs (Play from the editor) Touch_Screens, // Always (also on desktops without touches) Always, // Never (it still works: an invisible touch zone) Never,}When a touch control shows
enum. Source: touch.odin:59
TouchControlSettings
Section titled “TouchControlSettings”TouchControlSettings :: struct { // The stick's or button's radius, in game pixels radius: f32, // A stick moved less than this fraction of its radius counts as centered deadzone: f32, // The area that takes a finger, centered on the node (0 x 0: the circle itself) zone: [2]f32, // A joystick that starts where the finger lands in its zone (and goes back when it lifts) floating: bool, // When it's drawn visibility: TouchVisibility, // The mouse works it too (trying touch controls on a desktop) mouse: bool, // Its color (the knob and a pressed button are brighter) and opacity color: sdl.Color, opacity: f32,}A VirtualJoystick’s or TouchButton’s settings (scene files’ attributes; a plain value)
struct. Source: touch.odin:69
TouchControlKind
Section titled “TouchControlKind”TouchControlKind :: enum { Joystick, Button,}What a touch control is
enum. Source: touch.odin:100
TouchControl
Section titled “TouchControl”TouchControl :: struct { // The node part: transform, hierarchy, hooks using node: Node, // Which control it is kind: TouchControlKind, // Its settings settings: TouchControlSettings, // The actions it presses: a joystick's left, right, up, down; a button's first ("" none). In // the node's arena actions: [4]string, // Being held: by a finger (its id), or by the mouse held, byMouse: bool, finger: sdl.FingerID, // Where the stick's base is now (a floating one moves), in game coordinates base: [2]f32, // The stick's position: -1..1 each way, deadzone applied (a button: {0, 0}) value: [2]f32, // The input frame it last updated in (Input_Update uses controls that updated last frame) updatedFrame: u64,}The part VirtualJoystick and TouchButton share: settings, the finger it holds, its actions
struct. Source: touch.odin:106
VirtualJoystick
Section titled “VirtualJoystick”VirtualJoystick :: struct { using control: TouchControl,}A stick on the screen moved by a finger, pushing input actions
struct. Source: touch.odin:128
TouchButton
Section titled “TouchButton”TouchButton :: struct { using control: TouchControl, // Its label (in the node's arena; "" none) text: string, // Touched (data: none); let go onPressed: EventSubject `event:"The button was touched"`, onReleased: EventSubject `event:"The finger left the button"`,}A round button on the screen pressing an input action while touched
struct. Source: touch.odin:133
PlayerDeviceKind
Section titled “PlayerDeviceKind”PlayerDeviceKind :: enum { // The keyboard and mouse (and touch controls) Keyboard, // A gamepad Gamepad,}What a player plays with
enum. Source: players.odin:41
Player
Section titled “Player”Player :: struct { // Its number (0 the first to join) index: int, // What it plays with device: PlayerDeviceKind, // Its gamepad (SDL's id; kept while unplugged, to tell it apart) gamepad: sdl.JoystickID, // Its gamepad is plugged in (a keyboard player always is) connected: bool, // Its actions this frame (one per input action, in order) actions: [dynamic]PlayerActionState,}A local player
struct. Source: players.odin:49
PlayerActionState
Section titled “PlayerActionState”PlayerActionState :: struct { strength: f32, down, pressed, released: bool,}A player’s action this frame
struct. Source: players.odin:63
PlayerSettings
Section titled “PlayerSettings”PlayerSettings :: struct { // The most that can join (1 to MAX_LOCAL_PLAYERS) maxPlayers: int, // The keyboard (and mouse) can be a player's device keyboard: bool, // The input action that joins ("": a gamepad's South or Start, the keyboard's Enter or Space). // In the game's arena joinAction: string,}The project’s players settings (project.xml’s <players max keyboard joinAction/>)
struct. Source: players.odin:69
PlayersState
Section titled “PlayersState”PlayersState :: struct { settings: PlayerSettings, // Joining is on (`Players_StartJoining`) joining: bool, // The players, in order list: [dynamic]Player, // A player joined, left, lost its gamepad, got one back (data: the player's number) onJoined, onLeft, onDisconnected, onReconnected: EventSubject, // The events are set up (Players_Setup, or on first use) ready: bool,}The game’s players (InputState.players)
struct. Source: players.odin:83
Procedures
Section titled “Procedures”Steam_Setup
Section titled “Steam_Setup”Steam_Setup :: proc(game: ^Game, settings: SteamSettings, achievements: []SteamAchievementData)Sets the project’s Steam settings and achievements and starts the backend (Game_LoadProject does it with the project’s).
Inputs
- game: The game
- settings: App ID, cloud saves
- achievements: The achievements (copied into the game’s arena)
procedure. Source: steam.odin:116
Steam_SetBackend
Section titled “Steam_SetBackend”Steam_SetBackend :: proc(backend: SteamBackend, game: ^Game = nil)Replaces what runs the Steam calls (before the project loads, or between runs).
Inputs
- backend: The backend
- game: The game (nil: the current one)
procedure. Source: steam.odin:140
Steam_IsRunning
Section titled “Steam_IsRunning”Steam_IsRunning :: proc(game: ^Game = nil) -> boolWhether Steam answers (the stub always does)
procedure. Source: steam.odin:149
Steam_BackendName
Section titled “Steam_BackendName”Steam_BackendName :: proc(game: ^Game = nil) -> stringThe backend’s name (“stub”)
procedure. Source: steam.odin:155
Steam_GetAchievements
Section titled “Steam_GetAchievements”Steam_GetAchievements :: proc(game: ^Game = nil) -> []SteamAchievementDataThe achievements the project lists
procedure. Source: steam.odin:161
Steam_UnlockAchievement
Section titled “Steam_UnlockAchievement”Steam_UnlockAchievement :: proc(name: string, game: ^Game = nil) -> boolUnlocks an achievement (once: unlocking it again does nothing). Steam keeps it once stats are
stored (Steam_StoreStats); the stub writes it at once.
Inputs
- name: Its API name (“FIRST_BLOOD”); one the project doesn’t list is warned about
- game: The game (nil: the current one)
Returns
- Whether it’s unlocked now
procedure. Source: steam.odin:177
Steam_IsAchievementUnlocked
Section titled “Steam_IsAchievementUnlocked”Steam_IsAchievementUnlocked :: proc(name: string, game: ^Game = nil) -> boolWhether an achievement is unlocked
procedure. Source: steam.odin:188
Steam_ClearAchievement
Section titled “Steam_ClearAchievement”Steam_ClearAchievement :: proc(name: string, game: ^Game = nil) -> boolLocks an achievement again (testing). Returns whether it worked
procedure. Source: steam.odin:194
Steam_SetStat
Section titled “Steam_SetStat”Steam_SetStat :: proc(name: string, value: f64, game: ^Game = nil) -> boolSets a stat.
Inputs
- name: Its API name (“ENEMIES_DEFEATED”)
- value: The value
- game: The game (nil: the current one)
Returns
- Whether it was set
procedure. Source: steam.odin:210
Steam_AddStat
Section titled “Steam_AddStat”Steam_AddStat :: proc(name: string, delta: f64, game: ^Game = nil) -> boolAdds to a stat (0 when it was never set). Returns whether it was set
procedure. Source: steam.odin:216
Steam_GetStat
Section titled “Steam_GetStat”Steam_GetStat :: proc(name: string, game: ^Game = nil) -> f64A stat’s value (0 when it was never set)
procedure. Source: steam.odin:221
Steam_StoreStats
Section titled “Steam_StoreStats”Steam_StoreStats :: proc(game: ^Game = nil) -> boolSends stats and unlocked achievements to Steam (the stub writes them). Returns whether it worked
procedure. Source: steam.odin:229
Steam_CloudWrite
Section titled “Steam_CloudWrite”Steam_CloudWrite :: proc(file: string, data: []byte, game: ^Game = nil) -> boolWrites a Steam Cloud file.
Inputs
- file: Its name (“save1”; letters, digits,
-,_and.) - data: Its contents
- game: The game (nil: the current one)
Returns
- Whether it was written
procedure. Source: steam.odin:245
Steam_CloudRead
Section titled “Steam_CloudRead”Steam_CloudRead :: proc(file: string, game: ^Game = nil) -> ([]byte, bool)Reads a Steam Cloud file (temp allocated). Returns false when there’s none
procedure. Source: steam.odin:251
Steam_CloudDelete
Section titled “Steam_CloudDelete”Steam_CloudDelete :: proc(file: string, game: ^Game = nil) -> boolDeletes a Steam Cloud file. Returns whether there was one
procedure. Source: steam.odin:258
Steam_CloudExists
Section titled “Steam_CloudExists”Steam_CloudExists :: proc(file: string, game: ^Game = nil) -> boolWhether a Steam Cloud file exists
procedure. Source: steam.odin:264
TouchControl_Defaults
Section titled “TouchControl_Defaults”TouchControl_Defaults :: proc(kind: SceneNodeKind) -> TouchControlSettingsA touch control kind’s default settings (a button is smaller); other kinds get a joystick’s
procedure. Source: touch.odin:93
Input_GetTouches
Section titled “Input_GetTouches”Input_GetTouches :: proc(game: ^Game = nil) -> []TouchEvery finger on the screen this frame (and those lifted this frame, released).
Inputs
- game: The game (nil: the current one)
Returns
- The touches (valid until the next update)
Example
for touch in e.Input_GetTouches() { if touch.pressed && !touch.claimed do e.Debug_Circle(scene, touch.position, 20, {255, 0, 0, 255}, seconds = 0.5)}procedure. Source: touch.odin:156
Input_GetTouchCount
Section titled “Input_GetTouchCount”Input_GetTouchCount :: proc(game: ^Game = nil) -> intHow many fingers are down (lifted ones not counted)
procedure. Source: touch.odin:163
Input_IsTouchScreen
Section titled “Input_IsTouchScreen”Input_IsTouchScreen :: proc(game: ^Game = nil) -> boolWhether the screen has been touched since the game started (touch controls show then)
procedure. Source: touch.odin:172
CreateVirtualJoystick
Section titled “CreateVirtualJoystick”CreateVirtualJoystick :: proc(scene: ^Scene, actions: [4]string = {}, settings := DEFAULT_TOUCH_CONTROL_SETTINGS) -> ^VirtualJoystickCreates a VirtualJoystick and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- actions: The input actions it pushes: left, right, up, down (“” for none)
- settings: Its radius, deadzone, zone, look
Returns
- The joystick
Example
stick := e.CreateVirtualJoystick(scene, {"move_left", "move_right", "move_up", "move_down"})stick.settings.floating = truestick.settings.zone = {400, 400}procedure. Source: touch.odin:193
CreateTouchButton
Section titled “CreateTouchButton”CreateTouchButton :: proc(scene: ^Scene, action := "", text := "", settings := TouchControlSettings{}) -> ^TouchButtonCreates a TouchButton and adds it to the scene (at the start of the next update).
Inputs
- scene: The scene
- action: The input action it presses (“” none: listen to
onPressed) - text: Its label
- settings: Its radius, zone, look (the radius defaults to TOUCH_BUTTON_RADIUS)
Returns
- The button
Example
jump := e.CreateTouchButton(scene, "jump", "A")jump.position = {1160, 620}procedure. Source: touch.odin:217
VirtualJoystick_GetVector
Section titled “VirtualJoystick_GetVector”VirtualJoystick_GetVector :: proc(joystick: ^VirtualJoystick) -> [2]f32A joystick’s position: -1..1 each way (x right, y down), the deadzone applied
procedure. Source: touch.odin:234
TouchButton_IsPressed
Section titled “TouchButton_IsPressed”TouchButton_IsPressed :: proc(button: ^TouchButton) -> boolWhether a touch button is held
procedure. Source: touch.odin:240
TouchControl_SetActions
Section titled “TouchControl_SetActions”TouchControl_SetActions :: proc(control: ^TouchControl, actions: [4]string)Sets the actions a touch control presses (a joystick’s left, right, up, down; a button’s first).
Inputs
- control: A VirtualJoystick or TouchButton
- actions: The action names (“” none)
procedure. Source: touch.odin:252
TouchControl_ParseActions
Section titled “TouchControl_ParseActions”TouchControl_ParseActions :: proc(text: string) -> (actions: [4]string)A control’s actions as scene files store them: comma-separated, a joystick’s left, right, up,
down ("move_left,move_right,move_up,move_down"; empty ones for none).
Inputs
- text: The comma-separated names
Returns
- The four names (slices of
text)
procedure. Source: touch.odin:267
TouchControl_FormatActions
Section titled “TouchControl_FormatActions”TouchControl_FormatActions :: proc(given: [4]string) -> stringThe actions as scene files store them (temp allocated; trailing empty ones left out)
procedure. Source: touch.odin:279
Players_Setup
Section titled “Players_Setup”Players_Setup :: proc(game: ^Game, settings: PlayerSettings)Sets the players settings (Game_LoadProject does it with the project’s).
Inputs
- game: The game
- settings: Max players, the keyboard, the join action (copied)
procedure. Source: players.odin:102
Players_StartJoining
Section titled “Players_StartJoining”Players_StartJoining :: proc(game: ^Game = nil)Turns joining on: a press of the join input on a free device adds a player
procedure. Source: players.odin:114
Players_StopJoining
Section titled “Players_StopJoining”Players_StopJoining :: proc(game: ^Game = nil)Turns joining off (the lobby is done)
procedure. Source: players.odin:119
Players_IsJoining
Section titled “Players_IsJoining”Players_IsJoining :: proc(game: ^Game = nil) -> boolWhether joining is on
procedure. Source: players.odin:124
Players_Join
Section titled “Players_Join”Players_Join :: proc(device: PlayerDeviceKind, gamepad := 0, game: ^Game = nil) -> intAdds a player with a device, from code.
Inputs
- device: The keyboard, or a gamepad
- gamepad: Which gamepad (its number among the connected ones, 0 the first) for a gamepad player
- game: The game (nil: the current one)
Returns
- The new player’s number; -1 when the game is full, the device is taken, or there’s no such gamepad
Example
// A one-player game: the keyboard is player 0e.Players_Join(.Keyboard)procedure. Source: players.odin:145
Players_Leave
Section titled “Players_Leave”Players_Leave :: proc(index: int, game: ^Game = nil)Removes a player. The players after it move down a number (player 2 becomes 1).
Inputs
- index: The player’s number
- game: The game (nil: the current one)
procedure. Source: players.odin:165
Players_Clear
Section titled “Players_Clear”Players_Clear :: proc(game: ^Game = nil)Removes every player
procedure. Source: players.odin:178
Players_Count
Section titled “Players_Count”Players_Count :: proc(game: ^Game = nil) -> intHow many players have joined
procedure. Source: players.odin:186
Players_Get
Section titled “Players_Get”Players_Get :: proc(index: int, game: ^Game = nil) -> ^PlayerA player (nil: no such player); valid until a player joins or leaves
procedure. Source: players.odin:192
Player_IsActionPressed
Section titled “Player_IsActionPressed”Player_IsActionPressed :: proc(player: int, name: string, game: ^Game = nil) -> boolWhether a player’s action is held
procedure. Source: players.odin:199
Player_IsActionJustPressed
Section titled “Player_IsActionJustPressed”Player_IsActionJustPressed :: proc(player: int, name: string, game: ^Game = nil) -> boolWhether a player’s action went down this frame
procedure. Source: players.odin:205
Player_IsActionJustReleased
Section titled “Player_IsActionJustReleased”Player_IsActionJustReleased :: proc(player: int, name: string, game: ^Game = nil) -> boolWhether a player’s action was let go this frame
procedure. Source: players.odin:211
Player_GetActionStrength
Section titled “Player_GetActionStrength”Player_GetActionStrength :: proc(player: int, name: string, game: ^Game = nil) -> f32How strongly a player’s action is pressed (0 to 1)
procedure. Source: players.odin:217
Player_GetAxis
Section titled “Player_GetAxis”Player_GetAxis :: proc(player: int, negative, positive: string, game: ^Game = nil) -> f32A player’s axis from two actions: positive minus negative (-1 to 1)
procedure. Source: players.odin:223
Player_GetVector
Section titled “Player_GetVector”Player_GetVector :: proc(player: int, negativeX, positiveX, negativeY, positiveY: string, game: ^Game = nil) -> [2]f32A player’s direction from four actions, at most 1 long (diagonals aren’t faster).
Inputs
- player: The player’s number
- negativeX, positiveX, negativeY, positiveY: Left, right, up, down
- game: The game (nil: the current one)
Returns
- The direction (y down)
procedure. Source: players.odin:238
Player_Rumble
Section titled “Player_Rumble”Player_Rumble :: proc(player: int, low, high: f32, seconds: f32, game: ^Game = nil) -> boolRumbles a gamepad player’s gamepad (low and high frequency motors 0 to 1). Returns whether it took
procedure. Source: players.odin:246
Player_DeviceName
Section titled “Player_DeviceName”Player_DeviceName :: proc(player: int, game: ^Game = nil) -> stringWhat a player plays with, for a lobby (“Keyboard”, the gamepad’s name, “(unplugged)”)
procedure. Source: players.odin:256
SplitScreen_Rect
Section titled “SplitScreen_Rect”SplitScreen_Rect :: proc(player, count: int, area: [2]f32, gap: f32 = 0) -> sdl.FRectWhere a player’s view goes on a split screen: the area split for count players (1: all of it;
2: side by side; 3: one on top, two below; 4: four corners; more: a grid of columns).
Inputs
- player: The player’s number (0 first)
- count: How many players share the screen
- area: The screen (the game’s size)
- gap: Pixels between views
Returns
- The player’s rectangle
Example
// Given: views: []^e.CameraView, one per playerfor view, i in views { r := e.SplitScreen_Rect(i, len(views), game.size) view.position = {r.x, r.y} view.size = {r.w, r.h}}procedure. Source: players.odin:288
Constants and variables
Section titled “Constants and variables”STEAM_STUB_BACKEND
Section titled “STEAM_STUB_BACKEND”STEAM_STUB_BACKEND :: SteamBackend{ name = "stub", start = stubStart, stop = stubStop, unlock = proc(steam: ^SteamState, name: string) -> bool { if name == "" do return false steam.stub.unlocked[strings.clone(name)] = true steam.stub.dirty = true log.infof("Steam (stub): achievement %s unlocked", name) stubWrite(steam) return true }, clear = proc(steam: ^SteamState, name: string) -> bool { if name in steam.stub.unlocked { key, _ := delete_key(&steam.stub.unlocked, name) delete(key) steam.stub.dirty = true stubWrite(steam) } return true }, isUnlocked = proc(steam: ^SteamState, name: string) -> bool { return steam.stub.unlocked[name] }, setStat = proc(steam: ^SteamState, name: string, value: f64) -> bool { if name == "" do return false if _, ok := steam.stub.stats[name]; ok { steam.stub.stats[name] = value } else { steam.stub.stats[strings.clone(name)] = value } steam.stub.dirty = true return true }, getStat = proc(steam: ^SteamState, name: string) -> (f64, bool) { value, ok := steam.stub.stats[name] return value, ok }, store = proc(steam: ^SteamState) -> bool { return stubWrite(steam) }, cloudWrite = proc(steam: ^SteamState, file: string, data: []byte) -> bool { name, ok := stubCloudName(steam, file) return ok && Platform_SaveWrite(name, data) }, cloudRead = proc(steam: ^SteamState, file: string) -> ([]byte, bool) { name, ok := stubCloudName(steam, file) if !ok do return nil, false return Platform_SaveRead(name) }, cloudDelete = proc(steam: ^SteamState, file: string) -> bool { name, ok := stubCloudName(steam, file) if !ok || !Platform_SaveExists(name) do return false Platform_SaveDelete(name) return true }, cloudExists = proc(steam: ^SteamState, file: string) -> bool { name, ok := stubCloudName(steam, file) return ok && Platform_SaveExists(name) }, // ... 1 more lines (see the source)The stub backend: achievements and stats kept in the game’s save storage (steam-stub), cloud
files as save files (steam-cloud-<file>), each unlock logged. Every game runs it until the
Steamworks backend exists.
constant. Source: steam.odin:274
DEFAULT_TOUCH_CONTROL_SETTINGS
Section titled “DEFAULT_TOUCH_CONTROL_SETTINGS”DEFAULT_TOUCH_CONTROL_SETTINGS :: TouchControlSettings{radius = 64, deadzone = 0.2, mouse = true, color = {255, 255, 255, 255}, opacity = 0.5}A joystick’s defaults (a TouchButton’s radius is TOUCH_BUTTON_RADIUS)
constant. Source: touch.odin:88
TOUCH_BUTTON_RADIUS
Section titled “TOUCH_BUTTON_RADIUS”TOUCH_BUTTON_RADIUS :: 40A TouchButton’s radius by default
constant. Source: touch.odin:90
MAX_LOCAL_PLAYERS
Section titled “MAX_LOCAL_PLAYERS”MAX_LOCAL_PLAYERS :: 8The most local players a game can have
constant. Source: players.odin:38
DEFAULT_PLAYER_SETTINGS
Section titled “DEFAULT_PLAYER_SETTINGS”DEFAULT_PLAYER_SETTINGS :: PlayerSettings{maxPlayers = 4, keyboard = true}Four players, the keyboard among them, joining with the default buttons
constant. Source: players.odin:80
