Unity Helpers

Logo

Treasure chest of Unity developer tools. Professional inspector tooling, high-performance utilities, spatial queries, and 20+ editor tools.

Visual Components Guide

TL;DR — Why Use These

These components solve the problem of creating complex, multi-layer sprite animations without pre-rendering every combination into massive sprite sheets.


AnimatedSpriteLayer

What it is: An immutable data structure (struct) that packages a sprite animation sequence with per-frame position offsets and layer-wide alpha transparency.

Why it exists: When building complex sprite animations (like a character with equipment, effects, or layered body parts), you need a standardized way to represent each layer with its timing, positioning, and transparency. This struct is the building block for the LayeredImage composition system.

Problem it solves:

When to Use

Use when:

Don’t use when:

Basic Usage

using WallstopStudios.UnityHelpers.Visuals;
using UnityEngine;

// Create a layer from sprite sequence
Sprite[] walkCycleFrames = LoadWalkCycleSprites(); // Must have Read/Write enabled!

// Optional: per-frame offsets in world space (e.g., for bobbing motion)
Vector2[] walkBobOffsets = new[]
{
    new Vector2(0, 0.1f),   // Frame 0: slight up
    new Vector2(0, 0),      // Frame 1: neutral
    new Vector2(0, 0.1f),   // Frame 2: slight up
    new Vector2(0, 0)       // Frame 3: neutral
};

AnimatedSpriteLayer bodyLayer = new AnimatedSpriteLayer(
    sprites: walkCycleFrames,
    worldSpaceOffsets: walkBobOffsets,
    alpha: 1f  // Fully opaque
);

// Create equipment layer (same frame count, different sprites)
Sprite[] helmetFrames = LoadHelmetSprites();
AnimatedSpriteLayer helmetLayer = new AnimatedSpriteLayer(
    sprites: helmetFrames,
    worldSpaceOffsets: null, // No offsets
    alpha: 0.9f  // Slightly transparent
);

// Combine in LayeredImage (see below)

Editor-Only: Creating from AnimationClip

In editor code, you can create layers directly from AnimationClips:

#if UNITY_EDITOR
using UnityEditor;

AnimationClip walkClip = AssetDatabase.LoadAssetAtPath<AnimationClip>("Assets/Animations/Walk.anim");
AnimatedSpriteLayer layer = new AnimatedSpriteLayer(
    clip: walkClip,
    worldSpaceOffsets: null,
    alpha: 1f
);
#endif

Important Notes

Texture Readability: All sprites must have Read/Write Enabled in their texture import settings. The constructor validates this and logs errors for non-readable textures.

To fix:

  1. Select the texture asset
  2. In Inspector, check “Read/Write Enabled”
  3. Click Apply

Frame Rate: Default frame rate is 12 fps (stored in AnimatedSpriteLayer.FrameRate constant). This matches classic sprite animation timing.

Offset Conversion: World-space offsets are automatically converted to pixel-space using sprite pixels-per-unit. This ensures offsets scale correctly with sprite resolution.


LayeredImage

What it is: A UI Toolkit VisualElement that composites multiple AnimatedSpriteLayer instances into a single animated image with alpha blending, automatic cropping, and frame timing.

Why it exists: Creating character customization systems (body + equipment), visual effects (base + glow), or any multi-layer sprite animation traditionally requires pre-rendering every combination into massive sprite sheets. LayeredImage composes layers dynamically at runtime.

Problem it solves:

When to Use

Use when:

Don’t use when:

Basic Usage

using WallstopStudios.UnityHelpers.Visuals.UIToolkit;
using UnityEngine.UIElements;

// Create layers (see AnimatedSpriteLayer section above)
AnimatedSpriteLayer[] layers = new[]
{
    bodyLayer,    // Base character
    armorLayer,   // Equipment layer 1
    helmetLayer,  // Equipment layer 2
    glowLayer     // Effect layer
};

// Create LayeredImage
LayeredImage characterImage = new LayeredImage(
    inputSpriteLayers: layers,
    backgroundColor: null,  // Transparent background (or use Color for solid background)
    fps: 12f,               // Animation speed
    updatesSelf: true,      // Automatically advances frames (uses Unity editor ticks or coroutines)
    pixelCutoff: 0.01f      // Alpha threshold for cropping transparent pixels
);

// Add to UI Toolkit hierarchy
rootVisualElement.Add(characterImage);

Manual Frame Control

If you need precise control over frame advancement:

LayeredImage manualImage = new LayeredImage(
    inputSpriteLayers: layers,
    backgroundColor: null,
    fps: 12f,
    updatesSelf: false,  // Disable automatic updates
    pixelCutoff: 0.01f
);

// In your update loop
void Update()
{
    manualImage.Update(force: false); // Advances frame based on elapsed time
}

// Or force immediate frame advance
manualImage.Update(force: true);

Changing Animation Speed

// Set frames per second at runtime
characterImage.Fps = 24f; // Speed up animation
characterImage.Fps = 6f;  // Slow down animation

Visual Demo

LayeredImage in Action

LayeredImage showing multi-layer character animation compositing in real-time

Character with dynamically composited layers animating at runtime

LayeredImage layer swapping demo showing layers being changed and animation updating instantly

Swapping equipment layers at runtime without pre-rendered sprite sheets

How Compositing Works

LayeredImage performs these steps each frame:

  1. Allocates canvas: Creates a texture large enough to hold all layers with their offsets
  2. Alpha blending: Layers are composited back-to-front with alpha blending
  3. Pivot alignment: Each sprite’s pivot point is respected during positioning
  4. Offset application: Per-frame pixel offsets are applied
  5. Cropping: Transparent borders are trimmed (configurable via pixelCutoff)
  6. Rendering: Final composited texture is displayed

Performance optimization:

Pixel Cutoff Parameter

Controls how aggressive transparent pixel cropping is:

// More aggressive cropping (removes near-transparent pixels)
layeredImage.pixelCutoff = 0.05f;

// Less aggressive (keeps more semi-transparent pixels)
layeredImage.pixelCutoff = 0.001f;

// No cropping (includes fully transparent border)
layeredImage.pixelCutoff = 0f;

Higher values = smaller final image, but may clip soft edges (glows, shadows).

Important Notes

Frame Synchronization: All layers must have the same number of frames. Mixing 4-frame and 8-frame animations will cause visual glitches.

Performance Considerations:

Editor vs Runtime:


EnhancedImage (UGUI)

What it is: An extended version of Unity’s UI Image component with HDR color support and texture-based shape masking.

Why it exists: Unity’s standard Image component doesn’t support:

See full documentation: Editor Tools Guide - EnhancedImage

Visual Demo

EnhancedImage HDR color intensity slider being adjusted, showing bloom effect increasing

HDR color values above 1.0 create bloom effects when post-processing is enabled

Quick example:

using WallstopStudios.UnityHelpers.Visuals.UGUI;

EnhancedImage image = GetComponent<EnhancedImage>();

// HDR color for bloom
image.HdrColor = new Color(2f, 0.5f, 0.1f, 1f); // RGB values > 1 for bloom

// Shape mask
image.shapeMask = myMaskTexture; // Black areas are transparent

Best Practices

AnimatedSpriteLayer

LayeredImage

EnhancedImage



FAQ

Q: Can I mix different frame counts in LayeredImage? A: No, all layers must have the same frame count. Pad shorter animations with duplicate frames if needed.

Q: Why are my layers not aligned correctly? A: Check that sprite pivots are set correctly (usually center). LayeredImage respects sprite pivot points.

Q: Can I change layers at runtime? A: Currently no, LayeredImage is immutable after construction. Create a new instance with updated layers.

Q: Performance impact vs pre-rendered sprites? A: Compositing costs ~1-3ms per image on modern hardware. Pre-rendered is faster but uses more memory/storage.

Q: Does this work with Unity’s Animator? A: No, LayeredImage is independent. It’s designed for UI Toolkit programmatic control.

Q: Can I export the composited result? A: Not directly, but you could capture the rendered texture using Texture2D.ReadPixels in a render texture setup.