innomotion media
innomotion media

Reputation: 922

Objects in Unity show up in editor but don't render on Android when script attached

I am building a little platformer, where the player can collect coins. Since there are sometimes 100+ spinning coins in one scene, I decided to render only coins that are in within a certain proximity to the player.

This worked before - but on a phone the levels with many coins tend to have some lags. With the coins the removed, my FPS was at straight 60. So this is what I did:

public class AddCoinScript : MonoBehaviour
{
    public static int coinCounter = 0; // static cause we will only want ONE counter and not many instances of the coin value
    private Text coinText;
    private float disBetweenBirdyAndEnemy;

    private GameObject birdy;
    private Renderer rndr;
    private BoxCollider2D bx2D;
    private bool canAnimate = false;
    private float startAnimDistance = 20;
    private bool coinCollected = false;

    void Start()
    {
        coinText = GameObject.Find("Coin Text").GetComponent<Text>(); // Get the Coin Text Element
        coinText.text = "x" + coinCounter.ToString();

        birdy = GameObject.Find("Birdy"); 
        rndr = GetComponent<Renderer>();
        bx2D = GetComponent<BoxCollider2D>();

        rndr.enabled = true;
        bx2D.enabled = true;
    }


    void FixedUpdate()
    {
         disBetweenBirdyAndEnemy = Vector3.Distance(birdy.transform.position, this.transform.position);
         
         if (disBetweenBirdyAndEnemy <= startAnimDistance)
         {
            if(!coinCollected)
            {
                rndr.enabled = true;
                bx2D.enabled = true;
            }

         }
         else
         {
            rndr.enabled = false;
            bx2D.enabled = false;
         }

        coinText.text = "x" + coinCounter.ToString();
    }

    void OnTriggerEnter2D(Collider2D col)
    {
        if (col.gameObject.name == "Birdy") // detect collision with ground game object 
        {
            AddCoinAndRemoveHitBoxAndSprite();
        }


    }

    private void AddCoinAndRemoveHitBoxAndSprite()
    {
        coinCounter++;
        coinCollected = true;
        rndr.enabled = false;
        bx2D.enabled = false;
        PlayCoinSound();
    }

    private void PlayCoinSound()
    {
        AudioSource aS = GameObject.Find("GetCoinSound").GetComponent<AudioSource>();
        aS.Play();
    }
}

The results are good. The coins render in the scene only when the player is close enough to them. However, once deployed onto my phone - the coins don't render at all any more. As if the players distance towards the coins is different on my phone than it is on my pc.

What am I missing?

Upvotes: 0

Views: 225

Answers (1)

Richard Johnson
Richard Johnson

Reputation: 78

I can't see anything obviously wrong with this, but if it is a distance check gone wrong, you could try disabling the entire coin and enable them only if they're on the screen.

A solution which would work with all resolutions is something along the lines of this:

private bool IsOnScreen(Vector3 worldPosition)
{
    Vector3 screenPoint = camera.WorldToViewportPoint(worldPosition);

    return screenPoint.x > 0 
        && screenPoint.x < 1
        && screenPoint.y > 0
        && screenPoint.y < 1;
}

A separate problem that could be solved is that your coin script is responsible for far too many things. Right now every coin

  • Knows how many coins the player has and can modify that number
  • Knows about and is modifying the UI text for coins on start
  • Is responsible for enabling/disabling its own components, when you could just destroy the gameObject when it's collected
  • Finds an audio gameObject and playing a sound

Because of all these responsibilities you'll have a harder time solving issues within this class. Consider having some sort of CoinManager class which can keep track of how many coins the player has (instead of every single coin knowing about the coin count).

Your class might look something like this

public class CoinManager : MonoBehaviour
{
    private Camera camera;
    [SerializeField] private AudioClip coinCollectedSound;
    [SerializeField] private AudioSource audioSource;
    [SerializeField] private List<CoinScript> coins;
    [SerializeField] private int coinCount;
    [SerializeField] private Text coinLabel;

    // This could be called by some spawner class that
    // adds coins to the scene
    public void AddCoin(CoinScript coin)
    {
        coins.Add(coin);
    }


    public void CoinCollected(CoinScript coin)
    {
        Destroy(coin.gameObject);
        coinCount++;
        audioSource.PlayOneShot(coinCollectedSound);
        // Alternatively, if you have a dedicated audio source 
        // for coin sounds you can just use audioSource.Play();
    }

    private void Update()
    {
        // Iterate over all the coins we know about
        foreach (var coin in coins)
        {
            // Now that we have a method that tells us whether a
            // coin is on the screen or not, we can enable and disable
            // all the coins at the same time in one place
            coin.transform.enabled = IsOnScreen(coin.transform.position);
        }
    }

    private void Awake()
    {
        // Gets the main camera in the scene
        // https://docs.unity3d.com/ScriptReference/Camera-main.html
        camera = Camera.main;
        coins = new List<CoinScript>();
    }

    private bool IsOnScreen(Vector3 worldPosition)
    {
        Vector3 screenPoint = camera.WorldToViewportPoint(worldPosition);

        return screenPoint.x > 0
               && screenPoint.x < 1
               && screenPoint.y > 0
               && screenPoint.y < 1;
    }
}

I don't have Unity in front of me so the above is off the top of my head.

Upvotes: 2

Related Questions