Dice Roller Pro just got a major upgrade

If you’ve been using Dice Roller Pro in your Unity project – or you’ve been eyeing it on the Asset Store – the latest update is a big one. I’ve spent the last stretch reworking the library from the inside out: a friendlier API, a smarter parser, faster rolls, hardened security, and a much more polished Editor experience.

Here’s what’s new and why it matters.

A simpler entry point: the Dice static API

Until now, every roll started with new Parser() and a System.Random. That’s fine for a library, but it’s a lot of ceremony for “just give me a number.”

Meet the new Dice static class:

int total = Dice.Roll("3d6+2");
if (Dice.TryRoll("4d6kh3!", out int dmg))
{
    ApplyDamage(dmg);
}

Behind the scenes it owns a thread-safe parser, a warm expression cache, and a per-thread System.Random seeded from Guid.NewGuid(). No more global state to manage – and the non-throwing TryRoll / TryParse overloads make user-input handling painless.

Notation upgrades

The parser learned a few new tricks tabletop folks have been asking for:

  • Modulo (%)1d100 % 6 works, with the same precedence as * and /.
  • Unary minus -1d6 + 10 finally evaluates the way you’d expect.
  • Uppercase D1D6 is now a synonym for 1d6. No more “why doesn’t this work?” support requests.
  • PEMDAS – operator precedence is properly respected across nested groups and parentheses.

Fluent modifiers

Configuring Modifiers used to mean object initializers and remembering which fields conflict with which. The new fluent builders fix that:

var brutal = Modifiers.Default
    .WithKeepHighest(3)
    .WithExplode();         // 4d6kh3!

Every With* returns a fresh Modifiers instance, so there’s no shared-state surprise when two dice point at the same configuration. The explode flavors (basic, compound, penetrating) are mutually exclusive – pick one and the others clear automatically.

A real Editor experience

This is where the update shines for asset-driven workflows.

Roll Preview on every dice asset. Open any BaseRoll in the Inspector and you’ll see a Roll Preview panel: pick an iteration count, hit Roll, and see live min / max / average / last value / last explanation. No more dropping a script in the scene just to sanity-check a configuration.

A guided Modifiers drawer. Keep/Drop collapses to a single “kind + value” row. Explode/Compound/Penetrating collapses to one dropdown. Mutually exclusive options are enforced in the UI, not just at
runtime.

Reorderable Group & Sequence editors. Composite assets get a proper ReorderableList. The + button shows a menu of every concrete dice type and creates a fresh sub-asset nested inside the parent — no
asset-graveyard hunting.

Distinct icons per dice type. Your Project window stops being a sea of identical script icons.

Native [CreateAssetMenu] on Normal / Fate / Percentile / Custom / Number dice, so they show up exactly where Unity users expect them.

Hardened against pathological input

If you’re parsing user expressions in a multiplayer game, a streaming overlay, or a community-content tool, this matters:

  • Input length capped at 10 000 chars.
  • Recursion depth capped at 64.
  • Node count capped at 5 000.
  • Division and modulo by zero now throw DivideByZeroException instead of silently returning the dividend.
  • The expression cache is atomically size-bounded, so a flood of unique inputs can’t quietly blow up memory.

Dice.TryRoll will catch all of these for you and return false – your game keeps running.

Faster, quieter, lighter

A bunch of allocation hot-spots got cleaned up:

  • BaseDice no longer boxes every die value or allocates a per-roll list.
  • Result is a sealed class with a read-only IResult contract – no defensive copying needed.
  • Keep/drop modifiers skip the final sort when they don’t need it.
  • The expression cache prewarms automatically once per play session via RuntimeInitializeOnLoadMethod, so the first roll of a play session isn’t the slowest one.

Bug fixes worth calling out

A few long-standing issues finally got squashed:

  • Exploding dice now chain correctly on consecutive maxes (a 6 after a 6! keeps going).
  • Sequence and Group no longer corrupt their results when used reentrantly or across threads.
  • CustomDice now supports the full exploding-dice semantics and survives serialization round-trips.
  • RollExplainer no longer adds confusing K/D markers when nothing was actually dropped.

What does the workflow look like now?

using DiceRollerPro;

// Quick rolls — no setup required
int hp = Dice.Roll("8d10+24");

// Safe parsing of player input
if (Dice.TryRoll(playerInput, out int result))
{
    ShowResult(result);
}
else
{
    ShowError("That's not a valid dice expression.");
}

// Or build it fluently in code
var attack = Modifiers.Default
    .WithKeepHighest(3)
    .WithExplode();

Or stay in the Editor: drag a NormalDice asset into your scene, configure modifiers visually, and use the Roll Preview to confirm the average damage matches your design intent – all without writing a line
of code.