Search Unity

Automatically assign a unique value to variable when script is added to gameobject in Editor?

Discussion in 'Scripting' started by Kaktusz, Jun 6, 2015.

  1. Kaktusz

    Kaktusz

    Joined:
    Jan 17, 2015
    Posts:
    9
    Hey! Sorry if this was asked before, but from I can't find any solution to this problem myself. Also, this is my first forum post so if I did something incorrectly then please tell me. And finally, I'm not sure if this should go on unity answers or forums, so tell me if it belongs over there :)

    Now, the problem:

    (Here is the backstory, just so people don't waste their time suggesting something that doesn't work. It's not essential so you don't have to read this part)
    I am making an RPG game with similar mechanics to Paper Mario; There's an overworld in which you walk around, and when you bump into an enemy then a turn-based battle starts. I have this part done. But I rely on every enemy having it's own ID. This is because every time I switch from the world scene to the battle scene and back to the world, the enemies are not considered the same objects. This means that after the level is loaded back the enemy should check if the battle was won by the player and either be removed or not. But a problem I had with this was that when the scene was reloaded the enemies would forget anything that happened before, including the battle starting at all. I also didn't want to use DontDestroyOnLoad as it is harder for me to manage the game with it. Finally, I came up with a solution. This is the ID I was talking about:

    (this is the part where I explain what I have been doing now)
    A non-monobehaviour class (EnemyManager) which has a lists called IDs. The Enemy script adds it's ID to the Manager, which has a variable (AttackingID) that is set whenever a battle starts. At the end of a battle, if the player wins, the game checks for which ID is equal to the attacking ID, and then removes it. Then, each enemy checks if it's ID is still in the list and if not, Destroys itself. This could probably be easier, but that's what I came up with.

    (This is the actual problem itself)
    Every enemy's ID is set to -1 by default. This is an "unassigned" state. This has to be changed manually for every enemy added, and the more enemies there are, the worse it gets. So what I really would like to know, is how to make the script assign a unique ID to itself when it's added to an enemy. The way I ideally would like to have it is that the first enemy created has an ID of 0, the next one 1, then 2, and so on. My takes on it would be to either have the enemy manager also hold the editor stuff, so maybe a list of all the IDs would be made in the editor, and whenever the Enemy script gets added, it would check the list of IDs and take the highest one there, and add 1 to it. This would be the enemy's ID then. In fact, there is probably no need for a list. Maybe a "highest ID number" would work.
    Anyway, I would like to know how to actually get something like this working. I have literally never even wrote a single word in a script in my entire life with the intent of it being run in the editor, so I'd really appreciate if someone showed me how to do it :)

    My (probably horrible) code:

    Enemy.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Enemy : MonoBehaviour {
    5.  
    6.     public GameObject battle;
    7.     public int ID = -1;
    8.     private bool isDead = false;
    9.     private bool canCollide = false;
    10.     // Use this for initialization
    11.  
    12.     void Start () {
    13.         if (EnemyManager.FirstStart && ID != -1) {
    14.             EnemyManager.IDs.Add(ID);
    15.         }
    16.  
    17.         if (ID == -1) {
    18.             Debug.LogWarning(name + "'s ID is -1 (default ID). Please change it", gameObject);
    19.         }
    20.  
    21.         if (!EnemyManager.FirstStart && ID != -1) {
    22.             if (!EnemyManager.IDs.Contains (ID)) {
    23.                 Destroy (gameObject);
    24.             } else
    25.             {
    26.                 isDead = false;
    27.             }
    28.         }
    29.  
    30.         StartCoroutine (WaitForCollide ());
    31.     }
    32.  
    33.     // Update is called once per frame
    34.     void Update () {
    35.  
    36.     }
    37.  
    38.     IEnumerator WaitForCollide() {
    39.         yield return new WaitForFixedUpdate ();
    40.         canCollide = true;
    41.     }
    42.  
    43.     void OnCollisionEnter (Collision col) {
    44.         if (col.transform.tag == "Player" && !isDead && canCollide) {
    45.             Debug.Log (isDead);
    46.             isDead = true;
    47.             if(EnemyManager.FirstStart)
    48.                 EnemyManager.FirstStart = false;
    49.             GameInfo.battlescene = battle;
    50.             GameInfo.OnWorldUnload();
    51.             EnemyManager.AttackingID = ID;
    52.             Application.LoadLevel(1);
    53.         }
    54.     }
    55. }
    56.  
    EnemyManager.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. public class EnemyManager {
    5.  
    6.     public static bool FirstStart = true;
    7.     public static List<int> IDs = new List<int> ();
    8.     public static int AttackingID = -1;
    9.  
    10.     public static void KillAttackingEnemy() {
    11.         if (AttackingID >= 0) {
    12.             for(int i = 0; i < IDs.Count; i++)
    13.             {
    14.                 if(IDs[i] == AttackingID)
    15.                     IDs.RemoveAt(i);
    16.             }
    17.             AttackingID = -1;
    18.         }
    19.     }
    20.  
    21. }
    22.  
    Note that some of the stuff is done in another script, but it's irrelevant to the question.

    Thanks to anyone who attempts to help me :)

    (Note: Sorry if I don't respond, I will be unable to access the internet for some time and I'm just posting this in case I get some quick responses. Don't expect a reply within 10-12 hours and over 16 hours after posting this. If I don't reply then I'll be able to only in a few days. Extremely sorry if someone leaves a quick reply!)
     
    Last edited: Jun 6, 2015
  2. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    Maybe this can work if all you need is something to work with the editor.
    http://docs.unity3d.com/ScriptReference/MonoBehaviour.Reset.html
    That is a method similar to Update or Start, except its only called in the editor. I never used it before, but according to the docs it says it will be ran when the component is reset OR when added for the first time. So you can place your Unique I'd assigner method within this method.
     
    yusuf_isik and Kiwasi like this.
  3. Kaktusz

    Kaktusz

    Joined:
    Jan 17, 2015
    Posts:
    9
    Great idea! I'll try it out if I have time to, and I'll tell you if it works. Thanks :D
     
  4. Kaktusz

    Kaktusz

    Joined:
    Jan 17, 2015
    Posts:
    9
    That's a good idea! The only possible problem I might have would be debugging, but even then it shouldn't be too hard. How did I not think of this?
     
  5. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    Maybe instead just store the position of the enemy before battle mode, and when you finish battle mode, get the enemy at the position you saved and do what you need with him. This way you can handle dynamicly created enemies without worrying about saving ids. How you would get the enemy is by doing a small Physics.OverlapSphere on the location you saved, get the enemy collider and delete or keep him.

    The slower way depending on how many enemies (but possible more accurate) would be to loop through all enemies and pick the one that matches the position.

    Long story short. All of their transform data / game object name can be their unique id.
     
    Last edited: Jun 6, 2015
    Kaktusz likes this.
  6. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    I deleted that post before you commented since I think I was wrong. The Id set would just be remade to another random id when the scene reloaded since you aren't storing the data anywhere.
    You probably did think of this and thats when you made this thread since your whole issue was not being able to easily save enemy data.
    See my post above for another solution.
     
    Last edited: Jun 6, 2015
  7. Kaktusz

    Kaktusz

    Joined:
    Jan 17, 2015
    Posts:
    9
    I actually did store the data in the enemy manager, but I ran into other problems. So I tried your latest idea, but the problem with that is that there will only be one enemy dead at one time. Except if you meant that I don't do it at the player's position, but at every position of an enemy I fought and won with. If so, then I'll try that :)

    EDIT:
    Great! It works better than ever :D Thanks so much!

    My code (for anyone who has the same problem):

    Enemy.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Enemy : MonoBehaviour {
    5.  
    6.     public GameObject battle;
    7.     private bool canCollide = false;
    8.     // Use this for initialization
    9.  
    10.     void Start () {
    11.    
    12.         StartCoroutine (WaitForCollide ());
    13.     }
    14.    
    15.     // Update is called once per frame
    16.     void Update () {
    17.    
    18.     }
    19.  
    20.     IEnumerator WaitForCollide() {
    21.         yield return new WaitForFixedUpdate ();
    22.         canCollide = true;
    23.     }
    24.  
    25.     void OnCollisionEnter (Collision col) {
    26.         if (col.transform.tag == "Player" && canCollide) {
    27.             EnemyManager.AttackingEnemyPosList.Add(transform.position);
    28.             GameInfo.battlescene = battle;
    29.             GameInfo.OnWorldUnload();
    30.             Application.LoadLevel(1);
    31.         }
    32.     }
    33. }
    34.  
    EnemyManager.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. public class EnemyManager {
    5.  
    6.     public static List<Vector3> AttackingEnemyPosList = new List<Vector3>();
    7.  
    8.     public static void KillEnemies(float range) {
    9.         foreach (Vector3 pos in AttackingEnemyPosList) {
    10.             Collider[] cols = Physics.OverlapSphere (pos, range);
    11.             foreach (Collider col in cols) {
    12.                 if (col.gameObject.GetComponent<Enemy> ()) {
    13.                     GameObject.Destroy (col.gameObject);
    14.                 }
    15.             }
    16.         }
    17.     }
    18.  
    19. }
    20.  
    Player.cs
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Player : MonoBehaviour {
    5.  
    6.     void Start () {
    7.         if (!GameInfo.playerInfo) {
    8.             GameInfo.playerInfo = this;
    9.             GameInfo.playerPos = transform.position;
    10.         }
    11.     }
    12.  
    13.     void OnLevelWasLoaded ()
    14.     {
    15.         if (!GameInfo.playerLost) {
    16.             transform.position = GameInfo.playerPos;
    17.             EnemyManager.KillEnemies (1f);
    18.         }
    19.     }
    20. }
    21.  
     
    Last edited: Jun 6, 2015
  8. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    987
    Out of curiosity (as I never played with data across scenes yet), simply storing the enemy gameobjects in a static list won't work when going back and forth from scenes?
     
  9. Kaktusz

    Kaktusz

    Joined:
    Jan 17, 2015
    Posts:
    9
    This is very unintuitive, but makes sense deeper down. Unity stores every gameobject instance internally and assigns an ID to it. Whenever the game is loaded up, every gameobject gets it's unique ID. The thing is, whenever a scene is reloaded, the gameobjects are not the same ones. They might look the same, have the same positions, behave the same, and pretty much be the exact same enemies as they were when they were first loaded up. But in Unity's eyes, they are different. It kind of makes sense because when you destroy an enemy and reload the scene, the enemy will still be alive. It's clearly not the same enemy then, even though it looks like the same enemy that you killed was just reset. Hope you understand :)
     
    HiddenMonk likes this.