Spreading fire - how to identify existing flame and not clone on top of it

I am trying to create spreading fire by cloning the flame in the empty positions around it, however I need to identify if a flame already exists otherwise it will clone 729 instances by the third update and crash (the for loops clone 9 times).

Here is my code. What should I put where I say EXISTING_FLAME to indicate there is an existing flame in that position so it will only create a clone if there is no flame there already? The flame is a particle system (currently Fire_03 from the free Elementals assets.)

void CloneFire ()
    {
        for (int i = -1; i < 1; i++)
        {
            for (int j = -1; j < 1; j++)
            {
                Vector3 spaceToSpread = new Vector3(i,0,j);
                Vector3 newFirePos = transform.position + spaceToSpread;
                if (newFirePos != EXISTING_FLAME)
                {
                    var newFire = GameObject.Instantiate(gameObject, newFirePos, Quaternion.identity);
                }
            }
        }
    }

The easiest method to perform this check is to attach a script called ‘fire’ (or similar) to your fire prefab, and have the script add/remove itself from a static list in OnEnable/OnDisable respectively. Then in your fire spawning script you can loop through all fires this static list, and skip the spawning if a fire is found in the desired location.

If your game is grid based then it might be easier and more performant to create a single 2D array that stores a reference to each fire, and when spawning a fire you first check to see if that cell is occupied.

Thanks, the 2D array sounds like the way to go since I was planning to use something similar for burn values of various objects.

My game grid is 50 by 50 so I set up an array that would reference those values. In my start function I set the array value where the fire starts to 1 (fire exists.) Before cloning the fire I check to see if the array value is 0 (fire doesn’t exist) and if I clone the fire I set the array value to 1. The problem is that the array doesn’t seem to be holding it’s value at each call of CloneFire, so it is creating 8 clones (only realizing the fire it is cloning from exists) every time. Here is the code:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireSpread : MonoBehaviour
{
    private float startTime;
    float pollTime = 5.0f; // Seconds between fire spreading
    private Vector3 spaceToSpread = new Vector3(0.0f, 0.0f, 0.0f);
    public int[,]fireGrid = new int[50, 50];

    // Use this for initialization
    void Start ()
    {
        startTime = Time.time;
        fireGrid [(int) transform.position.x, (int) transform.position.z] = 1;
    }
 
    // Update is called once per frame
    void Update ()
    {
        if(Time.time > startTime + pollTime)
        {
            startTime = Time.time;
            CloneFire();
        }
    }

    void CloneFire ()
    {
        for (int i1 = -1; i1 <= 1; i1++)
        {
            for (int j1 = -1; j1 <= 1; j1++)
            {
                spaceToSpread.x = i1;
                spaceToSpread.z = j1;
                Vector3 newFirePos = (transform.position + spaceToSpread);
                int x1 = (int) newFirePos.x;
                int z1 = (int) newFirePos.z;
                if (fireGrid[x1, z1] == 0)
                {
                    var newFire = GameObject.Instantiate (gameObject, newFirePos, Quaternion.identity);
                    fireGrid[x1, z1] = 1;
                }         
            }

        }
    }
}

Any idea why the fireGrid array is not holding onto the 8 values of 1 that are created during each call?

Use code tags!

Your array isn’t being shared between the fires because every time you call instantiate it’s being duplicated. What you should be doing is storing the fire data in a separate singular location, e.g. Some type of fire manager script, or a static array.

Thanks Darkcoder. I initially tried to have the array in a separate script, but was having a lot of issues, however I was also having a lot of somewhat basic errors in my current script for quite a while as well since I’m new to C#. I can try that again. I might also look into the static array. Any suggestions as to which is easier?

I also want to create an array that has the burn values for each spot and will allow me to adjust pollTime based on that. It would be nice if the array could automatically detect the game object in the spot and use some tag to hold the burn value, but I’ve heard some people recommend populating such an array by hand, and it would be nice if I could do that with a spreadsheet rather than in the code.

Both methods are fine, but using a component allows for more flexibility if needed.

I decided to go with a static array. I also created a static array for the pollTime (burn) values and populated it by hand in the code. A lot of copying and pasting, but I set my floor up symmetrically in all 4 quadrants, and used Math.Abs to make it easier (5000 value array rather than 20,000 value array since I expanded my floor to 100 by 200.)

So far everything works good, although player moment slows when a lot of clones exist. I destroy them after 10 seconds, so it may be something else. I am getting this exception:

IndexOutOfRangeException: Array index is out of range.
FireSpread.CloneFire () (at Assets/Scripts/FireSpread.cs:147)
FireSpread.Update () (at Assets/Scripts/FireSpread.cs:132)

These correspond to line 47 and 32 in the code below, and I’m am getting 999+ of these from line 147/47.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FireSpread : MonoBehaviour
{
    private float startTime;
    float pollTime; // Seconds between fire spreading
    private Vector3 spaceToSpread = new Vector3(0.0f, 0.0f, 0.0f);
    static int[,]fireGrid = new int[100, 200];
    static int[,]burnGrid = new int[100, 50];

    // Use this for initialization
    void Start ()
    {
        startTime = Time.time;
        fireGrid[(int) transform.position.x + 50, (int) transform.position.z + 100] = 1;

        int[,]burnGrid =
        {MASSIVE ARRAY};
        pollTime = burnGrid[Math.Abs((int) transform.position.z), Math.Abs((int) transform.position.x)];
    }
  
    // Update is called once per frame
    void Update ()
    {
        if(Time.time > startTime + pollTime)
        {
            startTime = Time.time;
            CloneFire();
        }
    }

    void CloneFire ()
    {
        for (int i1 = -1; i1 <= 1; i1++)
        {
            for (int j1 = -1; j1 <= 1; j1++)
            {
                spaceToSpread.x = i1;
                spaceToSpread.z = j1;
                Vector3 newFirePos = (transform.position + spaceToSpread);
                int x1 = (int) newFirePos.x + 50;
                int z1 = (int) newFirePos.z + 100;
                if (fireGrid[x1, z1] == 0)
                {
                    var newFire = GameObject.Instantiate (gameObject, newFirePos, Quaternion.identity);
                    fireGrid[x1, z1] = 1;
                    Destroy (newFire, 10.0f);
                }          
            }

        }
    }
}

Taking care of the IndexOutOfRangeException didn’t make my game any faster, but I’ll live with that. My problem now if that if I want the player to die when he comes in contact with the fire, I have to either figure out how to detect when the player is colliding with a clone or do a check for the player in my FireSpread script to see if he is in a spot where fireGrid = 1 (which I think is easier.) How can I detect my player’s transform value in my script?