Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Assign a property for each instance of a prefab

Discussion in 'Scripting' started by Jessy, Aug 27, 2007.

  1. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I assume what I want to do should be easy, but I'm not quite sure how to go about it. What I want to do is instantiate a prefab in a certain pattern, and assign a different value to a property of that object each time it is instantiated. From what I'm seeing in my Javascript books, it seems to me that I might need to define a function or a class, creating "dummy" properties to start off with, and then fill in useful values as my prefab is instantiated. However, I'm not sure how to make the instantiated prefab itself inherit these values.

    Think about it like this, perhaps: I will instantiate a prefab five times, and want each of these to be "numbered" somehow, so that I can only do something to them if they "are" the right number.

    Also, I tried using a for/in statement for an Array, and it didn't work properly. However, changing this to be a standard "for" loop allowed correct behavior. Still, I get an error, even though things look alright on screen. Here are the important bits. Any ideas?

    This does not work (all instantiated prefabs are created in the same place):
    Code (csharp):
    1. var dot : GameObject;
    2. var Elements : Vector3[];
    3.  
    4. function Start () {
    5.     for (var element in Elements){
    6.         placement = (Elements[element]);
    7.       Instantiate(dot, placement, Quaternion.identity);
    8.      }
    9. }
    This works. The error I get is "IndexOutOfRangeException: Array index is out of range.":
    Code (csharp):
    1. var dot : GameObject;
    2. var Elements : Vector3[];
    3.  
    4. function Start () {
    5.     for (n=0; n<=Elements.length; n++){
    6.         placement = (Elements[n]);
    7.         Instantiate(dot, placement, Quaternion.identity);
    8.     }
    9. }
    Thanks for any help you can give!
     
  2. skyhawk

    skyhawk

    Joined:
    Jun 13, 2005
    Posts:
    49
    use a static variable.

    extending what I said: have a static int and increment it every Start.
    Then have a normal int that is assigned to the current value of the static variable.
     
  3. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    This is something I was thinking about, but I don't know how to "attach" that "normal" integer to each instance of the prefab. I've tried creating a variable to hold such a value, but it hasn't worked so far. I did something like:

    Code (csharp):
    1. for (i=0, i<=5, i++){
    2.    varName = Instantiate(blah, yadda, rabble);
    3.    varName.valueiWantToAssign = (i+1);
    4. }
     
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    There are several different ways, actually. One way is just to give each object a different name:

    Code (csharp):
    1. function Start() {
    2.     for (i = 0; i < 5; i++) {
    3.       var myObject = Instantiate(something);
    4.       myObject.name = i.ToString();
    5.     }
    6. }
    I've done this fairly often...if you just need one unique variable per object, and you don't need the name for anything else, it seems like the simplest method. Probably not the fastest, though, since you're dealing with strings.

    Code (csharp):
    1. var dot : GameObject;
    2. var Elements : Vector3[];
    3.  
    4. function Start () {
    5.     for (var element in Elements){
    6.         placement = (Elements[element]);
    7.       Instantiate(dot, placement, Quaternion.identity);
    8.      }
    9. }
    The problem with this is that "element" is not a integer; it's a Vector3 since "Elements" is an array of Vector3s. With a foreach loop, you don't have an index counter. Every iteration through the loop, the next item in the array "Elements" is assigned to "element".

    Use this instead:

    Code (csharp):
    1.     for (var element in Elements){
    2.       Instantiate(dot, element, Quaternion.identity);
    3.      }
    And this:

    Code (csharp):
    1. var dot : GameObject;
    2. var Elements : Vector3[];
    3.  
    4. function Start () {
    5.     for (n=0; n<=Elements.length; n++){
    6.         placement = (Elements[n]);
    7.         Instantiate(dot, placement, Quaternion.identity);
    8.     }
    9. }
    The problem with this is that "n" is exceeding the number of items in the array, hence the error you're getting. Since numbering starts at 0, you want to change it to "n < Elements.length".

    --Eric
     
  5. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Excellent, Eric. Thanks a bunch. I'm having some success now.

    However, I'm having some difficulty actually using the named instances of the prefab in conditional statements. How would I change the following so that it becomes useful? Is there some sort of "un-String-ify" function, like the ToString() Eric brought to my attention?

    Code (csharp):
    1. if (name > someVariable) {iLikeSockPuppets;}
     
  6. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Code (csharp):
    1. if (parseInt(name) > someVariable)
    There's also parseFloat. Careful with that one...if you have a string like "378.482", it might interpret the result differently depending on the user's regional settings.

    --Eric
     
  7. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I'm going to try that ASAP. Thanks in advance.

    Do you have any suggestions for where I can go for a list of this stuff? I LOVE the Script Classes Index, but I have yet to find a similarly nice non-Unity-specific Javascript reference.
     
  8. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    http://www.go-mono.com/docs/

    Pay attention to warnings about versions of .NET at the top of documentation pages. The current version of Unity doesn't support .NET 2.0 classes.
     
  9. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,268
    The way I do this is by moving my Start code into a function with parameters that I can set, like:

    MySpawnerClass:

    Code (csharp):
    1. function MyEvent() {
    2.     var myObject = Instantiate(myObjectPrefab).GetComponent(MySpawnedClass);
    3.     myObject.Initialize( someVarToSet );
    4. }

    MySpawnedClass:

    Code (csharp):
    1. function Initialize( someVarToSet : someType ) {
    2.     myClassVar = someVarToSet;
    3.  
    4.     // Other starting code here
    5. }
     
  10. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I need to "practice" a bit before I can utilize the massive power of that, but thank you now for the fun I will have later!

    As for now, this new problem is REALLY bugging me. I have instanced my prefabs, and named them procedurally. To start off with, only ONE item should be blue. This code work fine inside of Unity, but upon building, the second to last prefab that is instanced becomes blue for no good reason! This is highly simplified code to what I really want to do, but I think it's the most concise I can make it in order to display the problem.

    Create a prefab object, any shape. Put this script on it.

    Code (csharp):
    1. var thing = 1;
    2.  
    3. function Update() {
    4.     if (parseInt(name) == thing) {
    5.         renderer.material.color = Color.blue;
    6.     }
    7.     if (parseInt(name) > thing) {
    8.         renderer.material.color = Color.green;
    9.     }
    10. }
    Then, create an empty, or whatever, and attach this script on that in order to get these prefabs placed in the scene:

    Code (csharp):
    1. var thingy : GameObject;
    2. var thingies : Vector3[];
    3.  
    4. private var placement : Vector3;
    5.  
    6. function Start () {
    7.     for (count=0; count<thingies.length; count++) {
    8.         placement = thingies[count];
    9.         var thingo = Instantiate(thingy, placement, Quaternion.identity);
    10.         thingo.name = (count+1).ToString();
    11.     }
    12. }
    If this works out for you like it does for me, you will have 1 Blue object in Unity, and 2 when you build the game. I don't want that second one!
     
  11. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Hmm, nope, works as expected here. Try this on the script for the prefabs:

    Code (csharp):
    1. function Start() {
    2.     Debug.Log("thing: " + thing + "  name: " + name);
    3. }
    Then build the game and run it, and then look at the console logfile...should give you some clue as to what's happening.

    --Eric
     
  12. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I don't know exactly what I should be looking for, but I get this, along with a lot of other stuff that means little to me at this point:

    Code (csharp):
    1. thing: 1  name: 1
    2. thing: 1  name: 2
    3. thing: 1  name: 3
    I don't have time for insane bugs right now!
     
  13. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Hmmm...unfortunately I can't see why that would fail. Well, you can try another method of assigning a variable to the objects; I'd suggest the method Marble outlined. If you don't want to deal with classes right now, you can set up a variable in the objects' scripts and assign it from the other script:

    Code (csharp):
    1.    for (count=0; count<thingies.length; count++) {    
    2.       placement = thingies[count];
    3.       var thingo = Instantiate(thingy, placement, Quaternion.identity);
    4.       var thingoScript : thingoScriptName = thingo.GetComponent(thingoScriptName);
    5.       thingoScript.thingVar = count+1;
    6.    }
    7.  
    And then the objects' script, which I'm referring to here as thingoScriptName (obviously you'd use the actual name of the script there), would have:

    Code (csharp):
    1. var thing = 1;
    2. var thingVar : int;
    3.  
    4. function Update() {
    5.    if (thingVar == thing) {
    6.       //etc.
    7.  
    This way thingVar gets assigned a unique value when the object is instantiated. Note that thingVar can't be a private variable if you want to access it from other scripts like that.

    --Eric
     
  14. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Thanks again, Eric. This method is new enough to me that I am getting something out of it, but it's not so crazy that it's going over my head, given my current experience.

    However, I get the same buggy, unusable results. I am going to try creating a new project and using these scripts in it. If it worked for you, it might work for me. I've had weird things happen with Unity that became fixed upon building, but this is the first time it's been the other way around. Not good.

    Edit: And then...

    It worked for me. Should I report this to OTEE?
     
    Rujash likes this.
  15. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I have been working on this project for a while now without that weirdness reappearing. I am now at a point where I have the two scripts working together flawlessly, but I want to trigger new scripts only at certain points.

    I believe that "function Start() {}" will come in handy, but I don't know how to make a script be "asleep" until I want it to do whatever is in "function Start() {}". I tried wrapping up some code into a function, like so, hoping to be able to Invoke said function, but that didn't work.

    Code (csharp):
    1. function doWheniSaySo() {
    2.    function Start() {//do stuff}
    3.    function Update() {//do even better stuff}
    4. }
    Edit: I was just thinking, maybe I should use "Instantiate" to place an object, with the script I want to call attached to it, into a scene. It seems a bit weird to make a prefab that will only be instanced one time, but that's all I've got right now. :idea: