{"id":"naithss/anima","name":"anima","scope":"naithss","platform":"roblox","description":"fork of sentinel69402 Anima.","version":"0.0.4","latest":"0.0.4","versions":["0.0.2","0.0.3","0.0.4"],"license":"Apache-2.0","licenseRating":"safe","licenseCaveats":["Modified files must carry a notice of changes. If the package ships a NOTICE file, its attributions must be preserved.","License identified from the packaged LICENSE file; the manifest declared none."],"licenseVerified":true,"dependencies":{},"integrity":"f75c05924d9fecc658095cd7fea5026ed266744c7edf3211f8405764065b1e0a","likes":0,"downloads":0,"install":"forest install naithss/anima","url":"https://forest.dev/p/roblox/naithss/anima","files":"https://api.forest.dev/ai/package/roblox/naithss/anima/files","readme":"<img src=\"assets/anima128.png\" width=\"\">\n\n# Anima; Tame your animations\n\n[![License](https://img.shields.io/badge/License-Apache2.0-blue.svg)](LICENSE)\n[![Version](https://img.shields.io/badge/Version-2-green.svg)](README.md)\n[![Roblox Studio](https://img.shields.io/badge/Compatible-Roblox%20Studio-red.svg)]()\n\n---\n\nAnimations that don’t fight back.\n\nAnima is a lightweight animation library for Roblox. It’s built to remove the repetitive boilerplate of loading tracks and to fix the common conflicts that come with Roblox's default character scripts. It gives you clean control over playback, blending, and state transitions without trying to own your actual gameplay logic.\n\nThe goal is simple: You decide what should play, and Anima handles the behavior.\n\n---\n\n### Why use this?\n\nStandard Roblox animation code often turns into a mess of:\n\n- Duplicated track loading logic\n- Fighting priorities between scripts\n- Jarring animation cuts\n- Hard-to-read movement checks scattered everywhere\n\nAnima approach is different:\n\n- Tracks are cached once and reused.\n- Animations stay alive at weight 0 during blends to avoid \"popping\".\n- It uses actual blending instead of just stopping and starting tracks.\n- High-level systems (like state machines) are completely optional.\n\n---\n\n### Core Features\n\n- **Zero-cost Caching**: AnimationTracks are loaded once and stored. No runtime hitches from loading assets on the fly.\n- **1D Blend Controllers**: Smoothly interpolate between states like Idle, Walk, and Run based on a single value (usually speed).\n- **Optional State Machine**: A simple way to manage high-level intent (e.g., \"Is the character jumping?\") without mixing it into your physics code.\n- **Folder-Based**: No manual configuration. Point it at a folder and it finds everything for you.\n- **NPC & Custom Rig Support**: Works out of the box for NPCs or non-humanoid models using either a `Humanoid` or an `AnimationController`.\n- **Singleton Pattern**: Automatically manages and returns a single instance per Player, preventing duplicated logic.\n- **Animate Conflict Handling**: Anima can automatically disable Roblox’s default \"Animate\" script so it doesn't fight your custom system.\n- **Well Documented**: Clear internal comments and a clean API designed for a better developer experience.\n\n---\n\n### What Anima isn't\n\nTo keep it lightweight, this library specifically avoids:\n\n- Movement logic or physics\n- Input handling\n- Character controllers\n- Gameplay decisions\n\nAnima handles how the animations look and transition, but it doesn't decide how your game works.\n\n---\n\n### Quick Start\n\n```lua\nlocal Anima = require(game.ReplicatedStorage.Anima)\n\n-- (folder, subject [Player or NPC Model], debug?, disableAnimate?)\nlocal anima = Anima.new(\n    game.ReplicatedStorage.anims,\n    game.Players.LocalPlayer, -- Or workspace.NPC\n    false,\n    true\n)\n\nanima:PlayAnimation(\"Idle\", {\n    loop = true,\n    fadeTime = 0.2\n})\n\n-- Optional: Initialize with IDs instead of a folder\nlocal animaWithIds = Anima.new({\n    Idle = 12345678,\n    Walk = 87654321,\n}, game.Players.LocalPlayer)\n```\n\n---\n\n### 1D Blend Controllers\n\nThese keep animations playing in the background and smoothly shift their weights based on speed.\n\n```lua\nanima:CreateBlendController(\"Locomotion\", {\n    nodes = {\n        { name = \"Idle\", min = 0,  max = 1 },\n        { name = \"Run\",  min = 6,  max = 16 }\n    }\n})\n\n-- Update in a loop\nlocal speed = humanoid.MoveDirection.Magnitude * humanoid.WalkSpeed\nanima:UpdateBlend(\"Locomotion\", speed)\n```\n\nThis prevents the \"snapping\" or \"popping\" usually seen when switching between walk and run states.\n\n---\n\n### State Machine\n\nUse this to define high-level character intent. A common pattern is to let the State Machine decide _what_ the character is doing, and let a Blend Controller decide _how_ that motion looks.\n\n```lua\nlocal SM = anima:CreateStateMachine({\n    initial = \"Locomotion\",\n    context = { IsJumping = false }\n})\n\nSM:AddState(\"Jump\", {\n    onEnter = function()\n        anima:PlayAnimation(\"Jump\")\n    end,\n    transitions = {\n        Locomotion = function(ctx) return not ctx.IsJumping end\n    }\n})\n```\n\n---\n\n### Examples\n\nThe `examples/` folder contains focused scripts for specific use cases:\n\n1. **Basic Playback**: The simplest way to play an animation.\n2. **Disabling Animate**: How to take full control of a character.\n3. **State Machines**: Managing high-level animation states.\n4. **Locomotion Blending**: Smoothly handling movement speed transitions.\n5. **Composition**: Using states and blending together.\n6. **Overlays**: Layering actions (like punching) over movement using priorities.\n7. **NPC Support**: Operating Anima on non-player characters and `AnimationController` rigs.\n\n---\n\n### API Reference\n\n#### Core Types\n\n```lua\ntype PlaybackConfig = {\n    fadeTime: number?,           -- Default: 0.2\n    weight: number?,             -- Default: 1.0\n    priority: Enum.Priority?,    -- Default: Action\n    loop: boolean?,              -- Default: false\n    stopOthers: boolean?         -- Default: true\n}\n\ntype BlendNode = {\n    name: string,                -- Animation name\n    min: number,                 -- Start influence\n    max: number                  -- Peak influence\n}\n\ntype State = {\n    onEnter: (() -> ())?,        -- Called when entering state\n    onExit: (() -> ())?,         -- Called when leaving state\n    transitions: { [string]: (context: table) -> boolean }?\n}\n```\n\n#### Anima Methods\n\n**Initialization**\n\n- `Anima.new(source, subject, debug?, disableAnimate?)` -> `Anima`\n  - `source` can be a **Folder** or a **Table** (`{[string]: number | string}`).\n  - `subject` can be a **Player** or a **Model** (NPC/Custom Rig).\n  - Automatically detects and utilizes a **Humanoid** or **AnimationController** within the model.\n  - If a Player is provided, it returns a singleton instance.\n- `:LoadAnimations()` - Manually re-scan the animation folder.\n- `:Cache()` - Reloads character animator and tracks (call this on respawn).\n- `:WatchForChanges()` - Enables live-reloading of animations during development.\n\n**Playback**\n\n- `:PlayAnimation(name, config?)` -> `AnimationTrack?`\n- `:StopAnimation(name, fadeTime?)` -> `boolean`\n- `:PauseAnimation(name)` / `:ResumeAnimation(name)`\n- `:PlayLooped(name, loopCount?)`\n- `:SetAnimation(name, id)` - Dynamically set or replace an animation using an ID or Instance.\n- `:SetAnimationSpeed(name, speed)` - Persistently sets the playback speed for an animation.\n- `:SetWeight(name, weight, fadeTime?)`\n- `:FadeAllOut(fadeTime?)`\n\n**Queries**\n\n- `:GetAnimationTrack(name)` -> `AnimationTrack?`\n- `:GetPlayingTracks()` -> `{ string }`\n- `:GetAnimationProgress(name)` -> `(progress: number, isPlaying: boolean)`\n- `:IsAnimationPlaying(name)` / `:IsAnimationLoaded(name)`\n\n**Grouping & Sequencing**\n\n- `:QueueAnimations(names, config?)` - Sequential playback.\n- `:playSequence(names, fadeTime)` - Simplified sequential playback.\n- `:setAnimationTag(name, tag)` / `:fadeOutTag(tag, fadeTime?)` - Batch control via tags.\n\n**Systems**\n\n- `:CreateBlendController(name, profile)` -> `BlendController`\n- `:UpdateBlend(name, value)`\n- `:CreateStateMachine(config)` -> `StateMachine`\n\n**Lifecycle**\n\n- `:getPlaySignal()` / `:getStopSignal()` - Returns custom Signal objects.\n  - Listeners receive: `(animationName: string, player: Player?, character: Model)`\n- `:setAnimationCallbacks(name, callbacks)` - Hook into state changes or markers.\n- `:Destroy()` - Clean up all tracks and event connections.\n\n#### BlendController Methods\n\n- `:Update(value)` - Manually update the blend tree weights.\n- `:Destroy()` - Clean up the controller.\n\n#### StateMachine Methods\n\n- `:AddState(name, state)` - Register a new state.\n- `:GetState()` -> `string` - Returns current state name.\n- `:SetState(name)` - Force a state transition.\n- `:Update()` - Evaluate transitions based on context.\n- `:Lock(duration)` - Prevent transitions for a set amount of time.\n- `.Context` - Workspace table for transition data.\n\n---\n\n### Philosophy\n\nAnima is designed to be predictable and composable. It shouldn't have side effects that surprise you. If it does, that's a bug.\n\n---\n\n### License\n\nApache License 2.0\n\n![Anima Promo](assets/AnimaPromo.gif)\n","readmeTruncated":false}