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

Reset pooled object to prefab settings

Discussion in 'Scripting' started by serbusfish, Feb 18, 2018.

  1. serbusfish

    serbusfish

    Joined:
    Dec 27, 2016
    Posts:
    247
    I have set up a pooling solution for weapon projectiles, but i'm having problems pooling my homing shot. Firing the shot for the first time works as it should, but once it has been used, deactivated and reactivated for a second use it spawns with numerous problems, including moving too fast and continuing to track the target it was following before it was disabled.

    I am told its possible to reference the prefab version that the pooled object was spawned from so that any values that have been changed can be reset, is this correct, and if so how can I do this without instantiating a fresh projectile?
     
  2. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Depends on the settings, but if it were just speed & the target, you should be able to set those when you fire?
     
  3. serbusfish

    serbusfish

    Joined:
    Dec 27, 2016
    Posts:
    247
    I have a missile behaviours asset from the store, target tracking is done during flight, so they can change target multiple times whilst in the air. I have contacted the creator of the asset and he told me I should try looking into resetting the projectile settings to prefab settings.
     
  4. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Not sure what to tell you?

    Apply whatever those prefab settings are when you instantiate your game object was my suggestion.. :)
    Obviously, the ones that matter.
     
  5. DonLoquacious

    DonLoquacious

    Joined:
    Feb 24, 2013
    Posts:
    1,667
    If a value is changing from how the object was set up as a prefab, then it's likely either something that changes every time that object is used, in which case you don't need to worry about resetting it because it's going to be overwritten again anyways on the next use, or it changes by incrementing over time, in which case resetting should just mean "setting to zero", or the equivalent. If something is changing only sometimes, in a way that isn't easy to reset without knowing the prefab's initial values, that seems sort of odd to me- maybe you need more than 1 prefab, or maybe you should set up a slightly more involved process.

    One thing you might want to try is making a component whose sole job is to handle resetting values on other components. Have it copy some values to internal fields with an Init() function call, then calling Reset() there overwrites the current values with the old ones, for the whole object. It's quite possible to set this up using reflection and FieldInfo in such a way that you can define things dynamically per prefab, but that's a lot of time and effort, so more likely you'll need a new component type per prefab so that you can define which fields need to be copied and reset in the script. I don't really like this method as I prefer to minimize MonoBehaviour usage in my projects, but it might look something like:
    Code (Csharp):
    1. public class Poolable : MonoBehaviour
    2. {
    3.     private SomeComponent someComponent;
    4.     private float _fireRate = 1f;
    5.     private float _size = 1f;
    6.  
    7.     public void Init()
    8.     {
    9.         if(someComponent == null)
    10.             someComponent = GetComponent<SomeComponent>();
    11.  
    12.         _fireRate = someComponent.fireRate;
    13.         _size = someComponent.size;
    14.     }
    15.  
    16.     public void Reset()
    17.     {
    18.         if(someComponent == null)
    19.             return;
    20.  
    21.         someComponent.fireRate = _fireRate;
    22.         someComponent.size = _size;
    23.     }
    24. }
    A better solution might be to use ScriptableObjects instead- storing values for GameObject entities as states, then resetting to any given state as needed. A simple example may be something like:
    Code (CSharp):
    1. [CreateAssetMenu(menuName = "ProjectileStates")]
    2. public class ProjectileStates : ScriptableObject
    3. {
    4.     [System.Serializable]
    5.     private class ProjectileState
    6.     {
    7.         public string stateName = "default";
    8.         public float fireRate = 1f;
    9.         public float size = 1f;
    10.     }
    11.  
    12.     [SerializeField]
    13.     private ProjectileState[] _states;
    14.  
    15.  
    16.     public void SetObjectState(GameObject go, string stateName)
    17.     {
    18.         foreach(var state in _states)
    19.         {
    20.             if(state.stateName == stateName)
    21.             {
    22.                 SetObjectState(go, state);
    23.                 break;
    24.             }
    25.         }
    26.     }
    27.  
    28.     private void SetObjectState(GameObject go, ProjectileState state)
    29.     {
    30.         var someComponent = go.GetComponent<SomeComponent>();
    31.  
    32.         someComponent.fireRate = state.fireRate;
    33.         someComponent.size = state.size;
    34.     }
    35. }
    This kind of system will enable you to reset GO entities, especially poolable objects, to initial states pretty easily, and it'll also enable you to set up simple variations to prefabs without needing to make additional prefabs or include additional MonoBehaviours. You can make a reference to a ProjectileStates object in one of your scene managers and drag and drop this in, so that you can access it from there, or you can toss this asset into Resources and load it that way, or if you're absolutely sure you'll only ever need 1 instance of this asset, you can put it in Resources and have it self-load when accessed through static functions, essentially making it a singleton.

    So yeah, lots of ways to reset some values, but I honestly don't think you should be trying to access the prefab itself to do it- that feels unnecessarily messy to me, and I'm not sure there's any built-in way to detect what prefab was used to instantiate an object after it's been instantiated. Prefabs are only specially recognized in the editor- at runtime they're the same as any other GameObject, just as a standalone asset instead of residing in a specific scene. There's no internal field for a GameObject that says "this prefab is what was used to create me", so if you want to copy values from the prefab over to the instantiated object, you'll need to keep track of that connection yourself, and copy the values over yourself.
     
    Last edited: Feb 18, 2018