Reputation: 922
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
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
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