Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Tweaking a prefab before or immediately after instantiation during runtime

Discussion in 'Scripting' started by Juxtaposed, May 16, 2020.

  1. Juxtaposed

    Juxtaposed

    Joined:
    Aug 23, 2015
    Posts:
    29
    <SOLVED> see my own solution 3 replies down

    So I tried changing the rotation of children within a prefab immediately after instantiating the prefab. This does not work. As far as I can tell the only transform and rotation manipulation that is possible is through passing parameters into the instantiation call which only allows manipulating the parent object in the prefab.

    I have found a work around but is has undesirable side effects. The solution is to delay the manipulation for about 0.02 seconds (undoubtedly this value is hardware dependent).

    As example:

    Consider a scene that only has an empty GameObject with a testPrefabModAfterInstantiation script component, shown below and a button that executes the Test() function. The prefab variable references a prefab that is made up of an empty gameobject with a SimpleDoor script component and a child 3D object (something where rotation can be seen (I understand that in this example there is no reason for an empty gameobject as the parent but this is a simplified example, I have much more complex prefabs that I want to manipulate multiple children of). The value of the targetState variable in the editor can be used to determine what which rotation will be applied.

    The version of testPrefabModAfterInstantiation shown directly below does not work because it changes the rotation immediately after instantiating the prefab. Further below I have a variation that does work in an undesirable fashion.

    Code (CSharp):
    1. public class testPrefabModAfterInstantiation : MonoBehaviour
    2. {
    3.     [SerializeField] int targetState;
    4.     [SerializeField] GameObject prefab;
    5.     SimpleDoor instantiatedObject;
    6.  
    7.     public void Test()
    8.     {
    9.         instantiatedObject = prefab.GetComponent<SimpleDoor>();
    10.         instantiatedObject.PrefabStart();
    11.  
    12.         //GameObject tmp = Instantiate(prefab);
    13.         //instantiatedObject = tmp.GetComponent<SimpleDoor>();
    14.         if (instantiatedObject != null)
    15.         {
    16.             int beforeState = instantiatedObject.GetState();
    17.             instantiatedObject.SetState(targetState);
    18.             Debug.Log("before: " + beforeState + " after: " + instantiatedObject.GetState());
    19.         }
    20.         Instantiate(prefab);
    21.     }
    22. }

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class SimpleDoor : MonoBehaviour
    6. {
    7.     public Vector3 to;
    8.     public Vector3 from;
    9.  
    10.     private int state;
    11.     private Quaternion openRotation;
    12.     private Quaternion closedRotation;
    13.  
    14.     private void Start()
    15.     {
    16.         openRotation = Quaternion.Euler(to);
    17.         closedRotation = Quaternion.Euler(from);
    18.     }
    19.  
    20.     public int GetState()
    21.     {
    22.         return state;
    23.     }
    24.     public void SetState(int newState)
    25.     {
    26.         state = newState;
    27.         if (state == 1)
    28.         {
    29.             transform.rotation = closedRotation;
    30.         }
    31.         else
    32.         {
    33.             transform.rotation = openRotation;
    34.         }
    35.     }
    36. }
    In this version of testPrefabModAfterInstantiation I wait for 0.2 seconds before applying the rotation to the instantiated game object. This works but of course is undesirable because for 0.2 seconds it is in the wrong rotation. And I'm sure duration of the delay required is hardware dependent.

    Code (CSharp):
    1. public class testPrefabModAfterInstantiation: MonoBehaviour
    2. {
    3.     [SerializeField] int targetState;
    4.     [SerializeField] GameObject prefab;
    5.     SimpleDoor instantiatedObject;
    6.  
    7.     public void Test ()
    8.     {
    9.         instantiatedObject = prefab.GetComponent<SimpleDoor>();
    10.         instantiatedObject.PrefabStart();
    11.  
    12.         //GameObject tmp = Instantiate(prefab);
    13.         //instantiatedObject = tmp.GetComponent<SimpleDoor>();
    14.         if(instantiatedObject != null)      
    15.         {
    16.             scheduleLoad = true;
    17.         }
    18.         Instantiate(prefab);
    19.     }
    20.     bool scheduleLoad = false;
    21.     float timer = 0;
    22.     private void Update()
    23.     {
    24.         if (scheduleLoad)
    25.         {
    26.             timer += Time.deltaTime;
    27.             {
    28.                 if (timer > .02) //0.02 reliably works but 0.01689 sometimes works below that it doesn't seem to work
    29.                     //and I obviously see the object show up then move because I'm waiting 0.02 seconds to reposition it.
    30.                 {
    31.                     scheduleLoad = false;
    32.                     int beforeState = instantiatedObject.GetState();
    33.                     instantiatedObject.SetState(targetState);
    34.                     Debug.Log("before: " + beforeState + " after: " + instantiatedObject.GetState());
    35.                     timer = 0;
    36.                 }
    37.             }
    38.         }
    39.     }
    40. }

    So all that being said. Is it possible to change a prefab before or immediately after instantiating it without causing other problems?

    Also is there any method to be notified when an object is fully instantiated. It may come down to my having to simply wait for it to be instantiated before manipulating it.
     
    Last edited: May 16, 2020
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    What makes you think this? Did you try logging the "open" and "closed" rotations to see what they are? They might not be set correctly. I do this all the time in my projects without an issue.
     
    Last edited: May 16, 2020
  3. Juxtaposed

    Juxtaposed

    Joined:
    Aug 23, 2015
    Posts:
    29
    No I didn't log the rotations, but I rotated a visible object by the rotation. I know the open and closed rotations are set correctly because the object is rotated correctly when I change the code delay applying the rotation by changing the code in testPrefabModAfterInstantiation from the first version's lines 16-18 to the second version's line 16 and including the update code of the second version.
     
  4. Juxtaposed

    Juxtaposed

    Joined:
    Aug 23, 2015
    Posts:
    29
    After trying convoluted methods for hours to get this solved (and not liking any of the paths I was attempting because they were convoluted) I have found the solution and I feel ridiculous because it is pretty simple.

    The solution is for the code that instantiates the prefab to set the target transform in some variable in a script component somewhere in the instantiated prefab object and at the same time to set a bool to flag that this was done. The instantiated object should also have in its start method an if statement checking that flag. This way during the start method's execution (before the object is visible) it can handle its own positioning and rotating of its children based on the values set by the instantiating function.

    I probably should have just stepped away for a bit and come back to it. A person is usually more clear minded then than they are banging their head against convoluted code for hours.