Header menu logo Mibo.Raylib

Animation (2D Sprite Animation)

Mibo provides a format-agnostic 2D animation system in Mibo.Animation. It integrates with the existing Draw.sprite / LightDraw.litSprite rendering pipeline.

Core Types

Type

Purpose

Animation

A struct holding frame rectangles, duration, and loop flag

GridAnimationDef

Definition for animations in grid-based spritesheets

SpriteSheet

Texture + named animations with O(1) index-based access

AnimatedSprite

Runtime state (current frame, time, visual properties)

Quick Start

open Mibo.Animation

// 1. Create a SpriteSheet from a uniform grid
let sheet = SpriteSheet.fromGrid texture 32 32 8 [|
  { Name = "idle"; Row = 0; StartCol = 0; FrameCount = 1; Fps = 1.0f; Loop = false }
  { Name = "walk"; Row = 1; StartCol = 0; FrameCount = 4; Fps = 8.0f; Loop = true }
|]

// 2. Create an AnimatedSprite
let sprite = AnimatedSprite.create sheet "idle"

// 3. Update each frame (in your animation system)
let updatedSprite = AnimatedSprite.update deltaTime sprite

// 4. Draw (in your view)
let src = AnimatedSprite.currentSource sprite
buffer |> Draw.sprite {
    Texture = sheet.Texture; Dest = r (int position.X) (int position.Y) 32 32
    Source = src; Origin = Vector2.Zero; Rotation = 0f
    Color = Color.White; Layer = 0<RenderLayer>
}

SpriteSheet Factory Functions

SpriteSheet.fromGrid – Uniform Grid Layouts

let sheet = SpriteSheet.fromGrid texture 48 48 4 [|
  { Name = "idle";   Row = 0; StartCol = 0; FrameCount = 1; Fps = 1.0f;  Loop = false }
  { Name = "walk";   Row = 1; StartCol = 0; FrameCount = 4; Fps = 8.0f;  Loop = true }
  { Name = "attack"; Row = 2; StartCol = 0; FrameCount = 6; Fps = 12.0f; Loop = false }
|]

The GridAnimationDef struct:

[<Struct>]
type GridAnimationDef = {
  Name: string
  Row: int
  StartCol: int
  FrameCount: int
  Fps: float32
  Loop: bool
}

SpriteSheet.single – Explicit Frame Rectangles

let frames = [|
  Rectangle(0, 0, 64, 64)
  Rectangle(64, 0, 64, 64)
  Rectangle(128, 0, 64, 64)
|]
let sheet = SpriteSheet.single texture frames 10.0f true

SpriteSheet.fromFrames – Full Control

let idleAnim: Animation = {
  Frames = [| Rectangle(0, 0, 48, 48) |]
  FrameDuration = 1.0f
  Loop = false
}

let walkAnim: Animation = {
  Frames = [| for i in 0..3 -> Rectangle(i * 48, 48, 48, 48) |]
  FrameDuration = 1.0f / 8.0f
  Loop = true
}

let sheet = SpriteSheet.fromFrames texture (Vector2(24.0f, 24.0f)) [|
  "idle", idleAnim
  "walk", walkAnim
|]

SpriteSheet.static' – Single Static Frame

let sheet = SpriteSheet.static' texture (Rectangle(0, 0, 32, 32))
let sprite = AnimatedSprite.create sheet "default"

Animation Index Queries

let walkIdx =
  match SpriteSheet.tryGetAnimationIndex "walk" sheet with
  | ValueSome idx -> idx
  | ValueNone -> 0

let sprite = oldSprite |> AnimatedSprite.playByIndex walkIdx

AnimatedSprite API

Creation and Animation Control

let sprite = AnimatedSprite.create sheet "idle"
let colored = AnimatedSprite.createWith sheet "idle" Color.Red 1.5f
let walkingSprite = sprite |> AnimatedSprite.play "walk"
let sprite = sprite |> AnimatedSprite.playIfNot "walk"
let sprite = sprite |> AnimatedSprite.restart
let isWalking = sprite |> AnimatedSprite.isPlaying "walk"

Update

let updated = AnimatedSprite.update deltaTime sprite

Visual Properties

sprite
|> AnimatedSprite.withScale 2.0f
|> AnimatedSprite.withColor Color.Red
|> AnimatedSprite.withRotation (MathF.PI / 4.0f)
|> AnimatedSprite.flipX true
|> AnimatedSprite.facingLeft

Drawing

AnimatedSprite does not provide its own draw functions. Instead, use AnimatedSprite.currentSource to get the current frame's source rectangle, then draw via the general Draw.sprite or LightDraw.litSprite:

let src = AnimatedSprite.currentSource sprite
buffer |> Draw.sprite {
    Texture = sprite.Sheet.Texture
    Dest = Rectangle(int position.X, int position.Y, int src.Width, int src.Height)
    Source = src
    Origin = Vector2.Zero
    Rotation = sprite.Rotation
    Color = sprite.Color
    Layer = layer
}

Manual Frame Access

let sourceRect = AnimatedSprite.currentSource sprite
let texture = sprite.Sheet.Texture

buffer.Add(layer, DrawSprite {
    Texture = texture
    Dest = Rectangle(position.X, position.Y, float32 sourceRect.Width, float32 sourceRect.Height)
    Source = sourceRect
    Origin = Vector2.Zero
    Rotation = 0f
    Color = Color.White
    Layer = layer
})

Animation Type

[<Struct>]
type Animation = {
  Frames: Rectangle[]
  FrameDuration: float32
  Loop: bool
}

Helpers

let totalTime = Animation.duration anim
let spriteTime = AnimatedSprite.duration sprite
let finished = AnimatedSprite.isFinished sprite

Performance Tips

  1. Resolve animation names once: Use AnimationIndices + playByIndex to avoid string allocations in update loops
  2. Share SpriteSheets: create sheets once at init, reuse for all instances
// At init time
let walkIndex = sheet.AnimationIndices["walk"]

// In update (zero allocations)
let sprite = oldSprite |> AnimatedSprite.playByIndex walkIndex

Texture Atlases & Sprite Management

Mibo is format-agnostic: a SpriteSheet is simply a Texture plus a set of Source Rectangles.

Textures are Raylib_cs.Texture2D and rectangles are Raylib_cs.Rectangle.

// Example: pseudo-code for a custom loader
let loadHero (ctx: GameContext) =
    let tex = Raylib.LoadTexture "hero_atlas"
    let frames = MyJsonParser.parse "hero_metadata.json"
    SpriteSheet.fromFrames tex (Vector2(32.f, 32.f)) frames

See Also

val sheet: obj
val sprite: obj
val updatedSprite: obj
val src: obj
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
Multiple items
type StructAttribute = inherit Attribute new: unit -> StructAttribute

--------------------
new: unit -> StructAttribute
[<Struct>] type GridAnimationDef = { Name: string Row: int StartCol: int FrameCount: int Fps: float32 Loop: bool }
Multiple items
val string: value: 'T -> string

--------------------
type string = System.String
Multiple items
val float32: value: 'T -> float32 (requires member op_Explicit)

--------------------
type float32 = System.Single

--------------------
type float32<'Measure> = float32
type bool = System.Boolean
val frames: obj array
val idleAnim: GridAnimationDef
val walkAnim: GridAnimationDef
val walkIdx: int
union case ValueOption.ValueSome: 'T -> ValueOption<'T>
val idx: int
union case ValueOption.ValueNone: ValueOption<'T>
val colored: obj
val walkingSprite: obj
val isWalking: obj
val updated: obj
val sourceRect: obj
val texture: obj
[<Struct>] type Animation = { Frames: obj array FrameDuration: float32 Loop: bool }
val totalTime: obj
val spriteTime: obj
val finished: obj
val walkIndex: obj
val loadHero: ctx: 'a -> 'b
val ctx: 'a
val tex: obj
val frames: obj

Type something to start searching.