Prefab Doesn't Store Public Fields?
I have created AI characters. As each character is hit by a raycast from the player, I show their name and healthbar on a canvas. As the player looks away, the canvas clears. Seems simple, and it works, as long as all of the objects are in scene, and I manually place the UI elements into the public field slots.
I want to start with an empty level and spawn prefab AI characters in. When I do, their public fields for UI elements are empty. What can I do to ensure that these fields hold the entries for the canvas and UI elements?
EDIT: Attaching the Enemy script from updated comment.
<Deleted for brevity>
void Start ()
{
// HealthBarCanvas = GetComponent<EnemyHealthBars> ();
healthBarSlider = GetComponent<EnemyHealthBars> ().healthBarSlider;
healthBarText = GetComponent<EnemyHealthBars> ().healthBarText;
HealthBarCanvas.SetActive (false);
}
// Update is called once per frame
void Update ()
{
Ray ray = PlayerCamera.ViewportPointToRay (new Vector3(0.5f,0.5f,0));
if (Physics.Raycast (ray, out hit, lookDistance))
{
if (hit.collider.gameObject.tag == AITag && updateTimer > 0.5f)
{
currentlyLookingAtTarget = true;
currentTarget = hit.collider.gameObject;
currentAISystem = currentTarget.GetComponent<AI> ();
updateTimer = 0;
}
if (hit.collider.gameObject.tag == AITag)
{
updateTimer += Time.deltaTime;
lookAwayTimer = 0;
}
if (hit.collider.gameObject.tag != AITag)
{
lookAwayTimer += Time.deltaTime;
if (lookAwayTimer > lookAwaySeconds)
{
currentlyLookingAtTarget = false;
currentTarget = null;
}
}
}
if (deathTimerActive)
{
deathTimer += Time.deltaTime;
if (deathTimer > 1)
{
HealthBarCanvas.SetActive (false);
currentlyLookingAtTarget = false;
deathTimerActive = false;
currentTarget = null;
}
}
if (currentlyLookingAtTarget && !deathTimerActive)
{
HealthBarCanvas.SetActive(true);//HealthBarCanvas.SetActive(true);
healthBarSlider.value = currentAISystem.currentHealth;
if (currentAISystem.currentHealth <= 0)
{
deathTimerActive = true;
}
}
if (!currentlyLookingAtTarget)
{
HealthBarCanvas.SetActive (false);
}
}
}
Answer by HenryStrattonFW · Feb 12, 2017 at 12:29 AM
This is because prefabs exist outside of the scene, meaning they cannot store references to items that are in the scene as it has no knowledge of the scene. This is also why you will notice that if you set references to items that are not within the prefab itself, those values remain in bold when you apply changes, because they cannot be saved with the prefab.
To get around this, you will have to do one of two things, one is to have prefabs locate those items and store the references after they are instantiated, you could do this based on tags, names, component searches. OR what I see as the better method, is to have a script that manages your UI (for arguments sake lets call it UiController), this can be placed on the object in the scene, and hold references to the GUI elemnents, then instead of your enemy objects directly effecting the UI display, they simply find and make method calls to this UiController (again via a find by name, find by type, etc). and the UiController responds by updating the necessary UI elements.
POSTED AS ANSWER AS CO$$anonymous$$$$anonymous$$ENT DIDN'T WOR$$anonymous$$!!!! @HenryStrattonFW I know that a prefab can't retain a link to the in-game elements. Can you point me to an instructional video to setup a UIcontroller?
All I want to do is have this script, placed on each AI object, enable a worldspace UI overtop of the AI-unit that is hit by the raycast, show its text (name) and display a slider for hitpoints. The Canvas is a child of the Enemy game object. All UI components are children of the Canvas. Each Enemy AI object has its own canvas. I have public fields for Canvas, Slider, and Text, and have dragged and dropped the elements into their respective fields.
When I place multiple AI's into the scene, the raycast turns on ALL canvases(undesired), and they ALL appear to share the same updates! If I hit one, all of the sliders move. The expected behavior is raycast hits AI. Single AI's Canvas gets set active. AI takes a hit, only its single slider moves. $$anonymous$$ove targeting reticule (ray) off the AI, its Canvas is no longer active.
I am down to my last few hairs. This seemed a lot simpler in concept! At this point, I would be happy if I could just have a single, screenspace Canvas that does the same thing with prefab AI-characters (no public fields that require drag and drop UI elements).
It seems comments have a size limit, I've moved the script into your original post, and converted your post to a comment.
First I apologies based on your second post it seems I misunderstood part of your issue. I thought you were talking about a single screenspace UI that each AI emeny shared, not a UI canvas within each enemy itself.
However that being the case, I'm unsure what issue you are having as I have no issues in a test project adding worldspace canvas + assets to a prefab, linking them via public fields in the inspector, and having those references persist when the prefab is later instantiated. Here is a link to screenshot (uploader wouldn't work) showing my template prefab (placed in scene for convenience only) its inspector with the UI elements connected, and a view of it in the scene (just a sphere with a text label). $$anonymous$$y test code instantiates X number of these setting their name based on their spawn order, each prefab then uses its references to set the label to its name and it all seems to work fine.
Can you compare this setup to the one you are using, as from what I can see there should be no reason why these public references should not be serialized with your prefab. THe code you posted uses GetComponent to try and find them, however get component only works on the object the script is attached to, in this scenario I assume the UI elements are children within the prefab, in which case you would want to use GetComponentInChildren ins$$anonymous$$d.
I have it now instantiating the prefab, and making the canvas a child of the AI-object.
Added: HealthBarCanvas = Instantiate(HealthBarCanvasPrefab, new Vector3 (transform.position.x,transform.position.y +2.51f,transform.position.z), Quaternion.identity); HealthBarCanvas.transform.SetParent (transform);
I also have it filling in the variables using GetComponentInChildren.
However, for some reason, it still wants to turn on ALL of the AI's canvases, and use the name and health stats of the target AI.
Am I doing something wrong with the RayCast? $$anonymous$$ark
Your answer
Follow this Question
Related Questions
Prefabs and public variables 1 Answer
For some reason I cannot drag my prefabs from the Project folder into the scene hierarchy. 2 Answers
Limit an amount of prefabs instantiated in javascript for Unity 5. 0 Answers
my instantiation /prefab of dan wont play death animations like orignal dan 0 Answers