Skip to content

Snap Studio Pro โ€” Capture API (headless / scripting)

Drive Snap Studio Pro captures from code โ€” your own editor scripts, or a headless -batchmode run for CI โ€” without opening the tool window.

It runs the exact production capture stack (RenderingManagerFactory โ†’ PrefabPreviewManager โ†’ CaptureEngine), the same one the golden-image regression harness uses, so output matches the interactive tool.

Covers static, animation, particle, and VFX capture (front angle). Directional (all-angle) export lands once its camera-rotation path is headless-safe โ€” see Roadmap.

Owns

  • Public API surface (SnapCapture, SnapBatchRunner, SnapJobResult, SnapRunReport)
  • JSON job schema (SnapJobFile and friends), parsed with Unity's JsonUtility
  • The headless stack builder (SnapPreviewStack)

Does NOT own

  • Capture / preview / pipeline implementation (it drives them as a black box)
  • The interactive tool window

From your own editor script

using RevGaming.SnapStudioPro.Api;

var result = SnapCapture.RunStatic(new SnapStaticJob
{
    prefabs = new[] { "Assets/Props/Barrel.prefab" },
    angles  = new[] { new SnapAngle { yaw = 45, pitch = 15 } },
});

Debug.Log(result.Success
    ? $"Wrote {result.ImageCount} images to {result.OutputDirectory}"
    : $"Failed: {result.Error}");

Animation โ€” a prefab's clip(s) to frames (+ optional sprite sheet / GIF):

var anim = SnapCapture.RunAnimation(new SnapAnimationJob
{
    prefab = "Assets/Characters/Hero.prefab",
    // clips = new[] { "Run", "Idle" },   // omit for every clip
    fps = 24,
    spriteSheet = true,
    gif = true,
});

Particle / VFX โ€” bake a simulated effect to frames (+ optional sprite sheet):

SnapCapture.RunParticle(new SnapParticleJob {
    prefab = "Assets/FX/Explosion.prefab", duration = 2, fps = 30, spriteSheet = true });

SnapCapture.RunVfx(new SnapVfxJob {        // needs SNAP_SUPPORTS_VFX
    prefab = "Assets/FX/Portal.prefab", duration = 3, fps = 30 });

Or run a whole job file (any mix of job types):

SnapRunReport report = SnapBatchRunner.RunJobFile("Assets/snap-jobs/assets.json");

Headless / CI

Unity -batchmode -quit -projectPath <project> \
      -executeMethod RevGaming.SnapStudioPro.Api.SnapBatchRunner.RunFromCommandLine \
      -snapJob Assets/snap-jobs/icons.json

Exit codes: 0 all jobs ok ยท 1 a job failed ยท 2 bad/missing -snapJob argument. Output goes to the Unity log (this surface uses Debug, not the quiet-by-default MyLogger, because batchmode has no diagnostics window and the log is the result).

Job file schema (JSON)

JsonUtility is used, so field names are case-sensitive and must match exactly. Missing fields fall back to defaults (strings via empty = inherit, ints via 0 = inherit). See sample-job.json.

{
  "defaults": {
    "outputDirectory": "Assets/SnapExports",  // where files go
    "format": "PNG",                          // PNG | JPG | TGA | EXR
    "width": 512, "height": 512,
    "transparent": true,                      // false -> solid backgroundColor
    "backgroundColor": "#1E4C52"
  },
  "staticJobs": [
    {
      "prefabs": ["Assets/A.prefab", "Assets/B.prefab"], // and/or:
      "prefabFolder": "Assets/Props",         // scanned recursively for prefabs
      "angles": [ { "yaw": 0, "pitch": 0, "roll": 0 } ], // empty -> one (0,0,0) view
      "outputDirectory": "", "format": "", "width": 0, "height": 0, // "" / 0 = inherit
      "separateByPrefab": false,              // subfolder per prefab
      "separateByAngle": false,               // subfolder per angle
      "fitCameraToPrefab": true,
      "exportNormalMap": false, "flatAlbedo": false
    }
  ],
  "animationJobs": [
    {
      "prefab": "Assets/Characters/Hero.prefab",
      "clips": ["Run", "Idle"],   // empty -> every clip on the prefab
      "fps": 24,                  // 0 -> 12
      "frameInterval": 1,         // capture every Nth frame (0 -> every frame)
      "startTime": 0, "endTime": 0, // seconds; endTime 0 -> clip length
      "reverse": false, "applyRootMotion": false,
      "outputDirectory": "", "format": "", "width": 0, "height": 0,
      "spriteSheet": true, "gif": false,
      "autoLayout": true, "framesPerRow": 8, "sheetPrefix": "",
      "exportNormalMap": false, "flatAlbedo": false
    }
  ],
  "particleJobs": [
    {
      "prefab": "Assets/FX/Explosion.prefab",
      "duration": 2, "fps": 30,       // duration 0 -> 2, fps 0 -> 30
      "warmup": 0,                    // seconds to pre-simulate steady-state emitters
      "trimStartFrame": 0, "trimEndFrame": -1,   // -1 = through the end
      "alphaMode": "None",            // None | Auto | Straight | Additive
      "forceShaderSwap": false, "shaderMode": "AlphaTint", // only if forceShaderSwap
      "outputDirectory": "", "width": 0, "height": 0,
      "spriteSheet": true, "autoLayout": true, "framesPerRow": 8, "sheetPrefix": ""
    }
  ],
  "vfxJobs": [
    {
      "prefab": "Assets/FX/Portal.prefab",   // requires SNAP_SUPPORTS_VFX
      "duration": 3, "fps": 30,
      "alphaMode": "Straight",               // Straight | None | Additive
      "spriteSheet": true
    }
  ]
}

How it runs headless

The interactive tool drives capture through an editor coroutine pumped by EditorApplication.update (async โ€” it would never finish under -batchmode -quit). This API instead drains the same IEnumerable capture routine synchronously in a plain loop: the routine's yields exist for editor responsiveness, not correctness (render-to-texture, particle/VFX simulation, and animation sampling are all synchronous), so every step runs in-line and the process completes deterministically. Async shader compilation is disabled for the run so first-frame captures never hit placeholder shaders.

Roadmap

  • Done: static (RunStatic/staticJobs[]), animation (RunAnimation/animationJobs[] โ€” frames + sheet + GIF), particle (RunParticle/particleJobs[]), and VFX (RunVfx/vfxJobs[], under SNAP_SUPPORTS_VFX) โ€” all front-angle.
  • Next: directional / all-angle export (any path). It's UI-bound today (IDirectionalPreviewUpdater โ†’ SceneView.RepaintAll() + live camera), so it needs a headless-safe camera-rotation seam before it can run under -batchmode.