Skip to main content
4 of 6
added 935 characters in body
Philipp
  • 121.5k
  • 28
  • 261
  • 342

The problem is that Unity allows you to put your Counter script on as many gameObjects as you want, and each one would have an own ScoreAmount. So Counter.ScoreAmount is ambiguous. Unity doesn't know which one you mean.

So how could you fix that?

Solution A: Make ScoreAmount static.

When you declare a variable as static, then all instances of that script share the same value. That allows you to refer to that variable by the class name instead of any particular instance. So when you change public int ScoreAmount; to public static int ScoreAmount;, then you should get the behavior you want.

But while this might solve the problem for now, it might cause some problems later. For example when you want for some reason create a second object with an own count, like a 2nd player or an AI actor which can also collect things and count them.

Solution B: Make the ItemPickup get the component on the same object.

However, looking at your code, it seems like the ItemPickup component is on the gameObject which actually does the picking (and not on the item which can be picked up, which is why I would have done it, but I digress...). It is not clear from your question where the Counter component is housed, but if it is on the same gameObject, then you can use GetComponent<Counter>() to get a reference to it.

void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("Fruit")) 
    {
        Debug.Log("Picking up fruit...");
        GetComponent<Counter>().ScoreAmount += 1;
    }  
}

Solution C: Give the FruitPickup script a reference to the Count script.

This solution allows multiple Count script, but does not requires that they are not on the gameObject which does the collecting. So you can put it on a manager object or a UI element.

Give the FruitPickup script a variable public Count count and assign the object with the Count script using the inspector.

Solution D: Put the ItemPickup script on the items and have it detect which object collected it.

This is how I would do this, but it requires you to approach this a bit differently.

  1. Put the Counter object on the player
  2. Make the collider of the player not a trigger
  3. Put the FruitPickup component on object which can be picked up
  4. Make the colliders of the fruits triggers
  5. Write the FruitPickup class like this:
    void OnTriggerEnter2D(Collider2D other)
    {
        Counter counter = other.GetComponent<Counter>()
        if (counter != null) // the object is able to count fruit
        {
            counter.ScoreAmount += 1; // increase score of the object which collected this
            Destroy(gameObject); // self-destruct
        }  
    }

But now the Counter loses its connection to the Text element it used to control. The reason is that it used the method B to get a reference to its own Text component, which now won't work anymore. How do we fix that? By using the solution C and tell it which text object to use to visualize its state. Make the ScoreText variable public and remove its initialization in Start:

public class Counter : MonoBehaviour
{
    public int ScoreAmount;
    public Text scoreText;
    void Start()
    {
        ScoreAmount = 0;
    }

    void Update()
    {
        if (scoreText != null) { 
             ScoreText.text = scoreAmount.ToString();
        }
    }
}

Then go to the Inspector and drag the Text object which you want to visualize the score on the "Score Text" inspector field of the Counter component.

Although personally I recommend to separate mechanics from UI visualization, so I would probably use a 3rd script for UI elements for visualizing the state of an arbitrary Counter on its own Text. But that's a different topic.

Why do I recommend option D?

Why do it that way?

First, an object should contain the components which dictate its own behavior, not that of other things. Increasing the player's score and then disappearing is something the fruits do, not something the player-character does. So this behavior should be defined in a component of the Fruit gameObjects. Also, this allows you to do things like making the amount by which a fruit increases the player's score a public variable of that script. So you can create prefabs for different fruits which increase the player's score by different amounts.

Second, I would avoid putting a trigger on the player. Triggers should be on the objects in the world which other objects interact with. Not on the objects which do the interacting.

Third, detecting what the other object is in a collision by seeing what components it has allows you to create very modular and reusable code. The fruit doesn't care if whatever picks it up is a "Player". It only cares that it has the components which require it to be able to collect it. So you can have multiple players which collect fruit and even NPC characters which collect fruit, and you don't need to change anything about the fruits to do that.

Philipp
  • 121.5k
  • 28
  • 261
  • 342