Change Variables between Prefabs

Hay all!
Newish Unity user, first time poster
:stuck_out_tongue:

Anyway, as my topic says, Im trying to pass variables between prefabs.
Bare with me while i try to explain.
I have a Main code(gameobject) that is the start of the program, It spawns prefabs called hero and enemy.
When hero hits enemy, I want to subtract from its health until it eventually does.
example code im using on a (test bed).

public class GameStart : MonoBehaviour
{
    public GameObject heroPrefab;
    public GameObject enemyPrefab;

    private void Start()
    {
        GameObject Enemy = Instantiate(enemyPrefab, new Vector2(0, 4f), Quaternion.identity);
        GameObject Hero = Instantiate(heroPrefab, new Vector2(4, 4f), Quaternion.identity);
    }
}
public class Player : MonoBehaviour
{
    public Enemy enemy;
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            enemy.currenthealth -= 1;
        }
    }
}
public class Enemy : MonoBehaviour
{
    public int currenthealth = 100;
    private void Update()
    {
        Debug.Log("Enemy = " +currenthealth);
    }
}

Both Hero and Enemy are inside prefabs.
When I run the program with just game objects in the hierarchy every thing works fine. But as soon as I turn them into prefabs, it stops working.
Ive tried many different solutions, like give them tags etc…

public GameObject Enemy;
Enemy = GameObject.FindGameObjectWithTag("Enemy");

But everything Ive tried will not work bewteen prefabs.

I can remove either gameobject by using similar code.

void OnCollisionEnter2D(Collision2D col)
{
    if (col.gameObject.name == "Enemy(Clone)")
    {
        Destroy(this.gameObject);
    }
}

Any help on this would be greatly appreciated.

Thanks in advance
:slight_smile:

6 Answers

6

To post code, you can use the “preformatted text” button (</>) in the editor. This will create a region like this:

```
type or paste code here
```
And then you can just paste your code between the ticks to have it all formatted as code. If your code is already in the post, I think you can highlight the code and hit the preformatted text button to have the same effect.

As to your question, I’m not sure if I understand your hierarchy. The Start() function in GameStart is creating an instance of the heroPrefab and the enemyPrefab, but nothing else here is going to reference those instances of the prefab. Are those the instances of the objects that you want to modify?

Thanks dstears, I see now.

As for the first bit of code, thats my main function that runs first. Its designed to just to set things in motion and control other aspects of the program.

The rest of the code is simple and controls hero and enemy, Im using simple code here as an example, I didnt want to Post my whole project.
But this is enough to see if its working or not.

As I mentioned above, when I use objects in the heirarchy, I can access the variables and change them when desired. But as soon as I turn them into prefabs and run. It makes them clones, I know how to manipulate the clones, except for accessing the variables they use from another prefab.

Imagine two clones(star war reference). one has money in his pocket. I want another clone to pick his pocket and take some money. So the original clone has a reduced amount.
:stuck_out_tongue:

I hope that make things clearer.

EDIT
Ok I think I foun d the problem, it is working but I can only see the health while running, in the inspecter when I click on the prefab. But it never updates the clone, unless I reinstantiate a new clone…

Any ideas would be awesome.
thanks in advance.

How does the Enemy reference get set in the Player class (public Enemy enemy)?

If you create an instance of the prefab in the GameStart class, that will create the “Player(Clone)” version in the hierarchy. This instance won’t have anything set in its version of public Enemy enemy. You could do something like this in your GameStart class:

    private void Start()
    {
        GameObject Enemy = Instantiate(enemyPrefab, new Vector2(0, 4f), Quaternion.identity);
        GameObject Hero = Instantiate(heroPrefab, new Vector2(4, 4f), Quaternion.identity);

        //Assign the new Player's enemy reference to the new Enemy created
        Hero.GetComponent<Player>().enemy = Enemy.GetComponent<Enemy>();
    }

I would recommend not using variable names that are exactly equal to your class names. I’m not sure if it will cause runtime issues, but it could be ambiguous if “Enemy” refers to the class or to the object created by GameStart.

Correct and correct.
Variable names are not set to class names, learnt that mistake already.
the referrence is to the enemy class as your question describes.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public Enemy enemy;

    private void Start()
    {
        
    }
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            enemy.currenthealth -= 1;
        }
    }
}

I Instantiate a new clone with new currenthealth. or else it just grabs the value from the prefab.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


public class GameStart : MonoBehaviour
{
    public GameObject heroPrefab;
    public GameObject enemyPrefab;
    private float timer;
    public Text enemyhealthdisplay;

    private void Start()
    {
        GameObject Enemy = Instantiate(enemyPrefab, new Vector2(0, 4f), Quaternion.identity);
        Enemy.GetComponent<Enemy>().currenthealth = Random.Range(0,80) + 20;
        timer = Time.time;
        GameObject Hero = Instantiate(heroPrefab, new Vector2(4, 4f), Quaternion.identity);
        int health = Enemy.GetComponent<Enemy>().currenthealth;
        enemyhealthdisplay.text = health.ToString();
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(1))
        { 
            if (Time.time - timer > 0.5f)
            {
                Vector2 Mousepos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
                //GameObject Enemy = Instantiate(enemyPrefab, new Vector2(0, 4f), Quaternion.identity);
                GameObject Enemy = Instantiate(enemyPrefab, Mousepos, Quaternion.identity);
                Enemy.GetComponent<Enemy>().currenthealth = 100;
                timer = Time.time;
            }       

        }
    }

}

Imagine a tower defence game. Player in the center and enemies spawn random locations around a tower. The Enemies have their own health. your weapon doesnt destroy them, until they hit muliple times.
Now I know how to control the Variables the clones hold of their own value, But what im after is a way to access these variables from another clone.
Its probably not possible…
Im guessing using oncollision would do that.
Check what the bullet/weapon hit and change that clones value…
But what if I do area effect damage to multiple clones…
Again, I think Ive just figured out and answered my own question.

Using distance and collision routines would access these variable easily.

correct?

But it still leaves me with…
If I wanted to accss the clones values in my main routine that doesnt collide with anything, and wanted to display for example a list of enemy(clone) and change a specific clones value…
I hope that makes sense.

Thanks in advance.
:slight_smile:

It is absolutely possible to access public variables and functions of an object from a different object. The key point is that you need to get a reference to the object you want to modify. There are several ways to do this depending on your needs.

If your main routine is responsible for spawning the enemies, then it could keep a List of each enemy that is spawned so that it always has a reference to the many copies of the enemy prefab that has been created. You’ll need to make sure this list gets updated to remove enemies that get destroyed.

There are a few ways to handle weapons. If either the bullet or enemy has a rigidbody, the the Enemy class could definevoid OnCollisionEnter2D(Collision2D collision) to reduce its own health value when a collision with a bullet is detected. In this case, you don’t need to get a reference to anything else because the OnCollisionEnter2D function is running on the object that got hit.

If you are using Physics2D calls to detect hits, you can get the GameObject from the set of colliders returned by the overlap functions.
For example:

        Collider2D[] hits = Physics2D.OverlapCircleAll(point, radius, layerMask);
        foreach(Collider2D c in hits) 
        {
            c.gameObject.GetComponent<Enemy>().currenthealth -= 1;
        }

It is likely a good idea to add a public function to the Enemy class to update its health so that it can handle the case where the health drops to 0. If you modify the public variable directly, then the health will eventually go negative.

Sorry,
I didnt realise this was still here and not closed :P.
Ive since moved onto UE, I could not get my head around this design in unity.

thank you all for you comments and replies.

take care.

PS.
Closed.