Project format and loading
The project and scene XML format, its parser, and building scenes from parsed data.
Files: project_format.odin, project_loader.odin.
File overviews
Section titled “File overviews”From project_format.odin:
Project and scene files: the XML format the editor saves and the engine loads.
A project is a folder
MyGame/ project.xml settings, the scene list and the asset list scenes/Menu.scene.xml one file per scene assets/... images, audio, fontsproject.xml:
<?xml version="1.0" encoding="UTF-8"?><project formatVersion="5" name="Asteroids" width="1280" height="720" scaleMode="Letterbox" background="#0f121e" startScene="MenuScene" pixelsPerMeter="100" gravityX="0" gravityY="980"> <scene name="MenuScene" path="scenes/MenuScene.scene.xml"/> <asset path="assets/ship.png" kind="Image"/> <asset path="data/fast_ship.data.json" kind="Data"/> <global script="GameState"> <prop name="lives" value="3"/> </global> <action name="jump" deadzone="0.5"> <key key="Space"/> <gamepad button="South"/> <axis axis="LeftY" direction="-1"/> </action></project>The optional renderer attribute picks the renderer games draw with: a RenderBackend name
(Auto, the default: SDL’s choice; OpenGL, OpenGLES2, Vulkan, GPU, Direct3D11,
Direct3D12, Metal, Software). See render_backend.odin.
<action> elements are the game’s input actions (input.odin): a name, a deadzone for sticks, and
its bindings: <key key="..."/> (a Key name), <mouse button="..."/> (MouseButton),
<gamepad button="..." device="0"/> (GamepadButton; no device = any gamepad) and
<axis axis="..." direction="1|-1" device="0"/> (GamepadAxis).
A scene file holds a tree of nodes. Nesting is the hierarchy, and a node’s x, y, rotation
and scale are relative to its parent:
<?xml version="1.0" encoding="UTF-8"?><scene formatVersion="4" name="PlayScene"> <node kind="Node2D" name="World" x="0" y="0"> <node kind="Camera" name="Camera" x="640" y="360" zoom="1"/> <node kind="Sprite" name="Ship" x="640" y="430" texture="assets/ship.png"> <node kind="AnimatedSprite" name="Flame" x="0" y="40" texture="assets/flame.png" columns="4" rows="1" fps="12"/> </node> <node kind="AnimatedSprite" name="Hero" x="200" y="400" texture="assets/hero.png" columns="4" rows="2" margin="0" spacing="2" autoplay="walk"> <animation name="walk" start="0" end="3" fps="10" loop="true"/> <animation name="jump" start="4" end="7" fps="12" playMode="PingPong"/> </node> </node> <node kind="Label" name="Title" x="640" y="180" text="ASTEROIDS" fontSize="64" color="#ebf0ff" centered="true" layer="7"/> <node kind="Menu" name="MainMenu" x="640" y="300" layer="7"> <item label="Play" action="scene:PlayScene"/> <item label="Quit" action="quit"/> </node> <node kind="Sprite" name="Player" x="640" y="600" texture="assets/ship.png"> <script name="PlayerController"> <prop name="speed" value="250"/> </script> </node> <node kind="StaticBody" name="Ground" x="640" y="700" collisionLayer="1"> <node kind="CollisionShape" shape="Rectangle" width="1280" height="40"/> </node> <node kind="DynamicBody" name="Crate" x="600" y="100" restitution="0.3"> <node kind="Sprite" texture="assets/crate.png"/> <node kind="CollisionShape" shape="Rectangle" width="48" height="48"/> </node> <node kind="Area" name="Goal" x="1200" y="640" collisionMask="2"> <node kind="CollisionShape" shape="Polygon" points="-20,20 20,20 0,-20"/> </node> <node kind="ParticleEmitter" name="Sparks" x="640" y="500" texture="assets/spark.png"> <particles amount="80" angle="270" angleSpread="40" angleVariance="1" colorRamp="0:#ffe664 0.5:#e63228 1:#28282800"/> </node></scene>An AnimatedSprite’s texture is a sprite sheet of columns x rows cells (margin pixels around
the grid, spacing between cells), numbered left to right, top to bottom from 0. Its
<animation> elements are named ranges of cells (start, end inclusive; end -1 is the last
cell), each with its fps, loop and playMode; autoplay names the one it plays from the start
(else the first). Without <animation> elements it plays one animation, “default”: every cell, at
the node’s fps, loop and playMode (the format before version 7). An animation’s durations
(milliseconds per frame) override its fps, and repeat plays a non-looping one that often.
With animations="assets/hero.aseprite" (an animation asset: an .anim.xml file or an Aseprite
file, AnimationSetData) it plays the asset’s sheet and animations instead of its own.
ParticleEmitter nodes (particles.odin) keep their settings in a <particles> child element: one
attribute per ParticleSettings field that differs from DEFAULT_PARTICLE_SETTINGS, named like
the field, in ParticleSettings_Get’s format. The node’s texture is the Texture shape’s image.
Physics (physics.odin, physics_nodes.odin): body nodes (StaticBody, DynamicBody, CharacterMover,
Area) get their shape from the CollisionShape nodes under them. Collision layers are lists of
layer numbers 1-64 (collisionLayer="1 3"), all, or empty for none.
Attributes every node may have: kind, name, x, y, rotation (degrees), scaleX,
scaleY, layer (0-7), visible, processMode (a ProcessMode: Inherit, Pausable,
WhenPaused, Always, Disabled), blendMode (a BlendMode: Blend, Add, Multiply, Modulate,
None), zIndex (draw order in the layer), ySort (children drawn by their y), groups (names,
separated by commas: groups="enemies,flying"). Kind-specific attributes are listed on SceneNodeData.
Missing attributes use the defaults in DEFAULT_SCENE_NODE. Paths are relative to the project
folder and use /. Colors are #rrggbb or #rrggbbaa. Booleans are true or 1; any other
value is false. A number or color that can’t be read keeps the default.
A scene’s root element can carry its own settings (SceneSettings, format 13): screenLayers
(layers drawn without the camera: HUDs, menus), background (instead of the project’s) and
gravityX / gravityY (instead of the project’s).
Current limits
- A scene’s Camera applies to every node on a layer that isn’t one of its
screenLayers, Labels and Menus included. - Labels and Menus follow their position only; their rotation and scale are ignored.
- Assets aren’t preloaded: images load with the nodes that use them, sounds and music with their sound nodes (and from code on first use); Font assets are listed but not used.
Version history
- 13: every node’s
processMode,blendMode,zIndex,ySortandgroups; CollisionShapeoneWay; the project’s window:fullscreen,vsync(default true),highDPI; assets of kind “Translation” (CSV tables: localization.odin), the project’slocaleandicon, and the editor’s<grid width height majorEvery opacity majorOpacity/>(GridSettings; games ignore it); the project’s sound output format (audioFrequency,audioChannels:AudioOutputSettings, 0 for the device’s own; stereo 48 kHz without them); scenes’ own settings (SceneSettings:screenLayers,background,gravityX/gravityYon the root element); animation assets (.anim.xmlfiles, kind “Animation”, and Aseprite files: animation_sets.odin), AnimatedSpriteanimations,<animation>durationsandrepeatandevents, thePingPongReverseplay mode;TileMapnodes (tilemap,frame,layers,tileCollision,animate,tag: tilemap.odin); Sprite and AnimatedSpritelayersandpivotSlice, SpritenineSlice,nineWidth,nineHeight, CollisionShapeslice(aseprite_nodes.odin); assets’filterand the project’stextureFilter(asset_import.odin); atlases (.atlas.xml, kind “Atlas”) and Spriteregion(atlas.odin); skeletons: Skeleton2D, Bone2D (boneLength), SkinnedMesh (texture,region, a<mesh vertices uvs triangles bones weights/>child) and IKChain (ikTip,ikTarget,ikLength,ikBend,ikEnabled) nodes (skeleton.odin); DialogueBox nodes (dialogue,startNode,boxWidth,boxHeight,typeSpeed,textSize,boxFont,boxPadding,boxColor,textColor,nameColor,pickColor,boxAutostart,hideWhenDone) and dialogue assets (.dialogue.xml, kind “Dialogue”: dialogue.odin); 2D lighting: scenes’lighting,ambient,bodyShadows, Light2D (lightKind,lightColor,energy,lightRadius,spotAngle,shadows) and LightOccluder (a CollisionShape’s shape attributes) nodes (lighting.odin); NavRegion (width,height,cellSize,agentRadius,diagonal) and NavAgent (speed,arriveDistance,avoidRadius,autoMove) nodes (navigation.odin); VirtualJoystick and TouchButton nodes (touchActions,touchRadius,deadzone,zoneWidth,zoneHeight,floating,touchVisibility,touchMouse,touchColor,touchOpacity; a button’s labeltext: touch.odin); ParallaxLayer (factorX,factorY,repeatX,repeatY), CameraZone (width,height,zoneTransition) and CameraView (width,height,viewCamera,viewZoom,border) nodes (camera_tools.odin); StateMachine nodes (start,animates,<param>,<state>,<transition>: state_machine.odin); UI controls (Panel, Button, ProgressBar, TextField, ScrollBox, RichText:width,height,anchor,margin,layout,align,justify,columns,padding,spacing,expand,disabled,value,min,max,direction,showValue,maxLength,patchMargins,textSize,textAlign,theme,placeholder,action: ui_controls.odin), assets of kind “Theme” (.theme.xml: ui_theme.odin) and the project’suiTheme; the project’stransition,transitionTime,transitionColorandloadingScene, ProgressBarshowLoading, Menu and Button actionspush:<Name>andpop(scene_flow.odin);Animatornodes (autoplay,speed,<clip>/<track>/<key>: animator.odin); LDtkLeveloneWayValues,areaValuesand<entity make script noScript>; bodies’material(assets of kind “PhysicsMaterial”,.material.xml: physics_material.odin); buses’<effect>children (audio_effects.odin) and a MusicPlayer’s<layer>children (music_layers.odin); VirtualJoystick and TouchButton nodes (touch.odin); the project’ssteamAppId,steamCloudand<achievement>children (steam.odin);<players max keyboard joinAction/>(players.odin); every node’sshaderand<shaderParam name value/>children, ScreenEffect nodes (effectScreenLayers), assets of kind “Shader” (.frag, compiled beside them as.frag.shader) and the project’sshaders(shaders.odin, screen_effects.odin); Line2D and Trail2D nodes (lineWidth,endWidth,lineColor,endColor,joint,roundCaps,closed,textureMode,lifetime,minDistance,emitting; a line’spoints: lines.odin); several<script>elements a node (script.odin); custom node types (kind="Player" baseKind="Sprite",<prop>children: custom_nodes.odin). Version 12 files are valid version 13 files. - 12:
LDtkLevelnodes (a level, or a whole world, of an LDtk project: ldtk_nodes.odin) and assets of kind “LDtk”. Version 11 files are valid version 12 files. - 11: the project’s
renderer. Version 10 files are valid version 11 files. - 10: sound nodes (AudioPlayer, AudioPlayer2D, MusicPlayer), mix buses (
<bus>in project.xml), assets of kind “Music”. Version 9 files are valid version 10 files. - 9: components (
<component>files, assets of kind “Component”) andComponentnodes. Version 8 files are valid version 9 files. - 8: input actions (
<action>in project.xml). Version 7 files are valid version 8 files. - 7: AnimatedSprite
<animation>elements,margin,spacingandautoplay. Version 6 files are valid version 7 files. - 6: ParticleEmitter nodes and their
<particles>element. Version 5 files are valid version 6 files. - 5: global scripts (
<global script="..."><prop name value/></global>in project.xml) and assets of kind “Data” (data resources,data/<name>.data.json: data.odin). Version 4 files are valid version 5 files. - 4: physics: StaticBody, DynamicBody, CharacterMover, Area, CollisionShape, RayCast and Joint
nodes, and the project’s
pixelsPerMeter,gravityX,gravityY,physicsDebugandphysicsSmoothing. Version 3 files are valid version 4 files. - 3: a node can have a script: a
<script name="...">child element holding<prop name="..." value="..."/>values for its exported fields (script.odin). Version 2 files are valid version 3 files without scripts. - 2: positions, rotation and scale are relative to the parent. A Camera’s rotation turns the view the way a rotated camera would (the world appears to turn the other way).
- 1: positions were world coordinates and parents had no effect; Camera rotation rotated the world. Version 1 scenes are converted when parsed.
Parsing (this file) only turns XML into plain data, without SDL, so it can be tested and used
by the editor. project_loader.odin builds engine scenes and nodes from that data.
SceneNodeKind
Section titled “SceneNodeKind”SceneNodeKind :: enum { // Groups other nodes; draws nothing Node2D, // A texture Sprite, // A sprite sheet animation AnimatedSprite, // Configures the scene's camera; not a drawn node Camera, // Text Label, // Vertical button menu whose buttons switch scenes or quit Menu, // Physics bodies (physics_nodes.odin); their CollisionShape children give them a shape StaticBody, DynamicBody, CharacterMover, Area, CollisionShape, // Casts a ray every physics step and reports the first hit RayCast, // Connects two physics bodies: a hinge, rope, spring, slider, weld or wheel Joint, // Emits particles: sparks, smoke, fire, rain (particles.odin) ParticleEmitter, // An instance of a component (a saved node tree): its nodes are built in its place Component, // Sound nodes (audio_nodes.odin): a sound; a sound in the world; streamed music AudioPlayer, AudioPlayer2D, MusicPlayer, // A level (or every level of a world) of an LDtk project: tiles, entities, collision LDtkLevel, // The tilemap layers of an Aseprite file: tiles, and collision from the tiles' properties // (format 13; tilemap.odin) TileMap, // Keyframe animations of other nodes' properties: clips of tracks (format 13; animator.odin) Animator, // 2D lighting (format 13; lighting.odin): a point or spot light; a shape that casts shadows Light2D, LightOccluder, // Navigation (format 13; navigation.odin): where characters can walk; a path finder moving its // parent NavRegion, NavAgent, // A state machine: states, transitions on parameters, animations per state (format 13; // state_machine.odin) StateMachine, // Camera tools (format 13; camera_tools.odin): a background moving slower than the world; a // room the camera stays inside; another camera's view in a rectangle ParallaxLayer, CameraZone, CameraView, // UI controls (format 13; ui_controls.odin): a box laying out controls; a button; a bar // showing a value; a line of text to type; a scrolling column; text with colors and sizes Panel, Button, ProgressBar, TextField, ScrollBox, RichText, // Touch controls (format 13; touch.odin): an on-screen stick pushing input actions; a round // button pressing one VirtualJoystick, TouchButton, // A shader over the whole scene: CRT, bloom, color grading (format 13; screen_effects.odin) ScreenEffect, // A thick line through points; a ribbon left behind its parent (format 13; lines.odin) Line2D, Trail2D, // Skeletal animation (format 13; skeleton.odin): a skeleton's root, a bone, a picture bent over // bones, a chain of bones reaching for a target Skeleton2D, Bone2D, SkinnedMesh, IKChain, // A conversation's box: lines typed out, options to choose (format 13; dialogue_box.odin) DialogueBox,}Node kinds a scene file can contain.
enum. Source: project_format.odin:215
SceneNodeCategory
Section titled “SceneNodeCategory”SceneNodeCategory :: enum { // Structure: grouping, the camera, component instances, levels Scene, // What draws: images, animations, particles Graphics, // Text and menus UI, // Bodies, shapes, rays and joints Physics, // Sounds and music Audio,}The groups tools list node kinds in (the editor’s Add Node dialog), in their display order.
enum. Source: project_format.odin:298
SceneNodeKindInfo
Section titled “SceneNodeKindInfo”SceneNodeKindInfo :: struct { // One line describing what the node does description: string, // The group it's listed in category: SceneNodeCategory,}What a node kind is, for tools that let users pick one (the editor’s Add Node dialog).
struct. Source: project_format.odin:323
NodeScriptData
Section titled “NodeScriptData”NodeScriptData :: struct { name: string, props: [dynamic]ScriptPropData,}A script after a node’s first, from a scene file: its name and field values
struct. Source: project_format.odin:389
NodeListenerData
Section titled “NodeListenerData”NodeListenerData :: struct { // The event: an EventSubject field of the node's type (`NodeType_Events`), "onFinished" or // through a field, "animation.onAnimationEnded" event: string, // The node whose script listens: a path from this node (`Node_FindPath`; "." for itself) target: string, // The handler's name (`Script_Listener`) handler: string, // A global's event instead of this node's: the global script's name ("GameState"). The // listener is then on the listening node (`target` is "." or empty) global: string,}A script listening to a node’s event, from a scene file:
<listener event="onFinished" target="../Hero" handler="onBloodFinished"/> inside the node.
Before the scene’s scripts get ready, the target node’s script’s handler (registered with
Script_Listener) is subscribed to the event (node_events.odin).
struct. Source: project_format.odin:400
MenuItemData
Section titled “MenuItemData”MenuItemData :: struct { // The button's text label: string, // `scene:<SceneName>` switches to that scene, `quit` quits the game action: string,}A button in a Menu node: <item label="Play" action="scene:PlayScene"/>.
struct. Source: project_format.odin:416
SceneNodeData
Section titled “SceneNodeData”SceneNodeData :: struct { // `kind`: what node it becomes (unknown or missing: Node2D) kind: SceneNodeKind, // `name` (missing: the kind's name, "Sprite") name: string, // Index of the parent node, -1 for top-level nodes. Nodes are stored depth-first: // every node's children follow it directly parent: int, // `x`, `y`: position relative to the parent, in pixels. Sprites and labels are centered on it // (labels unless `centered` is false) position: [2]f32, // Degrees clockwise, relative to the parent rotation: f32, // `scaleX`, `scaleY`: scale relative to the parent (1: unscaled) scale: [2]f32, // The render layer, 0-7 (out-of-range values are clamped) layer: int, // Hidden nodes, and everything under them, aren't drawn visible: bool, // Whether it updates while its scene is paused (Node.processMode) processMode: ProcessMode, // How its drawing combines with what's under it (Node.blendMode) blendMode: BlendMode, // Draw order within its layer (Node.zIndex) zIndex: i32, // Its children draw sorted by their y (Node.ySort) ySort: bool, // Groups it belongs to (Node.groups): `groups="enemies,flying"` groups: [dynamic]string,
// Sprite, AnimatedSprite, ParticleEmitter (its Texture shape): image path relative to the project folder texture: string, // Label: its text text: string, // Label: font size (default 24) fontSize: f32, // Label: text color (`#rrggbb` or `#rrggbbaa`; default white) color: sdl.Color, // Label: centered on its position (false: its top-left corner is there) centered: bool, // Camera: zoom (1: none, 2: twice as big) zoom: f32, // AnimatedSprite: the texture is a columns x rows grid of cells (at least 1 each) columns: int, // AnimatedSprite: rows of the sheet's grid (see `columns`) rows: int, // AnimatedSprite without `<animation>` elements: frames per second of its "default" animation fps: f32, // AnimatedSprite without `<animation>` elements: the "default" animation loops (default true). // Sound nodes read `loop` too, into `audio` (default false there) loop: bool, // AnimatedSprite without `<animation>` elements: the "default" animation's play mode playMode: AnimationPlayMode, // AnimatedSprite: the sheet's grid margin and spacing (pixels), its animations (none: one // "default" animation of every cell, from fps/loop/playMode above), and the one played first. // `margin`: pixels around the grid margin: f32, // AnimatedSprite: pixels between cells spacing: f32, // AnimatedSprite: its `<animation>` child elements, in order animations: [dynamic]AnimationData, // AnimatedSprite: the animation it plays from the start ("" or unknown: the first; with an // animation asset, "" is the asset's own) autoplay: string, // AnimatedSprite: `animations="assets/hero.anim.xml"`, an animation asset (an `.anim.xml` file, // or an Aseprite file: its tags) whose sheet and animations it plays instead of its own // (format 13; animation_sets.odin) animationSet: string, // Sprite, AnimatedSprite with an Aseprite image: `layers="Body,Sword"`, the layers drawn (by // name; a group's name brings its layers), "" for every visible one (format 13; // aseprite_nodes.odin) asepriteLayers: string, // Sprite, AnimatedSprite: `pivotSlice`, an Aseprite slice whose pivot goes on the node's origin // (frame by frame; "" for the image's middle) pivotSlice: string, // Sprite: `nineSlice`, a 9-patch Aseprite slice drawn stretched to `nineWidth` x `nineHeight` nineSlice: string, // Sprite: `region`, the region of the atlas its `texture` names (an `.atlas.xml` file: // atlas.odin; format 13) region: string, // Sprite: the 9-patch's size in pixels nineSize: [2]f32, // CollisionShape: `slice`, an Aseprite slice of its sprite whose rectangle it takes, frame by // frame (a hitbox; off where the slice is hidden; aseprite_nodes.odin) slice: string, // Menu: its `<item>` child elements, top to bottom items: [dynamic]MenuItemData, // Any kind: the script attached to the node (`<script name="...">`; "" for none) script: string, // Any kind: the script's exported field values (its `<prop>` elements) props: [dynamic]ScriptPropData, // Any kind: more scripts after the first (`<script>` elements after the first, format 13) moreScripts: [dynamic]NodeScriptData, // A custom node type's name (`kind="Player"`, `kind` then is its `baseKind`; "" none) and its // exported fields' values (`<prop name value/>` children of the node): custom_nodes.odin customKind: string, customProps: [dynamic]ScriptPropData, // Any kind: scripts listening to its events (`<listener event target handler/>`, format 13) listeners: [dynamic]NodeListenerData,
// StaticBody, DynamicBody, CharacterMover, Area: the layers it's on and collides with // (`collisionLayer="1 3"`, `collisionMask="all"`), and its surface. // `collisionLayer`: the layers it's on, as bits (layer n is bit n-1; default layer 1) collisionLayer: u64, // Bodies: the layers it collides with (default all). RayCast: the layers it hits collisionMask: u64, // Bodies: surface friction (default 0.6) friction: f32, // Bodies: bounciness, 0 to 1 (default 0) restitution: f32, // Bodies: density, for the mass of dynamic bodies (default 1) density: f32, // Bodies: a physics material (`.material.xml`, physics_material.odin) whose friction, // restitution and density replace the three above ("": none) material: string, // DynamicBody: multiplies the project's gravity (default 1) gravityScale: f32, // DynamicBody: slows its movement over time (0: none) linearDamping: f32, // DynamicBody: slows its spin over time (0: none) angularDamping: f32, // DynamicBody: it doesn't rotate fixedRotation: bool, // DynamicBody: continuous collision for fast bodies (doesn't pass through thin walls) bullet: bool, // CharacterMover: the steepest floor, degrees floorMaxAngle: f32, // CollisionShape: `shape`; Rectangle `width` and `height`; Circle `radius`; Capsule `radius` // and `height` (overall); Polygon and Segment `points="x,y x,y ..."`; `disabled`; `oneWay` // (format 13: only its top stops what comes down on it). // `shape`: Rectangle (default), Circle, Capsule, Polygon or Segment shape: CollisionShapeKind, // CollisionShape: `width`, `height` in pixels (Rectangle; a Capsule's overall height is `.y`) shapeSize: [2]f32, // CollisionShape: Circle and Capsule radius, in pixels radius: f32, // CollisionShape: Polygon and Segment points, in the node's space (`points="x,y x,y ..."`) points: [dynamic][2]f32, // CollisionShape: left out of its body disabled: bool, // CollisionShape (format 13): a one-way platform, only its top stops what comes down on it oneWay: bool, // RayCast: `targetX`, `targetY` (relative to the node), `enabled`, `excludeParent` // (collisionMask is the layers it hits). // `targetX`, `targetY`: the ray's end, relative to the node target: [2]f32, // RayCast: casting (default true) enabled: bool, // RayCast: ignore the body it's under (default true) excludeParent: bool, // Joint (physics_joints.odin): `joint` (the kind), `bodyA` / `bodyB` (names or paths; bodyA // "" = the body it's under, or the world), `offsetX`/`offsetY` (Distance: body B's anchor), // `limit`, `lower`, `upper`, `motor`, `motorSpeed`, `maxMotor`, `spring`, `hertz`, `damping`, // `collideConnected`. // `joint`: the joint's kind (default Revolute) jointKind: JointKind, // Joint: name or path of body A ("" = the body the joint is under, or the world) bodyA: string, // Joint: name or path of body B bodyB: string, // Joint: `offsetX`, `offsetY`: Distance joints' anchor on body B, relative to the joint jointOffset: [2]f32, // Joint: `limit`: `lower` and `upper` apply enableLimit: bool, // Joint: the lower limit (Revolute: degrees; Distance: length in px; Prismatic, Wheel: px) lower: f32, // Joint: the upper limit, in `lower`'s units upper: f32, // Joint: `motor`: the motor runs enableMotor: bool, // Joint: motor speed (Revolute, Wheel: degrees per second; Distance, Prismatic: px/s) motorSpeed: f32, // Joint: the motor's maximum torque (Revolute, Wheel) or force (Distance, Prismatic) maxMotor: f32, // Joint: `spring`: the joint is springy enableSpring: bool, // Joint: the spring's frequency, in hertz hertz: f32, // Joint: `damping`: the spring's damping ratio (default 0.5) dampingRatio: f32, // Joint: the two bodies still collide with each other collideConnected: bool, // ParticleEmitter: its `<particles>` element (particles.odin) particles: ParticleSettings, // Component: the component file it's an instance of (relative to the project folder) component: string, // AudioPlayer, AudioPlayer2D, MusicPlayer: the sound (or music) file, the bus ("" Master), // and `volume`, `pitch`, `pan`, `loop`, `autostart`, `maxDistance`, `attenuation`, `fadeIn`, `fadeOut`. // `sound`: the sound (or music) file, relative to the project folder sound: string, // MusicPlayer: layers played in step with it (`<layer name sound volume/>` children: // music_layers.odin) musicLayers: [dynamic]MusicLayerData, // Sound nodes: the mix bus it plays through ("" for Master) bus: string, // Sound nodes: `volume`, `pitch`, `pan`, `loop`, `autostart`, `maxDistance`, `attenuation`, // `fadeIn`, `fadeOut` (missing ones from DEFAULT_AUDIO_SETTINGS) audio: AudioSettings, // LDtkLevel: `ldtk` (the .ldtk file), `level`, `allLevels`, `intGridCollision`, // `collisionValues` (solid), `oneWayValues`, `areaValues` (format 13), `spawnEntities`, // `background`, `smoothTiles`, and `<entity name="..." component="..." make="..." script="..." // noScript="true"/>` children (`make` and the script ones format 13: LDtkEntityRule); the // generated collision uses `collisionLayer`, `collisionMask`, `friction`, `restitution` // (ldtk_nodes.odin) ldtk: LDtkLevelSettings, // TileMap: `tilemap` (an Aseprite file), `frame`, `layers`, `tileCollision` (tilemap.odin); its // collision uses `collisionLayer`, `collisionMask`, `friction`, `restitution` too tilemap: TileMapSettings, // Animator: `autoplay` (the clip it starts with), `speed`, and `<clip name length loop>` // children holding `<track node property step>` children holding `<key time value ease/>` // (animator.odin) animator: AnimatorSettings, // UI controls (Panel, Button, ProgressBar, TextField, ScrollBox, RichText): their // `UISettings` as attributes of the same names (`width` / `height` for size, `patchMargins` // "l t r b"); their text in `text`, an image in `texture` (ui_controls.odin) ui: UISettings, // Light2D: `lightKind`, `color`... see lighting.odin (`LightSettings`: `lightKind`, // `lightColor`, `energy`, `lightRadius`, `spotAngle`, `shadows`). A LightOccluder's shape uses // `shape`, `width` / `height`, `radius` and `points` like a CollisionShape's light: LightSettings, // NavRegion (its area: `width`, `height`) and NavAgent: `NavSettings` as `cellSize`, // `agentRadius`, `diagonal`, `speed`, `arriveDistance`, `avoidRadius`, `autoMove` // (navigation.odin) nav: NavSettings, // StateMachine: `start`, `animates` (its AnimatedSprite's path), and `<param name type value>`, // `<state name animation x y>`, `<transition from to when after onFinished>` children // (state_machine.odin) stateMachine: StateMachineSettings, // ParallaxLayer (`factorX`, `factorY`, `repeatX`, `repeatY`; its image `texture`), CameraZone // (`width`, `height`, `zoneTransition`), CameraView (`width`, `height`, `viewCamera`, // `viewZoom`, `border`): camera_tools.odin cameraTools: CameraToolSettings, // CameraView: the Camera node it shows (a path from it) viewCamera: string, // VirtualJoystick, TouchButton: `TouchControlSettings` as `touchRadius`, `deadzone`, // `zoneWidth`, `zoneHeight`, `floating`, `touchVisibility`, `touchMouse`, `touchColor`, // `touchOpacity` (touch.odin; a button's label is `text`) touch: TouchControlSettings, // VirtualJoystick, TouchButton: the input actions it presses, comma-separated (a joystick's // left, right, up, down; a button's one): `touchActions` touchActions: string, // Any node: its shader (`shader`: "builtin:outline" or a project path; "" none) and its // parameters' values (`<shaderParam name value/>` children): shaders.odin shader: string, shaderParams: [dynamic]ScriptPropData, // ScreenEffect: the effect covers the screen layers too (`effectScreenLayers`, default true) effectScreenLayers: bool, // Line2D (its `points`, `texture`) and Trail2D (`texture`): `LineSettings` as `lineWidth`, // `endWidth`, `lineColor`, `endColor`, `joint`, `roundCaps`, `closed`, `textureMode`, // `lifetime`, `minDistance`, `emitting` (lines.odin) line: LineSettings, // Bone2D: `boneLength` (skeleton.odin) boneLength: f32, // SkinnedMesh (its `texture`, `region`): the `<mesh vertices uvs triangles bones weights/>` child mesh: MeshData, // IKChain: `ikTip`, `ikTarget` (paths from it), `ikLength`, `ikBend` ("positive" / "negative"), // `ikEnabled` ik: IKSettings, ikTip, ikTarget: string, // DialogueBox: `dialogue` (a `.dialogue.xml` asset), `startNode`, and `DialogueBoxSettings` as // `boxWidth`, `boxHeight`, `typeSpeed`, `textSize`, `boxFont`, `boxPadding`, `boxColor`, // `textColor`, `nameColor`, `pickColor`, `boxAutostart`, `hideWhenDone` dialogue, dialogueStart: string, dialogueBox: DialogueBoxSettings,}One node from a scene file. Fields that don’t apply to a node’s kind are ignored. Each field is
read from the attribute of the same name unless its comment names another one; a missing (or
unreadable) attribute keeps the value in DEFAULT_SCENE_NODE.
struct. Source: project_format.odin:428
AnimationData
Section titled “AnimationData”AnimationData :: struct { // The name scripts play it by (missing: "default") name: string, // First and last cell played, inclusive; end -1 is the sheet's last cell start: int, // The last cell played (inclusive; -1, the default: the sheet's last cell) end: int, // Frames per second (default 8) fps: f32, // Plays again from the start when it ends (default false, unlike the node's `loop`) loop: bool, // Forward (default), or another `AnimationPlayMode` playMode: AnimationPlayMode, // Seconds each frame shows (`durations`, milliseconds in the file; empty: 1 / fps each) durations: [dynamic]f32, // Times a non-looping animation plays (0 or 1: once) repeat: int, // Each frame's events (`events="step;;hit,shake"`: frames apart by ';', a frame's events by // ','; "" for none): an Aseprite file's from its cels' text or `event` property events: [dynamic]string,}An AnimatedSprite’s animation (or an animation asset’s): a named range of its sheet’s cells.
<animation name="walk" start="0" end="3" fps="10" loop="true" playMode="Forward"/><animation name="hit" start="4" end="6" durations="100 60 200" repeat="2"/>durations (milliseconds per frame, format 13) override fps for the frames they cover; repeat
plays a non-looping animation that many times.
struct. Source: project_format.odin:703
AnimationSetData
Section titled “AnimationSetData”AnimationSetData :: struct { // The sheet: an image path relative to the project folder (an Aseprite sheet's ends in // ASEPRITE_SHEET_SUFFIX) texture: string, // The sheet's grid columns, rows: int, // Pixels around the grid, and between cells margin, spacing: f32, // The animation played first ("": the first) autoplay: string, // Its animations, in order animations: [dynamic]AnimationData,}An animation asset: a sheet and named animations over its cells, which AnimatedSprite nodes (and
scripts: AnimatedSprite_SetAnimations) share. An .anim.xml file (asset kind “Animation”):
<?xml version="1.0" encoding="UTF-8"?><animations formatVersion="13" texture="assets/hero.png" columns="8" rows="2" autoplay="idle"> <animation name="idle" start="0" end="3" fps="6" loop="true"/> <animation name="run" start="8" end="15" fps="12" loop="true"/></animations>An Aseprite file is one too, read by AnimationSet_FromAseprite: its frames on a sheet, a
animation per tag.
struct. Source: project_format.odin:738
ScriptPropData
Section titled “ScriptPropData”ScriptPropData :: struct { // The exported field's name name: string, // The value in its text form (see `Script_SetField`) value: string,}A script field’s value in a scene file: <prop name="speed" value="250"/>
struct. Source: project_format.odin:828
SceneData
Section titled “SceneData”SceneData :: struct { // The root element's `name` name: string, // Every node, depth-first: a node's children follow it directly (`SceneNodeData.parent`) nodes: [dynamic]SceneNodeData, // The root element's scene-wide settings (format 13) settings: SceneSettings,}A parsed scene (or component) file: ParseScene, ParseComponent. Free with SceneData_Destroy.
struct. Source: project_format.odin:878
SceneSettings
Section titled “SceneSettings”SceneSettings :: struct { // Layers drawn in screen space, ignoring the camera (`Scene.screenLayers`): HUDs and menus. // `screenLayers="7 8"` (layer numbers 1-8) screenLayers: bit_set[RenderLayer], // Clear the screen with `background` instead of the project's color (`background="#rrggbb"`) hasBackground: bool, // The scene's background color, when `hasBackground` background: sdl.Color, // Use `gravity` instead of the project's (`gravityX`, `gravityY`) hasGravity: bool, // The scene's gravity in px/s², y down, when `hasGravity` gravity: [2]f32, // 2D lighting (lighting.odin): the world layers are dark outside lights (`lighting="true"`) lighting: bool, // The light everywhere (`ambient="#rrggbb"`; zero: `DEFAULT_AMBIENT`) ambient: sdl.Color, // Static bodies' shapes cast shadows (`bodyShadows="true"`) bodyShadows: bool,}A scene’s own settings, from its file’s root element (format 13):
<scene name="Level" screenLayers="8" background="#203040" gravityX="0" gravityY="490">.
Scene_Build applies them to the scene. The zero value is a scene without any.
struct. Source: project_format.odin:892
SceneRef
Section titled “SceneRef”SceneRef :: struct { // The scene's name (what `game->switchScene` and menu actions use) name: string, // Scene file path relative to the project folder path: string,}A scene of a project: <scene name="MenuScene" path="scenes/MenuScene.scene.xml"/>
struct. Source: project_format.odin:957
AssetRef
Section titled “AssetRef”AssetRef :: struct { // The file, relative to the project folder path: string, // "Image", "Audio", "Music", "Font", "Data", "Component", "LDtk" or "Translation" kind: string, // An image's filtering (`filter="Pixel"`, format 13; Default: the project's // `textureFilter`; asset_import.odin) filter: TextureFilter, // A TrueType font's MSDF atlas (`msdf="true"`, format 13): its text drawn sharp at any size on // the GPU renderer (fonts.odin; editors make the atlas) msdf: bool,}An asset a project lists: <asset path="assets/ship.png" kind="Image"/>
struct. Source: project_format.odin:965
ProjectData
Section titled “ProjectData”ProjectData :: struct { // The game's name (`name`) name: string, // `width`, `height`: the game's size in pixels (default 1280 x 720) size: [2]f32, // How the game fits its window (`scaleMode`; default Letterbox) scaleMode: ScaleMode, // The color behind everything (`background`; default black) background: sdl.Color, // The scene the game starts in (`startScene`; missing: the first scene listed) startScene: string, // Its scenes, in file order (`<scene>` elements) scenes: [dynamic]SceneRef, // Its assets, in file order (`<asset>` elements) assets: [dynamic]AssetRef, // pixelsPerMeter, gravityX / gravityY (px/s²), physicsDebug, physicsSmoothing physics: PhysicsSettings, // Global scripts, in start order: `<global script="GameState"><prop .../></global>` (globals.odin) globals: [dynamic]GlobalData, // Input actions, in order: `<action name="jump"><key key="Space"/></action>` (input.odin) actions: [dynamic]InputActionData, // Mix buses (Master's settings too, if listed): `<bus name="Music" volume="0.8"/>` (audio_nodes.odin) buses: [dynamic]AudioBusData, // Steam (steam.odin): `steamAppId`, `steamCloud`, and `<achievement name title description // hidden/>` children (format 13) steam: SteamSettings, achievements: [dynamic]SteamAchievementData, // Local players (players.odin): `<players max="4" keyboard="true" joinAction="join"/>` players: PlayerSettings, // The renderer games draw with (`renderer="Vulkan"`, format 11; Auto: SDL's choice, or the // GPU renderer when the project uses shaders) renderer: RenderBackend, // Some scene uses a shader (`shaders="true"`, written by editors): Auto then means GPU usesShaders: bool, // The game's window (`fullscreen`, `vsync`, `highDPI`, format 13) window: WindowSettings, // The game window's icon: an image path relative to the project (`icon="assets/icon.png"`, // format 13); "" for the engine's (the Crossfire logo) icon: string, // How images are filtered when they're drawn scaled or turned, unless an asset says otherwise // (`textureFilter="Pixel"`, format 13; Default: Smooth, Aseprite files Pixel; asset_import.odin) textureFilter: TextureFilter, // The language to start in (`locale="fr"`, format 13); "" for the player's system language. // The translation tables are the assets of kind "Translation" (localization.odin) locale: string, // The UI theme controls use when they and their parents name none (`uiTheme`, a `.theme.xml` // asset, format 13; ui_theme.odin) uiTheme: string, // How scene changes look (`transition`: a `TransitionKind`, `transitionTime` seconds, // `transitionColor`; format 13; scene_flow.odin) transition: Transition, // The scene shown while scenes load (`loadingScene`; "": none: every scene is built at start) loadingScene: string, // The editor's grid for this project (`<grid width="16" height="16"/>`, format 13); games // don't use it grid: GridSettings, // The format the game's sound output asks for (`audioFrequency`, `audioChannels`, format 13; // 0 for the device's own). The device isn't the project's: the player's `--audio-device` audioOutput: AudioOutputSettings,}A parsed project.xml (ParseProject, LoadProjectFile). Free with ProjectData_Destroy.
struct. Source: project_format.odin:981
GridSettings
Section titled “GridSettings”GridSettings :: struct { // A cell's width and height, world units (GRID_SIZE_RANGE) size: [2]int, // A major (more opaque) line every this many cells; 0 = none (GRID_MAJOR_RANGE) majorEvery: int, // The lines' opacity, percent (the editor draws them dark gray) opacity: int, // The major lines' opacity, percent majorOpacity: int,}A project’s grid, as tools show it and snap to it (the editor’s viewport). Games don’t use it.
struct. Source: project_format.odin:1045
WindowSettings
Section titled “WindowSettings”WindowSettings :: struct { // Start fullscreen (the desktop's resolution; the game scales into it) fullscreen: bool, // Wait for the display's refresh (no tearing; the frame rate follows the display) vsync: bool, // Use the display's full pixel density (sharper on high-DPI and Retina screens; more pixels to draw) highDPI: bool,}How a game’s window starts. Games change fullscreen and vsync while running with
Game_SetFullscreen / Game_SetVSync (an options menu).
struct. Source: project_format.odin:1068
ProjectError
Section titled “ProjectError”ProjectError :: enum { None, // The file couldn't be read (`LoadProjectFile`, `LoadSceneFile`) File_Not_Found, // The text isn't well-formed XML, or is empty Invalid_XML, // The root element isn't <project> / <scene> / <component>, as the parser expected Wrong_Root, // formatVersion is newer than PROJECT_FORMAT_VERSION Unsupported_Version,}What went wrong reading a project, scene or component file
enum. Source: project_format.odin:1081
Sprite
Section titled “Sprite”Sprite :: struct { // The node part: transform, hierarchy, hooks using node: Node, // The image it draws (the resource manager's; nil draws nothing) texture: ^sdl.Texture, // Its Aseprite file, when the image is one (full path, node arena; "" otherwise): its slices source: string, // The slice whose pivot is drawn at the origin ("": the middle; aseprite_nodes.odin) pivotSlice: string, // A 9-patch slice it draws stretched to `nineSize` ("": the whole image) nineSlice: string, // The 9-patch's size (0: the slice's own) nineSize: [2]f32, // The part of `texture` it draws, in pixels: an atlas region (atlas.odin; zero: all of it) region: sdl.FRect,}A texture drawn centered on the node’s origin. Created from Sprite nodes in scene files; its size comes from the texture and the node’s scale.
struct. Source: project_loader.odin:16
AnimatedSprite
Section titled “AnimatedSprite”AnimatedSprite :: struct { // The node part: transform, hierarchy, hooks using node: Node, // Its animations and the one playing (updated with the node; allocated in the node's arena) animation: AnimationPlayer, // Its Aseprite file, when its sheet is one (full path, node arena; "" otherwise): its slices source: string, // Its cells are the file's frames (an Aseprite file's animations), so slices follow them sourceFrames: bool, // The slice whose pivot is drawn at the origin, frame by frame ("": the middle) pivotSlice: string, // The Aseprite layers it draws ("": every visible one; node arena) layers: string,}A sprite sheet animation drawn centered on the node’s origin. Created from AnimatedSprite nodes:
its player holds the node’s animations (by name; “default” when the scene file has none) and
starts the autoplay one. Scripts switch with AnimationPlayer_Play(&sprite.animation, "jump").
struct. Source: project_loader.odin:38
Scene_Instantiate
Section titled “Scene_Instantiate”Scene_Instantiate :: Scene_AddComponentAnother name for Scene_AddComponent (its first name)
type. Source: project_loader.odin:341
Procedures
Section titled “Procedures”ParseAnimationSet
Section titled “ParseAnimationSet”ParseAnimationSet :: proc(text: string, allocator := context.allocator) -> (set: AnimationSetData, err: ProjectError)Reads an .anim.xml file.
Inputs
- text: The XML
- allocator: For the result’s strings and lists
Returns
- The animation set (free it with
AnimationSetData_Destroy), and .None or what went wrong
procedure. Source: project_format.odin:762
AnimationSetData_Destroy
Section titled “AnimationSetData_Destroy”AnimationSetData_Destroy :: proc(set: ^AnimationSetData, allocator := context.allocator)Frees an animation set’s strings and lists. Must use the allocator it was made with.
Inputs
- set: The set
- allocator: Its allocator
procedure. Source: project_format.odin:788
FormatScreenLayers
Section titled “FormatScreenLayers”FormatScreenLayers :: proc(layers: bit_set[RenderLayer], allocator := context.allocator) -> stringFormats screen layers as scene files store them: layer numbers 1-8 separated by spaces (“” for none).
Inputs
- layers: The layers
- allocator: For the text
Returns
- The text, e.g. “7 8”
procedure. Source: project_format.odin:923
ParseScreenLayers
Section titled “ParseScreenLayers”ParseScreenLayers :: proc(text: string) -> (bit_set[RenderLayer], bool)Parses screen layers: layer numbers 1-8 separated by spaces or commas (“” = none).
Inputs
- text: The text
Returns
- The layers, and false if a part wasn’t a layer number (the valid ones are kept)
procedure. Source: project_format.odin:941
ParseProject
Section titled “ParseProject”ParseProject :: proc(text: string, allocator := context.allocator) -> (project: ProjectData, err: ProjectError)Parses the text of a project.xml file.
Inputs
- text: The XML
- allocator: Allocator for the result’s strings and lists
Returns
- The project data, and .None or what went wrong. Free with
ProjectData_Destroy
Example
// Given: xmlText: stringproject, err := e.ParseProject(xmlText)if err != .None do log.errorf("Can't read the project: %v", err)defer e.ProjectData_Destroy(&project)procedure. Source: project_format.odin:1109
ParseScene
Section titled “ParseScene”ParseScene :: proc(text: string, allocator := context.allocator) -> (scene: SceneData, err: ProjectError)Parses the text of a scene file.
Inputs
- text: The XML
- allocator: Allocator for the result’s strings and lists
Returns
- The scene data with its nodes in depth-first order, and .None or what went wrong.
Free with
SceneData_Destroy
Example
// Given: xmlText: stringdata, err := e.ParseScene(xmlText)if err != .None do log.errorf("Can't read the scene: %v", err)defer e.SceneData_Destroy(&data)for n in data.nodes do fmt.println(n.kind, n.name)procedure. Source: project_format.odin:1262
ParseComponent
Section titled “ParseComponent”ParseComponent :: proc(text: string, allocator := context.allocator) -> (component: SceneData, err: ProjectError)Parses the text of a component file: one node tree saved to be reused in any scene.
<?xml version="1.0" encoding="UTF-8"?><component formatVersion="9" name="Ship"> <node kind="Sprite" name="Ship" texture="assets/ship.png"> <node kind="ParticleEmitter" name="Exhaust" y="30"/> </node></component>Its first top-level node is the root. A component has one root: top-level nodes after it are
read as the root’s last children, their values as they are (an older editor let nodes be added
beside the root; they were dropped). Scenes use it through a Component
node, <node kind="Component" name="Ship2" x="100" y="50" component="components/ship.component.xml"/>:
the component’s nodes are built in its place, the root taking the instance’s name, transform,
layer and visibility (Scene_Build); Scene_AddComponent adds one from code.
Inputs
- text: The XML
- allocator: Allocator for the result’s strings and lists
Returns
- The nodes (depth-first, the root first), and .None or what went wrong. Free with
SceneData_Destroy
procedure. Source: project_format.odin:1316
ProjectData_Destroy
Section titled “ProjectData_Destroy”ProjectData_Destroy :: proc(project: ^ProjectData, allocator := context.allocator)Frees everything ParseProject allocated and zeroes the project. Must use the same allocator.
Inputs
- project: The project data
- allocator: The allocator it was parsed with
procedure. Source: project_format.odin:1340
SceneData_Destroy
Section titled “SceneData_Destroy”SceneData_Destroy :: proc(scene: ^SceneData, allocator := context.allocator)Frees everything ParseScene (or ParseComponent) allocated and zeroes the scene. Must use the
same allocator.
Inputs
- scene: The scene data
- allocator: The allocator it was parsed with
procedure. Source: project_format.odin:1382
FormatColor
Section titled “FormatColor”FormatColor :: proc(color: sdl.Color, allocator := context.allocator) -> stringFormats a color the way the format stores it: #rrggbb, or #rrggbbaa when not opaque.
Inputs
- color: The color
- allocator: Allocator for the string
Returns
- The color text
procedure. Source: project_format.odin:1468
ParseColor
Section titled “ParseColor”ParseColor :: proc(text: string) -> (sdl.Color, bool)Parses #rrggbb or #rrggbbaa.
Inputs
- text: The color text
Returns
- The color, and false if the text isn’t a valid color
procedure. Source: project_format.odin:1482
FormatPhysicsLayers
Section titled “FormatPhysicsLayers”FormatPhysicsLayers :: proc(layers: u64, allocator := context.allocator) -> stringFormats collision layers the way the format stores them: all, the layer numbers (“1 3”), or “”
for none.
Inputs
- layers: The layer bits (layer n is bit n-1)
- allocator: Allocator for the string
Returns
- The text
procedure. Source: project_format.odin:1502
ParsePhysicsLayers
Section titled “ParsePhysicsLayers”ParsePhysicsLayers :: proc(text: string) -> (u64, bool)Parses collision layers: all, or layer numbers 1-64 separated by spaces or commas (“” = none).
Inputs
- text: The layers’ text
Returns
- The layer bits (layer n is bit n-1), and false if a part isn’t a layer number 1-64 (it’s skipped; the others are still in the bits)
procedure. Source: project_format.odin:1523
FormatPoints
Section titled “FormatPoints”FormatPoints :: proc(points: [][2]f32, allocator := context.allocator) -> stringFormats points the way the format stores them: “x,y x,y”.
Inputs
- points: The points
- allocator: Allocator for the string
Returns
- The text
procedure. Source: project_format.odin:1550
ParsePoints
Section titled “ParsePoints”ParsePoints :: proc(text: string) -> ([][2]f32, bool)Parses points: “x,y x,y …”.
Inputs
- text: The points’ text
Returns
- The points (temp allocated), and false if one couldn’t be read (it’s skipped)
procedure. Source: project_format.odin:1568
LoadProjectFile
Section titled “LoadProjectFile”LoadProjectFile :: proc(path: string, allocator := context.allocator) -> (ProjectData, ProjectError)Reads and parses a project.xml file.
Inputs
- path: Path to project.xml
- allocator: Allocator for the result. Free with
ProjectData_Destroy
Returns
- The project data, and .None or what went wrong (.File_Not_Found when it can’t be read)
Example
project, err := e.LoadProjectFile("MyGame/project.xml")if err != .None do log.errorf("Can't load the project: %v", err)defer e.ProjectData_Destroy(&project)procedure. Source: project_loader.odin:68
LoadSceneFile
Section titled “LoadSceneFile”LoadSceneFile :: proc(path: string, allocator := context.allocator) -> (SceneData, ProjectError)Reads and parses a scene file.
Inputs
- path: Path to the .scene.xml file
- allocator: Allocator for the result. Free with
SceneData_Destroy
Returns
- The scene data, and .None or what went wrong (.File_Not_Found when it can’t be read)
procedure. Source: project_loader.odin:84
Game_LoadProject
Section titled “Game_LoadProject”Game_LoadProject :: proc(game: ^Game, project: ^ProjectData, projectDir: string) -> boolCreates every scene of a project in a game and switches to its start scene. Also sets the game’s
background color, physics settings, name (unless code set one), input actions, mix buses,
translations and global scripts from the project. Call from the game’s build procedure, after
game.size and game.scaleMode were set from the project (the scenes’ cameras and menus are
positioned for that size).
Asset paths are resolved against the project folder, so the game can run from any directory.
Inputs
- game: The game, during Game_Init
- project: The parsed project.xml
- projectDir: Folder containing project.xml
Returns
- false if a scene file couldn’t be loaded (the others are still created)
Example
// In the game's build procedure (game.size and game.scaleMode already set from the project)project, err := e.LoadProjectFile("MyGame/project.xml")if err != .None do returnif !e.Game_LoadProject(game, &project, "MyGame") do log.warn("Some scenes didn't load")procedure. Source: project_loader.odin:113
Scene_Build
Section titled “Scene_Build”Scene_Build :: proc(scene: ^Scene, data: ^SceneData, projectDir: string, allocator := context.temp_allocator, attachScripts := true) -> []^NodeCreates a scene’s nodes from parsed scene data, with the same parent/child structure.
A Camera node becomes a Camera2D node; the first one becomes the scene’s camera. Hidden nodes
are created hidden, so game code can show them later. Nodes keep their names (Scene_FindNode),
and nodes with a script get it attached (it must be registered: see script.odin); its ready
runs on the scene’s next update.
Inputs
- scene: The scene to fill
- data: The parsed scene
- projectDir: Folder that asset paths are relative to
- allocator: Allocator for the returned list
- attachScripts: Attach scripts. The editor’s viewport passes false: scripts don’t run there
Returns
- The created nodes, one per entry in
data.nodes(same order). Editors use it to keep the nodes in sync with their own data; the caller owns the slice
procedure. Source: project_loader.odin:198
SceneNodeKind_IsType
Section titled “SceneNodeKind_IsType”SceneNodeKind_IsType :: proc(kind: SceneNodeKind, typeName: string) -> boolWhether nodes of a kind can go in a script’s node field pointing to typeName (a node type’s
name without its package, as ScriptField.hint gives it): the kind’s type is that type, or
embeds it (DynamicBody embeds PhysicsBody, every node type embeds Node). Editors use it to
offer the nodes that fit a field.
Inputs
- kind: The node kind
- typeName: The type’s name (“Node”, “Sprite”, “PhysicsBody”…)
Returns
- Whether the field accepts the kind’s nodes
Example
assert(e.SceneNodeKind_IsType(.DynamicBody, "PhysicsBody"))procedure. Source: project_loader.odin:271
Scene_ApplySettings
Section titled “Scene_ApplySettings”Scene_ApplySettings :: proc(scene: ^Scene, settings: SceneSettings)Applies a scene file’s settings to a scene: its screen layers, and its background color and
gravity (none: the project’s). Scene_Build does it.
Inputs
- scene: The scene
- settings: The settings
procedure. Source: project_loader.odin:291
Scene_AddComponent
Section titled “Scene_AddComponent”Scene_AddComponent :: proc(scene: ^Scene, component: ComponentAsset, parent: ^Node = nil, position: [2]f32 = {}) -> ^NodeAdds an instance of a component to a scene: its whole node tree, built like a scene file’s, with
its scripts. Like Scene_AddNode, the nodes join the scene at the start of its next update
(onEnter, then the scripts’ ready). Call it as often as needed: each call makes another
instance. The component’s file is read once per game and cached. Scene_RemoveComponent
removes an instance.
Inputs
- scene: The scene
- component: The component (a
COMPONENT_...constant, or an exportede.ComponentAssetfield) - parent: The node to add it under (nil: the top level)
- position: Where its root goes, relative to the parent
Returns
- The instance’s root node (nil, logged, if the component can’t be read). It takes the
component’s name;
Node_GetComponenttells which component it is
Example
bullet := e.Scene_AddComponent(self.scene, COMPONENT_BULLET, position = e.Node_GetWorldPosition(self.node))procedure. Source: project_loader.odin:325
Scene_RemoveComponent
Section titled “Scene_RemoveComponent”Scene_RemoveComponent :: proc(scene: ^Scene, instanceId: EntityId) -> boolRemoves a component instance, its whole tree, like Scene_RemoveNode: at the start of the next
update they get onExit, then their destroy hooks, then their memory is freed. Only an
instance’s root is accepted (a node made by Scene_AddComponent, or a scene file’s Component
node), so a stray id can’t remove part of an instance; use Scene_RemoveNode for any node.
Inputs
- scene: The scene
- instanceId: The instance root’s
id
Returns
- false (logged) when no node of the scene has that id, or it isn’t an instance’s root
Example
// Given: self: ^e.Script, bullet: ^e.Node (from e.Scene_AddComponent)e.Scene_RemoveComponent(self.scene, bullet.id)procedure. Source: project_loader.odin:360
Node_GetComponent
Section titled “Node_GetComponent”Node_GetComponent :: proc(node: ^Node) -> (ComponentAsset, bool)The component a node is the root of an instance of.
Inputs
- node: The node
Returns
- The component (its path, like the
COMPONENT_...constants), and false for nodes that aren’t an instance’s root (nodes inside an instance included)
procedure. Source: project_loader.odin:384
Scene_GetComponentInstances
Section titled “Scene_GetComponentInstances”Scene_GetComponentInstances :: proc(scene: ^Scene, component: ComponentAsset) -> []^NodeEvery instance of a component in a scene: their roots, in the scene and waiting to join it (in tree order, then the order they were added). Removed instances are listed until the next update frees them.
Inputs
- scene: The scene
- component: The component
Returns
- The instances’ roots (temp allocated)
Example
for bullet in e.Scene_GetComponentInstances(scene, COMPONENT_BULLET) do e.Scene_RemoveComponent(scene, bullet.id)procedure. Source: project_loader.odin:405
Game_LoadComponent
Section titled “Game_LoadComponent”Game_LoadComponent :: proc(game: ^Game, path: string) -> (^SceneData, bool)A component’s parsed file, read the first time and kept for the game’s life, in the game’s arena (instances share it). A file that can’t be read is remembered too: it isn’t read again.
Inputs
- game: The game
- path: The component file, relative to the project folder (
Game.projectDir)
Returns
- The component’s nodes (don’t free them), and false (logged) if it can’t be read
procedure. Source: project_loader.odin:424
Constants and variables
Section titled “Constants and variables”PROJECT_FORMAT_VERSION
Section titled “PROJECT_FORMAT_VERSION”PROJECT_FORMAT_VERSION :: 13Version written by the editor. This loader also reads older versions
constant. Source: project_format.odin:210
SCENE_NODE_CATEGORY_NAMES
Section titled “SCENE_NODE_CATEGORY_NAMES”SCENE_NODE_CATEGORY_NAMES := [SceneNodeCategory]string{ .Scene = "Scene", .Graphics = "Graphics", .UI = "User interface", .Physics = "Physics", .Audio = "Sound",}Each category’s name as tools show it
variable. Source: project_format.odin:312
SCENE_NODE_KINDS
Section titled “SCENE_NODE_KINDS”SCENE_NODE_KINDS := [SceneNodeKind]SceneNodeKindInfo{ .Node2D = {"Groups nodes; moving, rotating or scaling it moves its children", .Scene}, .Sprite = {"Draws an image", .Graphics}, .AnimatedSprite = {"Plays a sprite sheet animation", .Graphics}, .Camera = {"Chooses the part of the scene the game shows; can follow a parent node", .Scene}, .Label = {"Draws a line of text", .UI}, .Menu = {"A list of buttons that switch scenes or quit the game", .UI}, .StaticBody = {"A physics body that doesn't move by itself: walls, floors, platforms", .Physics}, .DynamicBody = {"A physics body moved by gravity, collisions and forces", .Physics}, .CharacterMover = {"A body moved by code that slides along walls and floors: players, enemies", .Physics}, .Area = {"A region that reports bodies entering and leaving it: pickups, triggers", .Physics}, .CollisionShape = {"The shape of the physics body above it: rectangle, circle, capsule, polygon, segment", .Physics}, .RayCast = {"A ray that reports the first physics body in its way: line of sight, ground checks", .Physics}, .Joint = {"Connects two physics bodies: a hinge, rope, spring, slider, weld or wheel", .Physics}, .ParticleEmitter = {"Emits particles that move, spin, grow and change color: sparks, smoke, fire, rain", .Graphics}, .Component = {"An instance of a component: a node tree saved in the assets, reused in any scene", .Scene}, .AudioPlayer = {"Plays a sound: volume, pitch, pan, looping, through a mix bus", .Audio}, .AudioPlayer2D = {"Plays a sound in the world: panned and quieter with distance from the camera", .Audio}, .MusicPlayer = {"Streams music: fades, looping, seeking, through a mix bus", .Audio}, .LDtkLevel = {"A level made in the LDtk level editor: its tile layers, entities and IntGrid collision", .Scene}, .TileMap = {"Tilemap layers drawn in Aseprite: their tiles, and collision from the tiles' properties", .Graphics}, .Panel = {"A box that lays out the controls in it (rows, columns, grids) on a themed or 9-patch background", .UI}, .Button = {"A button with text or a picture: switches scenes, quits, or tells scripts it was pressed", .UI}, .ProgressBar = {"A bar showing a value in its range: health, stamina, loading", .UI}, .TextField = {"A box the player types a line of text in: names, chat, codes", .UI}, .ScrollBox = {"A column of controls that scrolls: long lists, inventories, credits", .UI}, .RichText = {"Text with colors and sizes that wraps: dialogue, descriptions, tooltips", .UI}, .Light2D = {"A light in a lit scene: a point or a spot cone, colored, with shadows from occluders", .Graphics}, .ParallaxLayer = {"A background that moves slower than the world as the camera moves (depth), tiled if asked: skies, hills", .Graphics}, .CameraZone = {"A room the camera stays inside while it's in it; moving to another slides the view over", .Scene}, .CameraView = {"What another camera sees, in a rectangle on the screen: split screen, minimaps, picture in picture", .Graphics}, .StateMachine = {"Keeps one of a set of states (idle, run, jump) and moves between them on conditions; plays each state's animation", .Scene}, .NavRegion = {"Where characters can walk: a grid over an area, blocked by static bodies, for finding paths", .Scene}, .NavAgent = {"Finds paths through a NavRegion and moves its parent along them, around other agents", .Scene}, .LightOccluder = {"A shape that blocks light and casts shadows: walls, pillars, trees", .Graphics}, .Animator = {"Animates other nodes' properties over time with keyframes: cutscenes, moving platforms, UI, effects", .Graphics}, .VirtualJoystick = {"An on-screen stick for touch screens, pushing input actions (moving left, right, up, down)", .UI}, .TouchButton = {"An on-screen round button for touch screens, pressing an input action (jump, fire)", .UI}, .ScreenEffect = {"A shader over the whole scene as drawn: CRT, bloom, color grading (GPU renderer)", .Graphics}, .Line2D = {"A thick line through points: ropes, lasers, paths, with a color gradient, round joints, a texture", .Graphics}, .Trail2D = {"A ribbon left behind its parent as it moves: sword swings, dashes, comets, tire tracks", .Graphics}, .Skeleton2D = {"The root of a character's bones: animate the bones, and the pictures on them follow", .Graphics}, .Bone2D = {"A bone of a skeleton: its children (pictures, more bones) move with it; key it in the Timeline", .Graphics}, .SkinnedMesh = {"A picture bent over bones: a mesh whose points follow weighted bones (soft bodies, sleeves, tails)", .Graphics}, .DialogueBox = {"Shows a conversation: the speaker, each line typed out, options to choose (a dialogue asset, or Yarn)", .UI}, .IKChain = {"Turns a chain of bones so its tip reaches a target: arms reaching, feet planted on the ground", .Graphics},}Every node kind a scene file can contain, with its description. Tools list node kinds from this
table, so a kind added to SceneNodeKind must be described here (the compiler enforces it).
Example
for kind in e.SceneNodeKind { info := e.SCENE_NODE_KINDS[kind] fmt.printfln("%s / %v: %s", e.SCENE_NODE_CATEGORY_NAMES[info.category], kind, info.description)}variable. Source: project_format.odin:340
DEFAULT_SCENE_NODE
Section titled “DEFAULT_SCENE_NODE”DEFAULT_SCENE_NODE :: SceneNodeData{ parent = -1, scale = {1, 1}, visible = true, fontSize = 24, color = {255, 255, 255, 255}, centered = true, zoom = 1, columns = 1, rows = 1, fps = 8, loop = true, collisionLayer = 1, collisionMask = PHYSICS_ALL_LAYERS, friction = 0.6, density = 1, gravityScale = 1, floorMaxAngle = 45, shapeSize = {32, 32}, radius = 16, target = {0, 50}, enabled = true, excludeParent = true, jointOffset = {0, 100}, dampingRatio = 0.5, particles = DEFAULT_PARTICLE_SETTINGS, audio = DEFAULT_AUDIO_SETTINGS, ldtk = DEFAULT_LDTK_LEVEL_SETTINGS, tilemap = DEFAULT_TILEMAP_SETTINGS, animator = DEFAULT_ANIMATOR_SETTINGS, ui = DEFAULT_UI_SETTINGS, light = DEFAULT_LIGHT_SETTINGS, nav = DEFAULT_NAV_SETTINGS, cameraTools = DEFAULT_CAMERA_TOOL_SETTINGS, boneLength = 32, ik = DEFAULT_IK_SETTINGS, dialogueBox = DEFAULT_DIALOGUE_BOX_SETTINGS,}Values used for attributes a node doesn’t specify
constant. Source: project_format.odin:836
DEFAULT_GRID_SETTINGS
Section titled “DEFAULT_GRID_SETTINGS”DEFAULT_GRID_SETTINGS :: GridSettings{size = {32, 32}, majorEvery = 4, opacity = 60, majorOpacity = 90}A project’s grid without a <grid> element: 32 px cells, a major line every 4
constant. Source: project_format.odin:1057
GRID_SIZE_RANGE
Section titled “GRID_SIZE_RANGE”GRID_SIZE_RANGE :: [2]int{2, 1024}The values a grid’s cell size may take (parsing clamps to it)
constant. Source: project_format.odin:1060
GRID_MAJOR_RANGE
Section titled “GRID_MAJOR_RANGE”GRID_MAJOR_RANGE :: [2]int{0, 64}The values a grid’s major line spacing may take, in cells
constant. Source: project_format.odin:1062
DEFAULT_WINDOW_SETTINGS
Section titled “DEFAULT_WINDOW_SETTINGS”DEFAULT_WINDOW_SETTINGS :: WindowSettings{vsync = true}A window as games had it before these settings: windowed, vsync, not high-DPI
constant. Source: project_format.odin:1078
SCENE_NODE_TYPES
Section titled “SCENE_NODE_TYPES”SCENE_NODE_TYPES := [SceneNodeKind]typeid{ .Node2D = Node, .Sprite = Sprite, .AnimatedSprite = AnimatedSprite, .Camera = Camera2D, .Label = Label, .Menu = Menu, .StaticBody = StaticBody, .DynamicBody = DynamicBody, .CharacterMover = CharacterMover, .Area = Area, .CollisionShape = CollisionShape, .RayCast = RayCast, .Joint = Joint, .ParticleEmitter = ParticleEmitter, .Component = Node, .AudioPlayer = AudioPlayer, .AudioPlayer2D = AudioPlayer, .MusicPlayer = AudioPlayer, .LDtkLevel = LDtkLevelNode, .TileMap = TileMapNode, .Animator = Animator, .Panel = Panel, .Button = Button, .ProgressBar = ProgressBar, .TextField = TextField, .ScrollBox = ScrollBox, .RichText = RichText, .Light2D = Light2D, .LightOccluder = LightOccluder, .NavRegion = NavRegion, .NavAgent = NavAgent, .StateMachine = StateMachine, .ParallaxLayer = ParallaxLayer, .CameraZone = CameraZone, .CameraView = CameraView, .VirtualJoystick = VirtualJoystick, .TouchButton = TouchButton, .ScreenEffect = ScreenEffect, .Line2D = Line2D, .Trail2D = Trail2D, .Skeleton2D = Skeleton2D, .Bone2D = Bone2D, .SkinnedMesh = SkinnedMesh, .IKChain = IKChain, .DialogueBox = DialogueBox,}The engine type a scene file’s node kind is built as (Node2D is a plain Node; a Component
instance is its component’s root, so it has no type of its own: Node here).
variable. Source: project_loader.odin:207
