LDtk
LDtk projects as plain data, and LDtkLevel nodes built from them.
Files: ldtk.odin, ldtk_nodes.odin.
File overviews
Section titled “File overviews”ldtk.odin
Section titled “ldtk.odin”LDtk projects (https://ldtk.io, the level editor by Deepnight): reading .ldtk files, so levels
drawn in LDtk play in the engine. The nodes that build them into a scene are in ldtk_nodes.odin.
LDtk_Load reads a project (JSON format 1.x; written against 1.5.3) into plain data:
- Worlds and levels. Without LDtk’s “Multi-worlds” option a project has one world, made from
the file’s root (its
levels,worldLayout, grid size); with it, every world ofworlds. Levels saved as separate files (“Save levels to separate files”:.ldtklnext to the project) are read too, so a level always has its layers. - Layers in LDtk’s order, top-most first (draw them back to front). Every kind keeps what a game needs: IntGrid values (row-major, 0 = empty), the tiles of Tiles layers and of auto-layers (an IntGrid layer with rules has both), entities. Offsets are the total ones (definition plus instance).
- Entities: identifier, iid, position (
px: where its pivot is, in the level), size, pivot, tags, tile, color and custom fields (LDtkValue: ints, floats, bools, strings, colors, enums as their value’s name, points, tiles, entity references, and arrays of those). - Definitions a game uses: tilesets (image path, grid, spacing, padding, custom data, enum tags), layer definitions (IntGrid value names and colors, parallax), entity definitions, enums.
Paths in the data (tileset.path, layer.tilesetPath, level.background.path) are the
.ldtk file’s folder joined with LDtk’s relative path: pass them to the resource manager as they
are. Everything a project holds lives in its own arena: LDtk_Destroy frees it all.
Entity definitions keep how LDtk draws them (render mode, tile render mode, nine-slice borders,
opacities, their tile), and each entity points at its definition (LDtkEntity.def).
Ignored: auto-layer rules (LDtk resolves them into tiles when it saves), editor-only settings.
LDtk’s internal icon atlas (embedAtlas: LdtkIcons) has no image file: entities using it draw
their icons only when the project has its own copy beside the .ldtk (LDTK_ICONS_FILE).
Example
project, problem := e.LDtk_Load("assets/world.ldtk")if project == nil do log.errorf("Can't read the level: %s", problem)defer e.LDtk_Destroy(project)level := e.LDtk_FindLevel(project, "Level_0")walls := e.LDtk_FindLayer(level, "Collisions")if e.LDtk_IntGridAt(walls, 3, 7) == 1 do fmt.println("a wall at cell 3, 7")ldtk_nodes.odin
Section titled “ldtk_nodes.odin”LDtk levels in scenes: an LDtkLevel node (scene files: <node kind="LDtkLevel" ldtk="..." level="..."/>) builds a level of an LDtk project (ldtk.odin) under itself when the scene is
built. Its top-left corner is at the node’s position; the nodes it makes aren’t in the scene
file, like a component’s.
LDtkLevel (LDtkLevelNode) the node in the scene file Level_0 (LDtkLevelView) the level: its background (color, image); getBounds Bg_textures (LDtkTileLayer) a layer's tiles, drawn in one batch; IntGrid cells Collisions (LDtkTileLayer) without tiles are drawn in their value's color Entities (Node) an entity layer: one node per entity, at its pivot Player ... Collision (StaticBody) IntGrid cells as rectangles (intGridCollision): solid and one-way Spikes (Area) an area value's cells (areaValues), named after the value-
Layers are made back to front, so LDtk’s top layer draws last (on top). Every node is on the LDtkLevel node’s render layer. Hidden layers (LDtk’s eye) are made hidden.
-
Entities, by their type’s rule (
<entity name make component script noScript/>,LDtkEntityRule; none: Auto), become: Auto, in order of preference, an instance of the component the rule maps (<entity name="Player" component="components/player.component.xml"/>), what code registered for it (LDtk_RegisterEntity), else anLDtkEntityNode(draws the entity’s tile if it has one from an image tileset; outlined in the editor); or whatmakesays:Component,Code(its rectangle, warned, if nothing registered it),Rectangle, orSkip(not made). A script is attached when what was made has none: the rule’sscript, else (Auto) the one named like the entity (Player);noScript="true"attaches none. The entity’s fields set the script’s exported fields with the same name (case and underscores ignored): ints, floats, bools, strings, enums (by value name), colors, points (the pixel middle of their cell). Code finds the entity’s data withLDtk_EntityOf(node). -
Collision: with
intGridCollisionnaming an IntGrid layer, each value is a kind (LDtk_CollisionKind): one-way (oneWayValues), an area (areaValues), solid (incollisionValues, or every other non-empty value when that’s empty), or nothing. Solid and one-way cells become one StaticBody per level (one-way shapes haveoneWay), each area value an Area named after the value, their rectangles merged (greedy: runs along rows, then down) so a wall is one shape, not a shape per cell. The body and areas get the node’scollisionLayer,collisionMask,frictionandrestitution. -
allLevels builds every level of the level’s world at its place in it (Free and GridVania: worldX, worldY; linear layouts: one after the other), for worlds that scroll from level to level.
-
Projects are read once per game and kept (
Game_LoadLDtk): every LDtkLevel node of a file shares one, and the data the nodes point at lives as long as the game (Game_ForgetLDtkdrops it, for tools whose file changed). -
Tilesets draw with nearest filtering (pixel art) unless
smoothTiles. -
Parallax (layers with a parallax factor), as LDtk does it: with the camera’s middle at c in the level, the layer’s points p show at c * factor + scale * p (scale = 1 - factor.x with parallax scaling; without it, minus half the layer’s size times the factor). Tile layers do it as they draw; entity layers move their group node in its update (a frame behind a camera that moves later). Not in tools (built without scripts): the level shows as it was made.
-
Entities draw as LDtk draws them: a tile by its definition’s tile render mode (Cover, FitInside, Repeat, Stretch, FullSizeCropped, FullSizeUncropped, NineSlice) at its tile opacity; in tools, without a tile, the definition’s shape (Rectangle, Ellipse, Cross; hollow, fill and line opacities). LDtk’s internal icons only with a copy of them (LDTK_ICONS_FILE).
-
Entity references:
LDtk_EntityRefNode/LDtk_EntityRefNodes(an EntityRef field’s node),LDtk_FindEntityByIid. -
Culling: tile layers draw only the tiles touching the screen (its corners taken back into the layer through the draw transform and the parallax).
-
Development runs reload saved LDtk files (LDtk_WatchFiles).
Example
// Given: self: ^e.Script// A coin in LDtk, with a "value" Int field, gets the Coin script with value setCoin :: struct { using script: e.Script, value: int `export:"default=1"`,}// Elsewhere: the level's datalevel := cast(^e.LDtkLevelNode)e.Scene_FindNode(self.scene, "Level")for mob in e.LDtk_FindEntityNodes(level, "Mob") do log.infof("a mob at %v", mob.position)solid := e.LDtk_IntGridAtPosition(level, "Collisions", e.Node_GetWorldPosition(self.node)) != 0door := e.LDtk_EntityRefNode(self.node, "door") // this entity's "door" field's entityLDtkLayerType
Section titled “LDtkLayerType”LDtkLayerType :: enum { IntGrid, Entities, Tiles, // An IntGrid-less auto-layer: tiles made by rules from another layer's IntGrid AutoLayer,}A layer’s kind
enum. Source: ldtk.odin:49
LDtkWorldLayout
Section titled “LDtkWorldLayout”LDtkWorldLayout :: enum { // Anywhere (worldX, worldY) Free, // On a grid (worldX, worldY are multiples of the world grid) GridVania, // In a row, in the order of the levels array LinearHorizontal, // In a column, in the order of the levels array LinearVertical,}How a world’s levels are placed
enum. Source: ldtk.odin:58
LDtkTilesetRect
Section titled “LDtkTilesetRect”LDtkTilesetRect :: struct { // The tileset's uid (LDtk_FindTileset) tileset: int, // The rectangle in the tileset's image, in pixels: top-left corner, width, height x, y, w, h: int,}A rectangle of a tileset’s image
struct. Source: ldtk.odin:70
LDtkGridPoint
Section titled “LDtkGridPoint”LDtkGridPoint :: distinct [2]intA cell of a grid: a Point field’s value
distinct type. Source: ldtk.odin:78
LDtkEntityRef
Section titled “LDtkEntityRef”LDtkEntityRef :: struct { // The iids of the entity, and of its layer, level and world (`LDtk_FindEntityByIid`) entityIid, layerIid, levelIid, worldIid: string,}Where an entity is: an EntityRef field’s value
struct. Source: ldtk.odin:81
LDtkValue
Section titled “LDtkValue”LDtkValue :: union { i64, f64, bool, string, sdl.Color, LDtkGridPoint, LDtkTilesetRect, LDtkEntityRef, []LDtkValue,}A custom field’s value. nil when the field is null (no value). Enums are their value’s name.
Arrays (Array<Int>…) are []LDtkValue.
union. Source: ldtk.odin:90
LDtkField
Section titled “LDtkField”LDtkField :: struct { // The field's name in LDtk identifier: string, // LDtk's type name: "Int", "Float", "Bool", "String", "Multilines", "Color", "Point", // "FilePath", "Tile", "EntityRef", "LocalEnum.Item", "ExternEnum.X", "Array<Int>"... type: string, // Its value (nil when null; `LDtk_FieldValue` reads it as a type) value: LDtkValue,}A custom field of a level or an entity
struct. Source: ldtk.odin:103
LDtkTile
Section titled “LDtkTile”LDtkTile :: struct { // Where it goes in the layer (its top-left corner, pixels; add the layer's offset) px: [2]f32, // Its top-left corner in the tileset image src: [2]f32, // Mirrored horizontally / vertically flipX, flipY: bool, // Opacity 0-1 alpha: f32, // The tile's id in its tileset id: int,}A tile of a Tiles layer or an auto-layer
struct. Source: ldtk.odin:114
LDtkEntity
Section titled “LDtkEntity”LDtkEntity :: struct { // Its definition's name ("Player") identifier: string, // Unique id (other entities' EntityRef fields point at it) iid: string, // Where its pivot is, in pixels from the level's top-left corner (without the layer offset) px: [2]f32, // Its grid cell grid: [2]int, // The pivot in its rectangle: 0-1 ({0.5, 1}: bottom middle) pivot: [2]f32, // Width and height in pixels size: [2]f32, // Its definition's tags tags: []string, // The tile LDtk shows it with (its definition's, or a field's); none for no tile tile: Maybe(LDtkTilesetRect), // The color LDtk shows it in (its smart color: a Color field's, else its definition's) color: sdl.Color, // Its custom fields' values fields: []LDtkField, // Its definition (render mode, tile mode...); nil if the project has none by its name def: ^LDtkEntityDef,}An entity instance
struct. Source: ldtk.odin:128
LDtkLayer
Section titled “LDtkLayer”LDtkLayer :: struct { // The layer's name ("Collisions") identifier: string, // Unique id of this layer instance iid: string, // What it holds type: LDtkLayerType, // Cell size in pixels gridSize: int, // Width and height in cells cells: [2]int, // Total pixel offset (definition plus instance) offset: [2]f32, // Opacity 0-1 opacity: f32, // Shown (LDtk's eye) visible: bool, // The tileset its tiles come from: its uid (-1 for none) and its image's path tileset: int, // The tileset image's path, joined to the project's folder ("" for none) tilesetPath: string, // IntGrid layers: cells.x * cells.y values, row by row; 0 is empty intGrid: []i32, // Tiles layers' tiles, and auto-layers' (IntGrid layers with rules too), back to front tiles: []LDtkTile, // Entities layers: its entities entities: []LDtkEntity, // From the layer definition: how much slower it scrolls (-1 to 1, 0 = none), and whether // parallax also scales it parallax: [2]f32, // Parallax also scales the layer parallaxScaling: bool,}A layer of a level
struct. Source: ldtk.odin:154
LDtkBackground
Section titled “LDtkBackground”LDtkBackground :: struct { // "" for none path: string, // Where the image's top-left corner goes in the level, in pixels topLeft: [2]f32, // Its scale ({1, 1}: its own size) scale: [2]f32, // The part of the image shown: x, y, width, height crop: [4]f32,}A level’s background image, placed as LDtk shows it
struct. Source: ldtk.odin:189
LDtkNeighbour
Section titled “LDtkNeighbour”LDtkNeighbour :: struct { // The neighbour's iid (`LDtk_FindLevel`) levelIid: string, // The side: "n", "s", "e", "w", "ne"..., or "<", ">", "o" dir: string,}A level touching this one in the world: its iid, and the side (n, s, e, w, ne…, or <, >, o for levels below, above, overlapping)
struct. Source: ldtk.odin:202
LDtkLevel
Section titled “LDtkLevel”LDtkLevel :: struct { // The level's name ("Level_0") identifier: string, // Unique id (what neighbours and entity references use) iid: string, // LDtk's numeric id uid: int, // Its top-left corner in the world (Free and GridVania layouts; -1 for linear ones) world: [2]f32, // Its depth in the world (levels stacked over each other) worldDepth: int, // Width and height in pixels size: [2]f32, // Its background color (the project's when it doesn't set one) bgColor: sdl.Color, // Its background image background: LDtkBackground, // The levels touching it neighbours: []LDtkNeighbour, // Its custom fields' values fields: []LDtkField, // Top-most first, as LDtk lists them layers: []LDtkLayer,}A level
struct. Source: ldtk.odin:210
LDtkWorld
Section titled “LDtkWorld”LDtkWorld :: struct { // The world's name ("World" for projects without Multi-worlds) identifier: string, // Unique id iid: string, // How its levels are placed layout: LDtkWorldLayout, // GridVania's cell size gridSize: [2]int, // Its levels, in LDtk's order levels: []LDtkLevel,}A world: its levels and how they’re laid out
struct. Source: ldtk.odin:236
LDtkTileset
Section titled “LDtkTileset”LDtkTileset :: struct { // Its id (what layers and tile rects point at: `LDtk_FindTileset`) uid: int, // Its name identifier: string, // The image, joined to the project's folder ("" for none, or LDtk's internal icons) path: string, // LDtk's own icon atlas (no image file the engine can load) embedded: bool, // Tile size in pixels gridSize: int, // Pixels between tiles spacing: int, // Pixels around the tiles, at the image's edges padding: int, // Image size in pixels, and in cells size: [2]int, // Image size in cells cells: [2]int, // Its tags tags: []string, // Text attached to tiles in LDtk ("custom data"), by tile id customData: []LDtkTileData, // Tiles tagged with an enum's values enumTags: []LDtkEnumTag,}A tileset definition
struct. Source: ldtk.odin:257
LDtkTileData
Section titled “LDtkTileData”LDtkTileData :: struct { // The tile's id in the tileset tileId: int, // The text LDtk holds for it data: string,}A tile’s custom data (a tileset’s)
struct. Source: ldtk.odin:285
LDtkEnumTag
Section titled “LDtkEnumTag”LDtkEnumTag :: struct { // The enum value's name enumValue: string, // The ids of the tiles tagged with it tileIds: []int,}The tiles tagged with an enum value (a tileset’s)
struct. Source: ldtk.odin:293
LDtkIntGridValue
Section titled “LDtkIntGridValue”LDtkIntGridValue :: struct { // The value cells hold (1 and up) value: int, // "" when unnamed identifier: string, // The color LDtk shows its cells in color: sdl.Color,}An IntGrid value’s name and color (a layer definition’s)
struct. Source: ldtk.odin:301
LDtkLayerDef
Section titled “LDtkLayerDef”LDtkLayerDef :: struct { // Its id uid: int, // The layer's name (layers of levels have the same: `LDtk_FindLayerDef`) identifier: string, // What its layers hold type: LDtkLayerType, // Cell size in pixels gridSize: int, // IntGrid layers: their values' names and colors intGridValues: []LDtkIntGridValue, // How much slower its layers scroll (-1 to 1, 0 = none) parallax: [2]f32, // Parallax also scales its layers parallaxScaling: bool,}A layer definition
struct. Source: ldtk.odin:311
LDtkEntityRenderMode
Section titled “LDtkEntityRenderMode”LDtkEntityRenderMode :: enum { Rectangle, Ellipse, Tile, Cross,}How LDtk draws an entity: its definition’s shape, or its tile
enum. Source: ldtk.odin:329
LDtkTileRenderMode
Section titled “LDtkTileRenderMode”LDtkTileRenderMode :: enum { // Scaled to cover it, keeping its shape, cropped around the pivot Cover, // Scaled to fit inside, keeping its shape, at the pivot FitInside, // Repeated at its size from the top-left corner Repeat, // Scaled to the rectangle Stretch, // At its size, cropped to the rectangle around the pivot FullSizeCropped, // At its size, at the pivot, not cropped FullSizeUncropped, // Corners at their size, edges and middle repeated (nineSlice borders) NineSlice,}How an entity’s tile fills its rectangle (LDtk’s tileRenderMode)
enum. Source: ldtk.odin:337
LDtkEntityDef
Section titled “LDtkEntityDef”LDtkEntityDef :: struct { // Its id uid: int, // The entity's name ("Player") identifier: string, // Default width and height in pixels size: [2]f32, // The pivot in its rectangle: 0-1 pivot: [2]f32, // The color LDtk draws it in color: sdl.Color, // Its tags tags: []string, // How LDtk draws it renderMode: LDtkEntityRenderMode, // How its tile fills its rectangle tileRenderMode: LDtkTileRenderMode, // Top, right, bottom, left (NineSlice) nineSlice: [4]f32, // Shapes: no fill; fill and line opacities. Tiles: opacity hollow: bool, // Opacities 0-1: the shape's fill (default 0.08) and line (default 1), and the tile (default 1) fillOpacity, lineOpacity, tileOpacity: f32, // The definition's tile (entities may show another, from a field) tile: Maybe(LDtkTilesetRect),}An entity definition
struct. Source: ldtk.odin:355
LDtkEnumDef
Section titled “LDtkEnumDef”LDtkEnumDef :: struct { // Its id uid: int, // The enum's name identifier: string, // Its values' names, in order values: []string,}An enum definition: its values, in order
struct. Source: ldtk.odin:383
LDtkProject
Section titled “LDtkProject”LDtkProject :: struct { // Holds everything below (`LDtk_Destroy` frees it) arena: MemoryArena, // The .ldtk file, and its folder (paths in the data are joined to it) path, dir: string, // The LDtk version that saved it ("1.5.3") jsonVersion: string, // The levels' default background color bgColor: sdl.Color, // At least one (projects without Multi-worlds have one, from the file's root) worlds: []LDtkWorld, // Tileset definitions tilesets: []LDtkTileset, // Layer definitions layers: []LDtkLayerDef, // Entity definitions entities: []LDtkEntityDef, // Enum definitions, external ones included enums: []LDtkEnumDef,}An LDtk project. Made by LDtk_Load; everything in it is in its arena (LDtk_Destroy).
struct. Source: ldtk.odin:395
LDtkLevelSettings
Section titled “LDtkLevelSettings”LDtkLevelSettings :: struct { // The .ldtk file, relative to the project folder file: string, // The level's identifier (or iid); "" for the first level level: string, // Every level of the level's world, each at its place in the world allLevels: bool, // The IntGrid layer whose cells become collision ("" for none), and which values (none: every // non-empty cell) collisionLayer: string, // `collisionValues="1 3"`: the IntGrid values that are solid (empty: every non-empty cell // that isn't one-way or an area; "0" alone: none, 0 being LDtk's empty cell) collisionValues: [dynamic]int, // `oneWayValues="2"`: values that are one-way platforms (bodies pass up through them) oneWayValues: [dynamic]int, // `areaValues="4"`: values that become Areas (triggers: onCollisionEnter / Exit), one per value, // named after the value in LDtk ("Spikes") areaValues: [dynamic]int, // Make nodes for entities spawnEntities: bool, // Draw the level's background color and image background: bool, // Linear filtering for tilesets (off: nearest, for pixel art) smoothTiles: bool, // What entities become and run, by entity identifier (`<entity>` elements; types without one // follow the Auto rules) entityComponents: [dynamic]LDtkEntityComponent,}What a scene file’s LDtkLevel node says (SceneNodeData.ldtk)
struct. Source: ldtk_nodes.odin:85
LDtkEntityComponent
Section titled “LDtkEntityComponent”LDtkEntityComponent :: struct { // The entity's identifier in LDtk ("Player") entity: string, // The component file, relative to the project folder (`make` Component; Auto with one too) component: string, // What its entities become (Auto: the component when set, else code, else the rectangle) make: LDtkEntityMake, // The script they run: "" for the one named like the entity (when what's made has none) script: string, // No script, not even the one named like the entity noScript: bool,}What an entity type becomes and which script its entities run (<entity name="Coin" component=".." make=".." script=".." noScript="true"/>, attributes only when not the default).
The first files had only a component: that still means the component.
struct. Source: ldtk_nodes.odin:119
LDtkEntityRule
Section titled “LDtkEntityRule”LDtkEntityRule :: LDtkEntityComponentAnother name for LDtkEntityComponent: what an entity type becomes and runs
type. Source: ldtk_nodes.odin:133
LDtkEntityMake
Section titled “LDtkEntityMake”LDtkEntityMake :: enum { // The mapped component, else what code registered (`LDtk_RegisterEntity`), else the rectangle Auto, // The rule's component Component, // What code registered (`LDtk_RegisterEntity`); the rectangle when nothing did Code, // Its rectangle (and tile), whatever is mapped or registered Rectangle, // Nothing: entities of the type aren't made Skip,}What an entity type’s nodes are
enum. Source: ldtk_nodes.odin:136
LDtkCollisionKind
Section titled “LDtkCollisionKind”LDtkCollisionKind :: enum { None, // Solid collision (a StaticBody's rectangles) Solid, // One-way platforms: solid from above only (`CollisionShape.oneWay`) One_Way, // An Area: reports bodies entering and leaving (triggers) Area,}What an IntGrid value of the collision layer becomes
enum. Source: ldtk_nodes.odin:150
LDtkLevelNode
Section titled “LDtkLevelNode”LDtkLevelNode :: struct { // The node part: transform, hierarchy, hooks using node: Node, // The project (the game's: Game_LoadLDtk), nil when it couldn't be read project: ^LDtkProject, // One per level built views: [dynamic]^LDtkLevelView, // Every entity's node, with its data spawned: [dynamic]LDtkSpawned, // Layers with a parallax factor move with the camera (not in tools that build without // scripts, like the editor: the level shows as it was made) parallax: bool, // What it was built with (in its arena): for reloading when the file changes settings: LDtkLevelSettings, // The physics settings of the collision bodies it makes collision: LDtkCollisionBody, // An entity's node was made (when the level is built, and for entities a reload added) onEntitySpawned: EventSubject `event:"An entity's node was made (built, or added by a hot reload; data: the node)" data:"node := cast(^e.Node)data.(rawptr)"`, // Its LDtk file was saved and the level updated (hot reload, dev runs) onReloaded: EventSubject `event:"Its LDtk file changed and the level was updated (dev runs: Play from the editor)"`,}The LDtkLevel node: what it built, for code that looks at the level.
struct. Source: ldtk_nodes.odin:193
LDtkCollisionBody
Section titled “LDtkCollisionBody”LDtkCollisionBody :: struct { // The layers it's on and the layers it collides with (bits: layer n is bit n-1) layer, mask: u64, // Its surface: friction and bounciness friction, restitution: f32,}The StaticBody an IntGrid layer becomes: the scene file node’s physics attributes
struct. Source: ldtk_nodes.odin:216
LDtkLevelView
Section titled “LDtkLevelView”LDtkLevelView :: struct { // The node part: transform, hierarchy, hooks using node: Node, // The level's data (the project's) level: ^LDtkLevel, // Its background image (nil for none) background: ^sdl.Texture, // Draw the background color and image (the scene file's `background`) drawBackground: bool,}A level, as built: draws its background
struct. Source: ldtk_nodes.odin:224
LDtkTileLayer
Section titled “LDtkTileLayer”LDtkTileLayer :: struct { // The node part: transform, hierarchy, hooks using node: Node, // The layer's data (the project's) data: ^LDtkLayer, // The tileset's image (nil for an IntGrid layer drawn in colors) texture: ^sdl.Texture, // The tileset's tile size tileSize: f32, // IntGrid cells' colors, by value (layers without tiles) colors: map[int]sdl.Color, // Move with the camera by the layer's parallax factor parallax: bool, // SDL's software renderer: the whole layer drawn once into a texture of its size, then copied // each frame (`ldtkTileLayerDraw`); nil until then, and again after a reload cache: ^sdl.Texture, // The renderer it was made with cacheRenderer: ^sdl.Renderer,}A layer’s tiles (or an IntGrid layer’s colored cells, when it has no tiles), drawn in one batch
struct. Source: ldtk_nodes.odin:236
LDtkEntityLayer
Section titled “LDtkEntityLayer”LDtkEntityLayer :: struct { // The node part: transform, hierarchy, hooks using node: Node, // The layer's data (the project's) data: ^LDtkLayer,}An entity layer’s group: moves with the camera when the layer has a parallax factor
struct. Source: ldtk_nodes.odin:257
LDtkEntityNode
Section titled “LDtkEntityNode”LDtkEntityNode :: struct { // The node part: transform, hierarchy, hooks using node: Node, // The entity's data (the project's) entity: ^LDtkEntity, // Its tile's tileset image (nil: no tile, or one from LDtk's internal icons without a copy) texture: ^sdl.Texture, // Its tile's rectangle in the image src: sdl.FRect, // Draw LDtk's shape when it has no tile (tools that build without scripts: the editor) outline: bool,}An entity that no component or spawner took: its rectangle, and its tile if it has one
struct. Source: ldtk_nodes.odin:265
LDtkSpawned
Section titled “LDtkSpawned”LDtkSpawned :: struct { // The node made for it (it leaves the list when the node is removed) node: ^Node, // The entity's data (the project's) entity: ^LDtkEntity, // The level it's in level: ^LDtkLevel,}An entity’s node and data
struct. Source: ldtk_nodes.odin:279
LDtkEntitySpawner
Section titled “LDtkEntitySpawner”LDtkEntitySpawner :: #type proc(scene: ^Scene, entity: ^LDtkEntity) -> ^NodeMakes a node for an entity: called with the entity’s data, it returns the node (the engine positions and names it, and adds it to the level). Registered with LDtk_RegisterEntity.
type. Source: ldtk_nodes.odin:292
LDtkPlacedLevel
Section titled “LDtkPlacedLevel”LDtkPlacedLevel :: struct { level: ^LDtkLevel, position: [2]f32,}A level a level node builds, and where in the node it goes
struct. Source: ldtk_nodes.odin:648
Procedures
Section titled “Procedures”LDtk_Load
Section titled “LDtk_Load”LDtk_Load :: proc(path: string) -> (^LDtkProject, string)Reads an LDtk project file (and its separate level files, if it has them).
Inputs
- path: The .ldtk file (read through the platform layer: the data pack, the browser’s files)
Returns
- The project (LDtk_Destroy frees it), or nil and what went wrong (temp allocated)
Example
project, problem := e.LDtk_Load("assets/world.ldtk")if project == nil do log.errorf("LDtk: %s", problem)defer e.LDtk_Destroy(project)procedure. Source: ldtk.odin:430
LDtk_Parse
Section titled “LDtk_Parse”LDtk_Parse :: proc(text, path: string) -> (^LDtkProject, string)Parses an LDtk project’s JSON. Separate level files are read relative to path.
Inputs
- text: The .ldtk file’s contents
- path: Where it is (the folder paths in it are relative to)
Returns
- The project (LDtk_Destroy frees it), or nil and what went wrong (temp allocated)
procedure. Source: ldtk.odin:446
LDtk_Destroy
Section titled “LDtk_Destroy”LDtk_Destroy :: proc(project: ^LDtkProject)Frees a project and everything in it. Data taken from it (levels, entities, strings) is gone too.
Inputs
- project: The project (nil does nothing)
procedure. Source: ldtk.odin:527
LDtk_FindLevel
Section titled “LDtk_FindLevel”LDtk_FindLevel :: proc(project: ^LDtkProject, name: string) -> ^LDtkLevelA level by identifier (“Level_0”) or iid, in any world.
Inputs
- project: The project (nil gives nil)
- name: The level’s identifier or iid
Returns
- The level, or nil
procedure. Source: ldtk.odin:543
LDtk_FindLayer
Section titled “LDtk_FindLayer”LDtk_FindLayer :: proc(level: ^LDtkLevel, identifier: string) -> ^LDtkLayerA level’s layer by identifier.
Inputs
- level: The level (nil gives nil)
- identifier: The layer’s name (“Collisions”)
Returns
- The layer, or nil
procedure. Source: ldtk.odin:561
LDtk_FindTileset
Section titled “LDtk_FindTileset”LDtk_FindTileset :: proc(project: ^LDtkProject, uid: int) -> ^LDtkTilesetA tileset by uid (what LDtkLayer.tileset and LDtkTilesetRect.tileset hold).
Inputs
- project: The project (nil gives nil)
- uid: The tileset’s uid
Returns
- The tileset, or nil
procedure. Source: ldtk.odin:577
LDtk_FindLayerDef
Section titled “LDtk_FindLayerDef”LDtk_FindLayerDef :: proc(project: ^LDtkProject, identifier: string) -> ^LDtkLayerDefA layer definition by identifier.
Inputs
- project: The project (nil gives nil)
- identifier: The layer’s name
Returns
- The definition, or nil
procedure. Source: ldtk.odin:593
LDtk_IntGridAt
Section titled “LDtk_IntGridAt”LDtk_IntGridAt :: proc(layer: ^LDtkLayer, cx, cy: int) -> intAn IntGrid layer’s value at a cell: 0 when empty, outside the layer, or not an IntGrid layer.
Inputs
- layer: The layer (nil gives 0)
- cx: The cell’s column, from the left
- cy: The cell’s row, from the top
Returns
- The cell’s value
Example
// Given: level: ^e.LDtkLevel, x: f32, y: f32walls := e.LDtk_FindLayer(level, "Collisions")cell := [2]int{int(x) / walls.gridSize, int(y) / walls.gridSize}solid := e.LDtk_IntGridAt(walls, cell.x, cell.y) != 0procedure. Source: ldtk.odin:616
LDtk_FindField
Section titled “LDtk_FindField”LDtk_FindField :: proc(fields: []LDtkField, identifier: string) -> ^LDtkFieldA custom field by identifier (of a level or an entity).
Inputs
- fields: The level’s or entity’s fields
- identifier: The field’s name
Returns
- The field, or nil
procedure. Source: ldtk.odin:633
LDtk_FieldValue
Section titled “LDtk_FieldValue”LDtk_FieldValue :: proc(fields: []LDtkField, identifier: string, default: $T) -> TA custom field’s value as a type, or the default when it’s missing, null or another type. Ints
read as floats too, and as int (which reads Int fields).
Inputs
- fields: The level’s or entity’s fields
- identifier: The field’s name
- default: What to return when the field has no value of type T (it also sets T)
Returns
- The value, or
default
Example
// Given: entity: ^e.LDtkEntityhp := e.LDtk_FieldValue(entity.fields, "hp", i64(3))name := e.LDtk_FieldValue(entity.fields, "name", "nobody")procedure. Source: ldtk.odin:655
LDtk_ValueText
Section titled “LDtk_ValueText”LDtk_ValueText :: proc(value: LDtkValue, gridSize: int) -> (string, bool)A field’s value in the text form scene files and script fields use: “12”, “0.5”, “true”, a
string, “#rrggbbaa”, a point as the pixel position of its cell’s middle (“x,y”, from gridSize).
Arrays, tiles and entity references have none.
Inputs
- value: The field’s value
- gridSize: The layer’s cell size in pixels (for points)
Returns
- The text (temp allocated), and whether the value has a text form
procedure. Source: ldtk.odin:683
LDtk_Dependencies
Section titled “LDtk_Dependencies”LDtk_Dependencies :: proc(text: string) -> []stringThe files a project needs besides the .ldtk itself: its separate level files, tileset images and level backgrounds, relative to the .ldtk’s folder (temp allocated). Exports copy them with it.
Inputs
- text: The .ldtk file’s contents
Returns
- The relative paths, each once (empty when the text isn’t valid JSON)
procedure. Source: ldtk.odin:708
LDtkLevelSettings_Destroy
Section titled “LDtkLevelSettings_Destroy”LDtkLevelSettings_Destroy :: proc(settings: ^LDtkLevelSettings, allocator := context.allocator)Frees what LDtkLevelSettings own and zeroes them. SceneData_Destroy calls it for every node.
Inputs
- settings: The settings
- allocator: The allocator their strings and lists were made with
procedure. Source: ldtk_nodes.odin:173
LDtk_RegisterEntity
Section titled “LDtk_RegisterEntity”LDtk_RegisterEntity :: proc "contextless" (identifier: string, spawn: LDtkEntitySpawner)Registers what an entity becomes in code, for entities no component is mapped to. Callable from
@(init) (contextless, like Script_Register). Registering an identifier again replaces the first.
Inputs
- identifier: The entity’s identifier in LDtk (“Torch”)
- spawn: Makes its node
Example
package scripts
import e "engine:."
@(init)registerSpawners :: proc "contextless" () { e.LDtk_RegisterEntity("Torch", proc(scene: ^e.Scene, entity: ^e.LDtkEntity) -> ^e.Node { return e.CreateParticleEmitter(scene, e.DEFAULT_PARTICLE_SETTINGS) })}procedure. Source: ldtk_nodes.odin:314
Game_LoadLDtk
Section titled “Game_LoadLDtk”Game_LoadLDtk :: proc(game: ^Game, path: string, projectDir := "") -> ^LDtkProjectA project, read the first time it’s asked for and kept for the game’s life (nil, logged, when it can’t be read; asked again, it stays nil until Game_ForgetLDtk).
Inputs
- game: The game that keeps it
- path: The .ldtk file, relative to the project folder
- projectDir: The project folder (“” for the game’s,
Game.projectDir; tools that build scenes pass theirs to Scene_Build)
Returns
- The project (the game’s: don’t free it), or nil
procedure. Source: ldtk_nodes.odin:379
Game_ForgetLDtk
Section titled “Game_ForgetLDtk”Game_ForgetLDtk :: proc(game: ^Game, path: string = "")Drops a project the game read, so it’s read again: after the file changed. Nodes still pointing into it must be gone first (rebuild their scenes).
Inputs
- game: The game
- path: The .ldtk file as given to
Game_LoadLDtk(“” for all, the projects replaced by reloads too)
procedure. Source: ldtk_nodes.odin:402
LDtk_FindEntityNodes
Section titled “LDtk_FindEntityNodes”LDtk_FindEntityNodes :: proc(root: ^LDtkLevelNode, identifier: string) -> []^NodeThe nodes made for an entity identifier, in level and layer order (temp allocated).
Inputs
- root: The LDtkLevel node (nil gives none)
- identifier: The entity’s identifier (“Mob”)
Returns
- The nodes
procedure. Source: ldtk_nodes.odin:433
LDtk_EntityOf
Section titled “LDtk_EntityOf”LDtk_EntityOf :: proc(node: ^Node) -> ^LDtkEntityThe LDtk data of an entity’s node (or of a node under it), or nil when it isn’t one.
Inputs
- node: The entity’s node, or a node under it
Returns
- The entity’s data (the project’s), or nil
Example
// Given: self: ^e.Scriptentity := e.LDtk_EntityOf(self.node)speed := e.LDtk_FieldValue(entity.fields, "speed", f64(100))procedure. Source: ldtk_nodes.odin:454
LDtk_IntGridAtPosition
Section titled “LDtk_IntGridAtPosition”LDtk_IntGridAtPosition :: proc(root: ^LDtkLevelNode, layer: string, position: [2]f32) -> intAn IntGrid layer’s value under a world position: 0 when empty, outside the levels, or when no level has that layer.
Inputs
- root: The LDtkLevel node (nil gives 0)
- layer: The IntGrid layer’s identifier
- position: The world position, in game coordinates
Returns
- The cell’s value
Example
// Given: self: ^e.Script, level: ^e.LDtkLevelNodeonLadder := e.LDtk_IntGridAtPosition(level, "Collisions", e.Node_GetWorldPosition(self.node)) == 3procedure. Source: ldtk_nodes.odin:481
LDtk_PlacedLevels
Section titled “LDtk_PlacedLevels”LDtk_PlacedLevels :: proc(project: ^LDtkProject, settings: LDtkLevelSettings, allocator := context.allocator) -> ([]LDtkPlacedLevel, bool)The levels a level node builds, where each goes in the node: the chosen one (settings.level, by
identifier or iid; “” for the first) at the node’s origin, or with allLevels every level of its
world, at its world position (laid end to end in linear worlds).
Inputs
- project: The parsed project
- settings: The level node’s settings
- allocator: For the list
Returns
- The levels, and false when the chosen level isn’t in the project
procedure. Source: ldtk_nodes.odin:666
LDtk_EntityComponent
Section titled “LDtk_EntityComponent”LDtk_EntityComponent :: proc(settings: ^LDtkLevelSettings, identifier: string) -> stringHow a level node builds an entity (spawnEntity), in order: the component mapped to its type
(LDtkLevelSettings.entityComponents), else code registered for it (LDtk_RegisterEntity), else
its rectangle (and tile); then, if what was built has no script, the script named like the type
(Script_Find(identifier)). Its LDtk fields set the script fields whose names match
(LDtk_FieldMatches). Editors describe it with these procedures.
Inputs
- settings: The level node’s settings
- identifier: The entity type (“Coin”)
Returns
- The component its type is mapped to, or “” for none
procedure. Source: ldtk_nodes.odin:888
LDtk_EntityRule
Section titled “LDtk_EntityRule”LDtk_EntityRule :: proc(settings: ^LDtkLevelSettings, identifier: string) -> LDtkEntityRuleAn entity type’s rule: what its entities become and run (zero, Auto everything, when the node has none for it).
Inputs
- settings: The level node’s settings
- identifier: The entity type
Returns
- The rule (its strings are the settings’)
procedure. Source: ldtk_nodes.odin:905
LDtk_EntityMade
Section titled “LDtk_EntityMade”LDtk_EntityMade :: proc(settings: ^LDtkLevelSettings, identifier: string) -> LDtkEntityMakeWhat an entity type’s nodes are, its rule resolved: Auto becomes the mapped component, else Code
when code registered it (LDtk_HasSpawner: never in an editor), else Rectangle; a Component rule
without a component is a Rectangle.
Inputs
- settings: The level node’s settings
- identifier: The entity type
Returns
- Component, Code, Rectangle or Skip
procedure. Source: ldtk_nodes.odin:922
LDtk_EntityScript
Section titled “LDtk_EntityScript”LDtk_EntityScript :: proc(settings: ^LDtkLevelSettings, identifier: string, builtHasScript: bool) -> (name: string, chosen: bool)The script an entity type’s nodes run: the rule’s, else (Auto) the one named like the type, but only when what’s made has no script of its own (a component’s root may).
Inputs
- settings: The level node’s settings
- identifier: The entity type
- builtHasScript: What’s made has a script already
Returns
- The script’s name (“” for none) and whether the rule names it (false: named like the entity)
procedure. Source: ldtk_nodes.odin:949
LDtk_CollisionKind
Section titled “LDtk_CollisionKind”LDtk_CollisionKind :: proc(settings: ^LDtkLevelSettings, value: int) -> LDtkCollisionKindWhat an IntGrid value of the collision layer becomes: one-way or an area when listed so, solid when listed solid, or (no solid values listed) every other non-empty value.
Inputs
- settings: The level node’s settings
- value: The cell’s value
Returns
- Its kind (None for 0)
procedure. Source: ldtk_nodes.odin:967
LDtk_HasSpawner
Section titled “LDtk_HasSpawner”LDtk_HasSpawner :: proc(identifier: string) -> boolWhether code registered a spawner for an entity type (LDtk_RegisterEntity): the game’s own
build has them; an editor (without the game’s scripts) never does.
Inputs
- identifier: The entity type
Returns
- true when a spawner makes its nodes
procedure. Source: ldtk_nodes.odin:985
LDtk_IsCollisionValue
Section titled “LDtk_IsCollisionValue”LDtk_IsCollisionValue :: proc(values: []int, value: int) -> boolWhether an IntGrid value is solid for a level node’s collision: any value but 0 when the node lists none, else one it lists.
Inputs
- values: The node’s collision values (
LDtkLevelSettings.collisionValues) - value: The cell’s value
Returns
- true when the cell becomes collision
procedure. Source: ldtk_nodes.odin:1000
LDtk_FieldMatches
Section titled “LDtk_FieldMatches”LDtk_FieldMatches :: proc(scriptField, ldtkField: string) -> boolWhether a script field takes an LDtk field’s value: their names match, ignoring case and underscores (“maxSpeed” matches LDtk’s “max_speed”, “MaxSpeed” and “Max_speed”).
Inputs
- scriptField: The script field’s name
- ldtkField: The LDtk field’s identifier
Returns
- true when they match
procedure. Source: ldtk_nodes.odin:1018
LDtk_EntityRefNode
Section titled “LDtk_EntityRefNode”LDtk_EntityRefNode :: proc(node: ^Node, field: string) -> ^NodeThe node of the entity an EntityRef field points at (field on the entity whose node, or a node
under it, is node), when that entity was built under the same LDtkLevel node; nil otherwise (no
such field, null, or an entity in a level that wasn’t built).
Inputs
- node: The entity’s node, or a node under it
- field: The EntityRef field’s identifier
Returns
- The referenced entity’s node, or nil
Example
// Given: self: ^e.Script// A lever whose "door" field points at a doordoor := e.LDtk_EntityRefNode(self.node, "door")if door != nil do door.visible = falseprocedure. Source: ldtk_nodes.odin:1509
LDtk_EntityRefNodes
Section titled “LDtk_EntityRefNodes”LDtk_EntityRefNodes :: proc(node: ^Node, field: string) -> []^NodeThe nodes an Array<EntityRef> field points at (temp allocated; entities that weren’t built
are left out).
Inputs
- node: The entity’s node, or a node under it
- field: The field’s identifier
Returns
- The nodes (none when the node isn’t an entity’s or the field isn’t an EntityRef array)
procedure. Source: ldtk_nodes.odin:1530
LDtk_FindEntityByIid
Section titled “LDtk_FindEntityByIid”LDtk_FindEntityByIid :: proc(root: ^LDtkLevelNode, iid: string) -> ^NodeAn entity’s node by its iid (what EntityRef fields hold).
Inputs
- root: The LDtkLevel node (nil gives nil)
- iid: The entity’s iid
Returns
- The node, or nil when no entity built under
roothas that iid
procedure. Source: ldtk_nodes.odin:1556
Constants and variables
Section titled “Constants and variables”LDTK_ICONS_FILE
Section titled “LDTK_ICONS_FILE”LDTK_ICONS_FILE :: "finalbossblues-icons_full_16.png"The image of LDtk’s internal icons (tilesets with embedAtlas: LdtkIcons): FinalBossBlues’ Icons
pack (finalbossblues.itch.io/icons), which LDtk carries but which the engine can’t ship. Put your
own copy beside the .ldtk file under this name and entities using it draw their icons.
constant. Source: ldtk.odin:254
DEFAULT_LDTK_LEVEL_SETTINGS
Section titled “DEFAULT_LDTK_LEVEL_SETTINGS”DEFAULT_LDTK_LEVEL_SETTINGS :: LDtkLevelSettings{ spawnEntities = true, background = true,}The settings an LDtkLevel node starts with
constant. Source: ldtk_nodes.odin:161
LDTK_WATCH_SECONDS
Section titled “LDTK_WATCH_SECONDS”LDTK_WATCH_SECONDS :: 0.5How often development runs look at the LDtk files (seconds)
constant. Source: ldtk_nodes.odin:364
