I’ve been struggling with this for some hours now, and I’m not sure if it’s me not understanding prefabs correctly or if there’s something else amiss here.
I have an event-driven system that the UI depends on, and it’s working perfectly in all areas with prefabs and links between prefabs except when I want to change the alpha of a canvas group.
It’s a turn based combat scene, the (simplified) setup is this:
The FadeToBlackOnCombatEnd has a very simple script attached that looks like this:
I click play, I invoke the either of the events that ends combat (PlayerWon / EnemyWon) and nothing happens. When I exit play mode, the CombatEndCanvas reads the updated prefab value and appears. So basically the script is modifying the CombatEndCanvas-prefab and it’s not reflected at runtime. I understand that this might be how prefabs work, that changes at runtime doesn’t reflect instantiated objects as they are plain serialized objects without a references to the prefab, right?
HOWEVER. I also have a Listener that on the same events sets the “GameOverText” to either Victory or Defeat depending on which event was invoked. This works flawlessly. The setup is exactly the same in both scenarios, prefabs are linked to prefabs. Setting the text is reflected at runtime and the alpha of the canvas is not. This is the prefab that sets the text:
What is going on here? Why isn’t setting alpha reflected at runtime but text is?
I’m basically trying (and currently) modifying the prefab and have it’s change reflected to it’s instances in runtime. It’s like this:
Create a FadeOnCombatEnd.prefab, which needs a CanvasGroup to apply the fade on.
Create a CombatEndCanvas.prefab with a canvas group. Link this prefab to the FadeOnCombatEnd in the project view.
Drag Both prefabs to the scene to instantiate them manually, links are still working and the FadeOnCombatEnd-instance is referencing the CombatEndCanvas.prefab when I click it (it’s highlighting the prefab in the project view).
Run the game, Invoke FadeOnCombatEnd and only the prefab is updated, not the instance I’ve created.
The above works flawlessly with setting a text. Same deal:
Create a prefab with a script to update text on it
Link another prefab with a text to it.
Instantiate them both by dropping them in the scene view
Run the game, invoke the UpdateText-script and text is updated BOTH on prefab and scene-instance.
TL;DR: I’m modifying the prefab directly in runtime, not the instantiated object. I’m getting different results depending on which component I’m modifying.
What I’d like to have is prefabs linking to other prefabs, and when an invoker changes a linked prefab I’d like it to update both the prefab and it’s instances. This would allow me to create a new scene and just drop all the prefabs in there to have all the base-functionality set up without having to go through and re-reference everything.
I also dread the day I’ve created 15 combat scenes and decide to add a reference for a prefab to another prefab and have to go through all previous scenes to set the reference to the instantiated object (which is what I have to do now with just 2 scenes).
Maybe I’m attacking this the wrong way? Maybe I should keep my base functionality for combat in one scene and have combat-specific functionality (environment, goals, spawnpoints…) in a prefab or scene that I can dynamically instantiate or load additively?
Once you drag something into the scene, it’s no longer a prefab but an instance of said prefab. If you update any of the fields in the prefab (and not the scene instance), those changes will be reflected in all scene instances if and only if you’ve not changed those values for a particular instance. You can push changes from a scene instance back to the prefab by clicking “Apply” in the top right corner of the inspector.
This system makes sense. If you have a bunch of UI prefabs and tweak their transition times, all of the instances update, but it doesn’t then go ahead and destroy all of your OnClick listeners that you hand crafted for every specific instance by resetting them back to the prefab value.
An easy way to tell if a prefab instance is getting its value from the prefab or not is the boldness of the text in the inspector. Bold, dark black text indicates the value is different than the prefab’s default.
As for your specific issue, I’m not really sure if you’re confusing prefabs and instances. If you have a link to a prefab, let’s say:
public class ChangePrefab : MonoBehaviour
{
public Transform prefab;
public void Awake() { prefab.position = Random.onUnitSphere; }
}
And link an object from the project view, your changes will be reflected in the original prefab. If you however link “prefab” to any object in the scene view, your changes will not be reflected in the original prefab (because it’s an instance).
Normally you would not modify a prefab for runtime changes. While I think I understand what you’re trying to do, I think you might run into more headaches trying to do it that way.
Normally you’ll want to modify your instances and apply data to them. For example, you might have a prefab that contains an image and text and it is used in a list to display enemy stats. You might have different pictures and strings used for the text. If you apply that to the prefab, it would change them all.
Generally you want to apply to instances you create or that already exist in your scene and you can have a central point of data that you pull from instead.
Yeah I’m starting to realise why I shouldn’t change prefabs, they should be more like blueprints and a starting point.
I guess my question then is: how do I handle references from a prefab to a scene object without having to manually link them in every scene I use the prefab. Im mostly interested in this for the maintenance issues that might arise, as described above.
Maybe I’ll change it up to being data-driven with Scriptable Objects as I’m currently doing for a lot of the other logic.
If you have any other advice or references I’d gladly look at it. Otherwise thanks guys!
If you don’t want to maintain a list of references to these instances, for changes you don’t need to do often you could have them all with their own unique tag you could search for, or you could have something else identifiable like a specific component.
Yeah that’s a good solution too! Will definitely keep that in mind for future reference =)
I did solve my problem by realising that I should not modify prefabs at runtime but instead modify instances. And the current issue I had with maintenance I solved by separating my scene in two parts and loading them additively: one part controllers/UI and one part environment. Now It’s one standard setup for all combat scenes I might build, hence no need to worry about future changes since they will only have to be done in one scene.
Thank you all. I definitely learned something from this!