Tech Discussion: Simulating Fire That Feels Real


In Firesafe Friend, fire isn’t just a visual effect—it’s a core gameplay system. It simulates how fire behaves across different house parts and materials, and teaches players how critical fire safety upgrades can be during a wildfire.

To support that, I designed a data-driven fire system in Unity, built to handle material variance, localized spread, and performance-friendly simulation across dozens of burnable objects.

How It Works

Every burnable object in the scene has a Combustible MonoBehaviour that handles logic. Fire properties are stored in a separate ScriptableObject (CombustibleInfo), giving us clean separation between logic and configuration.

public class FF_BaseCombustibleInfo : ScriptableObject
{
    public string partID;
    public float durability; // how long the part can resist fire
    public float flammability; //chance to ignite when near flames
    public Sprite icon;
    public MaterialClass materialClass; //a clear, intuitive label for how fire-resistant it is
    [TextArea]
    public string description;
}

This lets us easily tweak multiple fire profiles for different materials—wood, metal, brick, etc.— and apply them to any prefab without modifying code.

Heat-Based Spread System

Fire doesn’t spread automatically. Each Combustible tracks its own heat level over time. If the object is near one or more burning neighbors, it begins accumulating heat.

If currentHeat >= heatThreshold, the object ignites.

public virtual void AddHeat(float amount)
{
    if (heat > heatThreshold && !isOverHeated)
    {
        TryIgnite();
        isOverHeated = true;
        return;
    }
    heat += amount;
}
public virtual void TryIgnite()
{
    if (isOnFire) return;

    float fireCatchChance = CalculateFireCatchChance(flammability);

    if (UnityEngine.Random.value < fireCatchChance)
    {
       StartCoroutine(IgniteWithDelay());
    }
}

protected virtual IEnumerator IgniteWithDelay()
{
    yield return new WaitForSeconds(durability / 10 + baseBurnTime);
    isOnFire = true;
    burnTimer = durability / flammability + baseBurnTime;
    StartCoroutine(Burn());
}

That means:

  • Fire spreads faster between F-rated, flammable parts

  • Upgraded A-rated parts can hold off fire, slow the spread, or even stop it

This lets players watch a fire travel across a structure in ways that feel natural—and avoidable with the right upgrades.

Fire Lifecycle & Stages

All combustibles track their own fire lifecycle using a BurnStage enum:

enum BurnStage {
    BeforeIgniting,
    Igniting,
    Burning,
    BurnedOut
}

The burn logic is driven by a coroutine:

protected virtual IEnumerator Burn()
{
    BurnStage = BurnStage.Igniting;
    while (isOnFire)
    {
        burnTimer -= Time.deltaTime;
        if (burnTimer / startBurnTimer <= 0.25f) BurnStage = BurnStage.BurnedOut;
        else if (burnTimer / startBurnTimer <= 0.75f) BurnStage = BurnStage.Burning;

        yield return null;
    }
}

Each stage triggers events (OnIgnite, OnBurning, OnBurnedOut) which are used for visual FX, state transitions, and interaction logic.

Spreading Fire Across House Parts

BaseHousePartObject extends the base fire logic and introduces neighbor-aware spread using house graph data. When a part starts burning, it begins trying to ignite nearby parts:

private IEnumerator SpreadFireToNeighbour()
{
    while (isOnFire)
    {
        foreach (var neighbor in houseGraph.GetNeighbors(houseNode))
        {
            float distance = Vector3.Distance(transform.position, neighbor.transform.position);
            float delay = Mathf.Clamp(distance * 0.5f, 1f, 5f);
            yield return new WaitForSeconds(delay);
            neighbor.housePart?.TryIgnite();
        }
        yield return new WaitForSeconds(10f);
    }
}

This gives us localized, structure-based fire propagation—and makes upgraded materials valuable not just for the part itself, but for slowing down fire spread in general.

Visual Feedback & Destruction

The system handles real-time visual transitions through material color changes, VFX, and mesh replacement:

  • Burning parts slowly lerp to a burntColor

  • Burned parts are visually replaced with a burntModel mesh

  • Fire VFX are spawned and animated along the object’s height

private void HandleBurning()
{
    if (burntModel && burntMesh == null)
    {
        burntMesh = Instantiate(burntModel, transform);
        burntMesh.material = mesh.material;
        mesh.gameObject.SetActive(false);
    }
}

When a part is destroyed, it gets disabled or removed entirely, and the house’s burnedWeight is updated to reflect progress.

Leave a comment

Log in with itch.io to leave a comment.