Tristan Alexander
Tristan Alexander

Reputation: 25

Unity Destroy() doesn't delete script

Noob mistake I was only referencing the parents brain from the child's instead of copying it directly. It's a neural network that can grow in size (or shrink) through genetics. I was going to post it but it doesn't fit on this without me having to type a larger ratio of words to code oh look it works now

public class Animal1 : MonoBehaviour
{
    public class Neuron1
    {
        public int[] children;
        public float[] weights;
        public float bias;
        public float value;

        public Neuron1()
        {
            children = new int[10];
            for (int i = 0; i < 10; i++)
            {
                children[i] = -1;
            }
            weights = new float[10];
            for (int i = 0; i < 10; i++)
            {
                weights[i] = 1;
            }
            bias = 0;
            value = 0;
        }
    }

    float sightRange = 2;
    Vector2 left = new Vector2(-.4f,.6f);
    Vector2 right = new Vector2(.4f, .6f);
    RaycastHit2D hitLeft;
    RaycastHit2D hitForward;
    RaycastHit2D hitRight;
    public LayerMask animalLayerMask;

    [System.NonSerialized]
    public int food = 0;
    int foodCounter = 0;
    int foodForReproduction = 3;
    float noFoodCounter = 0;
    int lifeWithoutFood = 20;

    int maxBrainSize = 100;
    [System.NonSerialized]
    public Neuron1[] brain1 = new Neuron1[100]; //first three and last three are inputs and outputs and are never mutated
    int childrenMaxAmount = 5;

    float biasInitRange = 2;
    float weightInitRange = 2;
    int outputSize = 2;
    int inputSize = 3;

    GameObject manager;
    [System.NonSerialized]
    public bool firstGen = false;

    [System.NonSerialized]
    public GameObject parent1;
    [System.NonSerialized]
    public bool directCopy = false;

    float maxTravelRange = 5;

    Color color;
    float colorMutationRate = .1f;

    public GameObject animal1Prefab1;


    bool debugBool = false;

    void Start()
    {
        manager = GameObject.Find("Manager Object");

        if (firstGen == true)
        {
            InitializeBrain(3, 2, 20);
            firstGen = false;
            transform.GetComponent<SpriteRenderer>().color = new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f), 1);
        }
        else
        {
            if (parent1.GetComponent<Animal1>().brain1[0].bias == 1234)
            {
                Debug.Log("parent passing brain after deletion");
            }

            Mutate(parent1, 5, 1f, .5f);
            transform.GetComponent<SpriteRenderer>().color = color;
        }
    }

    void Update()
    {

        if (brain1[0].bias == 1234)
        {
            GetComponent<SpriteRenderer>().color = Color.black;
        }

        //teleportation
        if (transform.position.x > maxTravelRange) 
        {
            Vector2 newPosition = new Vector2(-maxTravelRange, transform.position.y);
            transform.position = newPosition;
        }
        if (transform.position.x < -maxTravelRange) 
        {
            Vector2 newPosition = new Vector2(maxTravelRange, transform.position.y);
            transform.position = newPosition;
        }
        if (transform.position.y > maxTravelRange) 
        {
            Vector2 newPosition = new Vector2(transform.position.x, -maxTravelRange);
            transform.position = newPosition;
        }
        if (transform.position.y < -maxTravelRange) 
        {
            Vector2 newPosition = new Vector2(transform.position.x, maxTravelRange);
            transform.position = newPosition;
        }

        //input
        hitLeft = Physics2D.Raycast(transform.position, transform.TransformDirection(left), sightRange, ~animalLayerMask); //left
        hitForward = Physics2D.Raycast(transform.position, transform.TransformDirection(Vector2.up), sightRange, ~animalLayerMask); //forward
        hitRight = Physics2D.Raycast(transform.position, transform.TransformDirection(right), sightRange, ~animalLayerMask); //right
        if (hitLeft)
        {
                brain1[0].value = 1;
        }
        if (hitForward)
        {
                brain1[1].value = 1;
        }
        if (hitRight)
        {
                brain1[2].value = 1;
        }

        //output
        Think();
        // one output for speed, another for turning
        int rotDir = 1;
        if (brain1[maxBrainSize - 1].value < 0)
        {
            rotDir = -1;
        }
        Vector3 eulerRotation = new Vector3(0, 0, (45 * rotDir + (transform.rotation.eulerAngles.z % 360)) % 360);
        transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.Euler(eulerRotation), Mathf.Abs(brain1[maxBrainSize - 1].value) * 25 * Time.deltaTime);
        if (brain1[maxBrainSize - 2].value > 0) // so they can't go backwards
        {
            transform.Translate(Vector2.up * (brain1[maxBrainSize - 2].value * .1f) * Time.deltaTime);
        }
        //reset outputs
        brain1[maxBrainSize - 2].value = 0;
        brain1[maxBrainSize - 1].value = 0;
        // reset inputs
        brain1[0].value = 0;
        brain1[1].value = 0;
        brain1[2].value = 0;

        noFoodCounter += Time.deltaTime;
        if (food > 0)
        {
            foodCounter++;
            food = 0;
            noFoodCounter = 0;
        }
        if (foodCounter > foodForReproduction)
        {
            foodCounter = 0;
            Reproduce();
        }
        if (noFoodCounter > lifeWithoutFood/2)
        {
            foodCounter = 0;
        }

        if (noFoodCounter > lifeWithoutFood) 
        {
            Destroy(gameObject);
        }
    }

    void InitializeBrain(int inputs, int outputs, int sizeExcludingInputsAndOutputs)
    {
        for (int i = 0; i < sizeExcludingInputsAndOutputs + inputs; i++)
        {
            brain1[i] = new Neuron1();
        }
        for (int i = maxBrainSize - outputs; i < maxBrainSize; i++) // outputs
        {
            brain1[i] = new Neuron1();
        }

        // all neurons in between the inputs and outputs
        for (int i = inputs; i < sizeExcludingInputsAndOutputs + inputs; i++)
        {
            brain1[i].bias = Random.Range(-biasInitRange, biasInitRange);
            for (short j = 0; j < childrenMaxAmount -1; j++) // -1 so there is always at least one free space for a child
            {
                int rnd1 = Random.Range(inputs, sizeExcludingInputsAndOutputs + inputs);
                bool alreadyContains = true;
                int errorCounter = 0;

                while (alreadyContains) // making sure none are children of themselves, and that it doesn't already have this child
                {
                    alreadyContains = false;
                    for (short h = 0; h < childrenMaxAmount; h++)
                    {
                        if (brain1[i].children[h] == rnd1) // if it already has this child
                        {
                            alreadyContains = true;
                            rnd1 = Random.Range(inputs, sizeExcludingInputsAndOutputs + inputs);
                            while (rnd1 == i) // and its not itself
                            {
                                rnd1 = Random.Range(inputs, sizeExcludingInputsAndOutputs + inputs);
                            }
                            break;
                        }
                    }
                    errorCounter++;
                    if (errorCounter > 100) // so we don't get stuck in small network sizes
                    {
                        goto outside2;
                    }
                }
                brain1[i].children[j] = rnd1;
                brain1[i].weights[j] = Random.Range(-weightInitRange, weightInitRange);
                if (Random.Range(1, 10) > 8)
                {
                    break; // so not every neuron has the same amount of children
                }
            }
        outside2:
            {
            }
        }
        // assign children of inputs
        for (int i = 0; i < inputs; i++)
        {
            for (int j = 0; j < childrenMaxAmount; j++) // assigning all max children (sometimes) to inputs, their amounts of children will never change
            {
                int rnd1 = Random.Range(inputs, sizeExcludingInputsAndOutputs + inputs);
                bool alreadyContains = true;
                int errorCounter = 0;
                while (alreadyContains)
                {
                    alreadyContains = false;
                    for (short h = 0; h < childrenMaxAmount; h++)
                    {
                        if (brain1[i].children[h] == rnd1)
                        {
                            alreadyContains = true;
                            rnd1 = Random.Range(inputs, sizeExcludingInputsAndOutputs + inputs);
                        }
                    }
                    errorCounter++;
                    if (errorCounter > 1000) // so we don't get stuck in small network sizes
                    {
                        goto outside1;
                    }
                }
                brain1[i].children[j] = rnd1;
                //brain1[i].weights[j] = 100; // input weight
            }
        outside1:
            {
            }
        }

        RemoveDeadNeurons();

        // assign parents of outputs
        int brainEnd = maxBrainSize - outputs;
        for (int i = brainEnd; i < maxBrainSize; i++)
        {
            for (short j = 0; j < childrenMaxAmount; j++) // same amount of parents as inputs have children
            {
                int rnd1 = Random.Range(inputs, brainEnd);
                while (brain1[rnd1] == null)
                {
                    rnd1 = Random.Range(inputs, brainEnd);
                }
                bool alreadyContains = true;
                int errorCounter = 0;
                while (alreadyContains) //making sure we dont already have this parent
                {
                    alreadyContains = false;
                    for (short h = 0; h < childrenMaxAmount; h++)
                    {
                        if (brain1[rnd1].children[h] == i)
                        {
                            alreadyContains = true;
                            rnd1 = Random.Range(inputs, brainEnd);
                            while (brain1[rnd1] == null)
                            {
                                rnd1 = Random.Range(inputs, brainEnd);
                            }
                        }
                    }
                    errorCounter++;
                    if (errorCounter > 1000) // so we don't get stuck 
                    {
                        goto outside3;
                    }
                }
                for (int h = 0; h < childrenMaxAmount; h++) //adding to next available child space of the parent
                {
                    if (brain1[rnd1].children[h] == -1)
                    {
                        brain1[rnd1].children[h] = i;
                        brain1[rnd1].weights[h] = Random.Range(-weightInitRange, weightInitRange);
                        break;
                    }
                }
            }
        outside3:
            {
            }
        }
    }

    void Think()
    {
        int[] activatedChildrenIndexes = new int[500];
        int[] workingArray = new int[500];
        int workingArrayCounter = 0;

        short layerCounter = 0;
        for (int i = 0; i < 500; i++)
        {
            activatedChildrenIndexes[i] = -1;
        }
        //initializing inputs as first activated children layer to be calculated
        activatedChildrenIndexes[0] = 0;
        activatedChildrenIndexes[1] = 1;
        activatedChildrenIndexes[2] = 2;

        int thoughtDepth = 6; //keep this as low as possible, will change to make it a percent of the amount of neurons that already exist
        for (int x = 0; x < thoughtDepth; x++)
        {
            for (int i = 0; i < 500; i++) // cycle through activated children and add their activated children to a list
            {
                int currentNodeIndex = activatedChildrenIndexes[i];
                if (currentNodeIndex == -1)
                {
                    layerCounter++;
                    break;
                }
                //activation function
                if (brain1[currentNodeIndex].value <= 0)
                {
                    brain1[currentNodeIndex].value = 0;
                }
                else
                {
                    brain1[currentNodeIndex].value = 1;
                }

                for (int j = 0; j < childrenMaxAmount; j++)
                {
                    if (brain1[currentNodeIndex].children[j] == -1)
                    {
                        break;
                    }

                    brain1[brain1[currentNodeIndex].children[j]].value
                        += (brain1[currentNodeIndex].value * brain1[currentNodeIndex].weights[j]) + brain1[currentNodeIndex].bias;

                    bool activatedChildIsInNextLayer = false;
                    for (int h = 0; h < 500; h++)
                    {
                        if (brain1[currentNodeIndex].children[j] == activatedChildrenIndexes[h])
                        {
                            activatedChildIsInNextLayer = true;
                        }
                    }
                    if (brain1[brain1[currentNodeIndex].children[j]].value > 0 
                        || layerCounter == 0 
                        || brain1[currentNodeIndex].children[j] > maxBrainSize - outputSize)
                    {
                        if (!activatedChildIsInNextLayer)
                        {
                            workingArray[workingArrayCounter] = brain1[currentNodeIndex].children[j];
                            if (workingArrayCounter < 499)//to prevent out of bounds indexing
                            {
                                workingArrayCounter++;
                            }
                        }
                    }
                    if (brain1[brain1[currentNodeIndex].children[j]].value < 0 && brain1[currentNodeIndex].children[j] < maxBrainSize - outputSize)
                    {
                        brain1[brain1[currentNodeIndex].children[j]].value = 0; //resetting negative nodes that didn't get added to next layer
                    }
                }
                brain1[currentNodeIndex].value = 0; // resetting value after passing to children
            }
            for (int i = workingArrayCounter; i < 500; i++)
            {
                workingArray[i] = -1;
            }
            workingArrayCounter = 0;
            for (int i = 0; i < 500; i++)
            {
                activatedChildrenIndexes[i] = workingArray[i];
            }
        }
    }

    void Mutate(GameObject parent, float mutationRate, float weightsMutate, float biasMutate)
    {
        color = parent.GetComponent<SpriteRenderer>().color;
        for (int i = 0; i < maxBrainSize; i++) // initialized to its parent's brain
        {
            if (parent.GetComponent<Animal1>().brain1[i] != null)
            {
                brain1[i] = new Neuron1();
                brain1[i].bias = parent.GetComponent<Animal1>().brain1[i].bias;
                for (int j = 0; j < childrenMaxAmount; j++)
                {
                    brain1[i].children[j] = parent.GetComponent<Animal1>().brain1[i].children[j];
                    brain1[i].weights[j] = parent.GetComponent<Animal1>().brain1[i].weights[j];
                }
            }
        }

        for (int i = inputSize; i < maxBrainSize - outputSize; i++)
        {
            if (brain1[i] != null)
            {
                if (Random.Range(0, 100) < mutationRate) // it will be mutated
                {
                    switch (Random.Range(1, 5))
                    {
                        case 1: //change one of its weights
                            int weightCounter = 0;
                            for (int k = 0; k < childrenMaxAmount; k++) // counting its weights
                            {
                                if (brain1[i].children[k] != -1)
                                {
                                    weightCounter++;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            brain1[i].weights[Random.Range(0, weightCounter)] += Random.Range(-weightsMutate, weightsMutate);
                            color.r += Random.Range(-colorMutationRate, colorMutationRate);
                            color.g += Random.Range(-colorMutationRate, colorMutationRate);
                            color.b += Random.Range(-colorMutationRate, colorMutationRate);
                            break;


                        case 2: //change its bias
                            brain1[i].bias += Random.Range(-biasMutate, biasMutate);
                            color.r += Random.Range(-colorMutationRate, colorMutationRate);
                            color.g += Random.Range(-colorMutationRate, colorMutationRate);
                            color.b += Random.Range(-colorMutationRate, colorMutationRate);
                            break;

                            
                        case 3:  //break a connection/ remove neuron
                            int childrenCounter = 0;
                            for (int k = 0; k < childrenMaxAmount; k++) // counting its children
                            {
                                if (brain1[i].children[k] != -1)
                                {
                                    childrenCounter++;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            int rndChild = Random.Range(0, childrenCounter);
                            for (int s = rndChild; s < childrenMaxAmount - 1; s++) // shift children list down starting from rndChild and to the second to last element
                            {
                                brain1[i].children[s] = brain1[i].children[s + 1];
                                brain1[i].weights[s] = brain1[i].weights[s + 1];
                            }
                            brain1[i].children[childrenMaxAmount - 1] = -1; // removing the last child after shifting the list down one
                            brain1[i].weights[childrenMaxAmount - 1] = 0;
                            RemoveDeadNeurons();
                            color.r += Random.Range(-colorMutationRate, colorMutationRate);
                            color.g += Random.Range(-colorMutationRate, colorMutationRate);
                            color.b += Random.Range(-colorMutationRate, colorMutationRate);
                            break;
                            
                            
                        case 4: //add a new child if possible
                            int childrenCounter2 = 0;
                            for (int k = 0; k < childrenMaxAmount; k++) // counting its children
                            {
                                if (brain1[i].children[k] != -1)
                                {
                                    childrenCounter2++;
                                }
                                else
                                {
                                    break;
                                }
                            }
                            if (childrenCounter2 < childrenMaxAmount) // if we have room for another child
                            {
                                int newChild = Random.Range(inputSize, maxBrainSize);
                                while (brain1[newChild] == null)
                                {
                                    newChild = Random.Range(inputSize, maxBrainSize);
                                }
                                brain1[i].children[childrenCounter2] = newChild;
                                brain1[i].weights[childrenCounter2] = Random.Range(-weightInitRange, weightInitRange);
                                color.r += Random.Range(-colorMutationRate, colorMutationRate);
                                color.g += Random.Range(-colorMutationRate, colorMutationRate);
                                color.b += Random.Range(-colorMutationRate, colorMutationRate);
                            }
                            break;
                            
                    }
                }
            }
        }

        
        if (Random.Range(0, 100) < 10) // create a new neuron if space is available for one
        {
            for (int i = inputSize; i < maxBrainSize - outputSize; i++)
            {
                if (brain1[i] == null) // the first available neuron
                {
                    brain1[i] = new Neuron1();
                    int rndParent = Random.Range(0, maxBrainSize - outputSize);
                    int childrenCounter3 = 0;
                    if (brain1[rndParent] != null)
                    {
                        for (int k = 0; k < childrenMaxAmount; k++) // counting its children
                        {
                            if (brain1[rndParent].children[k] != -1)
                            {
                                childrenCounter3++;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                    while (brain1[rndParent] == null || childrenCounter3 == childrenMaxAmount) // making sure it is both an existing neuron and that it has room for another child
                    {
                        childrenCounter3 = 0;
                        rndParent = Random.Range(0, maxBrainSize - outputSize);
                        if (brain1[rndParent] != null)
                        {
                            for (int k = 0; k < childrenMaxAmount; k++) // counting its children
                            {
                                if (brain1[rndParent].children[k] != -1)
                                {
                                    childrenCounter3++;
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                    }
                    brain1[rndParent].children[childrenCounter3] = i; //giving the neuron a parent
                    brain1[rndParent].weights[childrenCounter3] = Random.Range(-weightInitRange, weightInitRange);

                    int rndChild = Random.Range(inputSize, maxBrainSize);
                    while(brain1[rndChild] == null)
                    {
                        rndChild = Random.Range(inputSize, maxBrainSize);
                    }

                    brain1[i].children[0] = rndChild; // giving the new neuron a child, a weight, and its bias
                    brain1[i].weights[0] = Random.Range(-weightInitRange, weightInitRange);
                    brain1[i].bias = Random.Range(-biasInitRange, biasInitRange);
                    color.r += Random.Range(-colorMutationRate, colorMutationRate);
                    color.g += Random.Range(-colorMutationRate, colorMutationRate);
                    color.b += Random.Range(-colorMutationRate, colorMutationRate);
                    break;
                }
            }
        }
        
    }

    void RemoveDeadNeurons()
    {
        bool allGood = false;
        while (!allGood)
        {
            for (int i = inputSize; i < maxBrainSize - outputSize; i++) // for every hidden layer neuron
            {
                if (brain1[i] != null) // if it's a neuron
                {
                    if (brain1[i].children[0] == -1) // if it has no children
                    {
                        brain1[i] = null; // deleting neuron
                        for (int h = 0; h < maxBrainSize - outputSize; h++) //remove from other neurons children list, including from the input layer
                        {
                            if (brain1[h] != null)
                            {
                                for (int g = 0; g < childrenMaxAmount; g++)
                                {
                                    if (brain1[h].children[g] == i) // if brain[h] has the removed neuron as a child
                                    {
                                        for (int s = g; s < childrenMaxAmount - 1; s++) // shift children list down
                                        {
                                            brain1[h].children[s] = brain1[h].children[s + 1];
                                            brain1[h].weights[s] = brain1[h].weights[s + 1];
                                        }
                                        brain1[h].children[childrenMaxAmount - 1] = -1; // removing the last child in the list after shifting it down one
                                        brain1[h].weights[childrenMaxAmount - 1] = 0;
                                    }
                                }
                            }
                        }
                        goto outsideRDNLoop; // break out of for loop and recheck the brain
                    }
                    bool parentless = true;
                    for (int h = 0; h < maxBrainSize - outputSize; h++) // check if it is parentless
                    {
                        if (brain1[h] != null)
                        {
                            for (int g = 0; g < childrenMaxAmount; g++)
                            {
                                if (brain1[h].children[g] == i) // if it is a child of brain[h]
                                {
                                    parentless = false;
                                }
                            }
                        }
                    }
                    if (parentless)
                    {
                        brain1[i] = null; //deleting neuron
                        goto outsideRDNLoop;
                    }
                }
                if (i == (maxBrainSize - outputSize) - 1) // if we reached the end of the for loop and this line, we know the brain was allGood
                {
                    allGood = true;
                }
            }
        outsideRDNLoop:
            {
            }
        }
    }

    void Reproduce()
    {
        GameObject offspring = Instantiate(animal1Prefab1, new Vector2(transform.position.x + 1, transform.position.y + 1), Quaternion.identity);
        offspring.GetComponent<Animal1>().parent1 = gameObject;
        offspring.name = "animal1";
    }

    void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawRay(transform.position, transform.TransformDirection(left) * sightRange);
        Gizmos.DrawRay(transform.position, transform.TransformDirection(Vector2.up) * sightRange);
        Gizmos.DrawRay(transform.position, transform.TransformDirection(right) * sightRange);
    }
}

Upvotes: 2

Views: 381

Answers (3)

Russopotomus
Russopotomus

Reputation: 705

I've not run your code, just looked at it, but this looks suspicious if you are having problems with the contents of brain1 being overwritten elsewhere:

void Mutate(GameObject parent, float mutationRate, float weightsMutate, float biasMutate)
{
    color = parent.GetComponent<SpriteRenderer>().color;
    // This line here is suspicious:
    brain1 = parent.GetComponent<Animal1>().brain1; // initialized to its parent's brain

What you are doing here is replacing the reference to the child's brain with the parent's - so if you are starting with just one parent initially, every single entity will be sharing the same brain.

I think perhaps you meant to deep copy the contents from parent's brain into the child's brain? What your assignment here is doing is just taking the reference to the parent's brain and using it as the child brain as well.

Example Deep copy:

Updated Neuron1 class:

    public class Neuron1
    {
        public int[] children;
        public float[] weights;
        public float bias;
        public float value;

        public Neuron1()
        {
            children = new int[10];
            for (int i = 0; i < 10; i++)
            {
                children[i] = -1; // to stop looping when reaching -1
            }
            weights = new float[10];
            for (int i = 0; i < 10; i++)
            {
                weights[i] = 1;
            }
            bias = 0;
            value = 0;
        }

        // Added clone function:
        public Neuron1 Clone()
        {
            Neuron1 clone = new Neuron1();
            for (int i = 0; i < clone.children.Length; i++)
            {
                clone.children[i] = this.children[i];
            }
            for (int i = 0; i < clone.weights.Length; i++)
            {
                clone.weights[i] = this.weights[i];
            }
            clone.bias = this.bias;
            clone.value = this.value;

            return clone;
        }
    }

Updated start of Mutate function.

    void Mutate(GameObject parent, float mutationRate, float weightsMutate, float biasMutate)
    {
        color = parent.GetComponent<SpriteRenderer>().color;
        
        Neuron1[] parentBrain = parent.GetComponent<Animal1>().brain1; // initialized to its parent's brain
        for (int i = 0; i < brain1.Length; ++i)
        {
            brain1[i] = parentBrain[i].Clone();
        }

Upvotes: 1

HamTheMan
HamTheMan

Reputation: 49

DestoryImmediate() is not recommended to be used, instead use Destory()

Destroy() will set the object to null at the end of the frame and whereas DestroyImmediate() function immediately set object reference to null.

DestoryImmediate can also delete prefabs/scenes/art in your project outside of playmode which is another reason you should not use it.

Upvotes: 1

abo
abo

Reputation: 146

Well, I have a demo scene here. In it I created an empty game object. It has a script attached to it (CSharp.cs), like this:

Game Object

In my Update method I wrote

Update method

And as soon as I start the game, the game object including the script is deleted. But if I replace transform.gameObject wit just this then only the script disappears and the game object remains.

Upvotes: 1

Related Questions