- Home /
Coin Magnet Performance Issue Mobile
Another question about performance on mobile (iOS):
I've managed to implement a "coin magnet" system where the player attract the coins dropped by dead enemies.
The basic mechanics are:
-Each coin is pulled towards the player if the distance between them both is less than X. (magnet range) -The amount of movement towards the player each frame is based on a constant "magnet power" and distance over magnet range. So the closer the coin gets, more "force" is applied to it.
The problem is when I got lots of coins in screen the game fps drops lower than 20. The two main reasons I think are doing a FindObjectcsWithTag every frame to get all coins and a getComponent to each coin, every frame.
The possible solituon I first thought was to let the coins handle the magnet behavior instead of the player. But my question is:
Will all coins have a player reference and all the coins have an update cycle each give the same performance drop symptom?
Thanks in advance.
The thing is that the coin movement depends on data that is stored on player (or at least should be by design as the magnet is a feature of the player). So if I'm not wrong at least at the creation of each coin a getComponent must be used to see the player's magnet values so the coin can move itself. Anyone?
Answer by aldonaletto · Dec 19, 2012 at 02:11 AM
FindGameObjectsWithTag each Update isn't a good idea, for sure. You could try OverlapSphere to find the objects inside the range, and keep the coins in some special layer (layer 8, for instance) to restrict the search (player script):
var magnetRange: float = 5;
var minDistance: float = 0.5;
function Update(){
var coins: Collider[];
var here = transform.position;
// find colliders inside range and in layer 8:
coins = Physics.OverlapSphere(here, magnetRange, 1<<8);
for (var coin: Collider in coins){
var tfCoin = coin.transform;
var dist = Vector3.Distance(tfCoin.position, here);
if (dist < minDistance){ // closer than minDistance: grab the coin
Destroy(tfCoin.gameObject);
// add the points to your score
}
else
if (dist <= magnetRange){ // inside range: attract it
var vel = magnetRange / dist; // velocity inversely proportional to distance
// move coin to the player
tfCoin.position = Vector3.MoveTowards(tfCoin.position, here, vel * Time.deltaTime);
}
}
}
Only the coins should be kept in layer 8 - other objects in this layer would be attracted too.
NOTE: Since OverlapSphere only verifies the collider bounds, some of them may actually be a little out if range - that's why the distance is verified inside the loop.
I implemented this suggestion of yours. It works, but my performance problems are still there. The thing is, when the coin is within range of the magnet, I call one GetComponent().getHookedBy$$anonymous$$agnet(); to warn it. This is needed because when the coin gets instantiated it has an initial movement to a random nearby position inside a sphere. So when multiple coins are instantiated it creates an explosion effect. But if the magnet gets too close to the coin BEFORE it reaches it final destination, the initial coin movement must be called off, thats the reason of the GetComponent call. Any ideas? $$anonymous$$aybe put the movement on the coin.
If you already have some movement script in each coin, maybe moving the attraction code to the coins could improve the performance. Anyway, post the relevant parts of your code in the question - it would be easier to find some way to improve its performance.
Answer by GeorgeRigato · Dec 21, 2012 at 10:45 AM
Here it goes:
//This is the script on player - the coin is only collected with an actual trigger collision.
//And I'm more used to C#, if that is not a problem.
int coinLayer = LayerMask.NameToLayer("coin");
Vector3 here = transform.position;
Collider [] tempCoins = Physics.OverlapSphere(here, magnetDistance, 1<<coinLayer);
foreach (Collider obj in tempCoins)
{
Vector3 coinPos = obj.transform.position;
float dist = Vector3.Distance(here, coinPos);
if (dist < magnetDistance)
{
obj.GetComponent<MoedaController>().getHookedByMagnet();
obj.transform.position = Vector3.MoveTowards(coinPos, here, Time.deltaTime * magnetForce * (magnetDistance / dist));
}
}
//An this is the whole CoinController class
using UnityEngine;
using System.Collections;
public class MoedaController : MonoBehaviour {
public int coinValue=1;
public float spinSpeed = 245;
public float movementSpeed = 15;
public float minDistance = 0.01f;
public GameObject moedaEspecial;
private Vector3 targetPos;
private bool reachedPosition = false;
private Quaternion myRotation;
// Use this for initialization
void Start () {
//this is for a crazy random rotation behavior the coin has.
transform.rotation = Random.rotation;//Quaternion.Euler(0, tempYRot, 0);
myRotation = Random.rotation;
}
void Update () {
//Rotates the coin
transform.Rotate(myRotation.x * spinSpeed * Time.deltaTime,myRotation.y * spinSpeed * Time.deltaTime,myRotation.z * spinSpeed * Time.deltaTime, Space.Self);
//Initial movement of the coin to a random position nearby its spawn point
if (!reachedPosition)
{
//move the object
transform.position = Vector3.Lerp(transform.position, targetPos, movementSpeed * Time.deltaTime);
if (Vector3.Distance(transform.position, targetPos) <= minDistance)
{
reachedPosition = true;
}
}
}
//The object that handles all coin instantiation sends trough this method the range of the random position generation and another int that represents a small chance of this coin being a special coin worth 10x the original value.
public void setUp(int [] _temp)
{
targetPos = Random.insideUnitSphere * _temp[1] + transform.position;
if (Random.Range(0,100) < _temp[0]*3)
{
coinValue *= 10;
transform.GetChild(0).renderer.enabled = false;
GameObject go = (GameObject) Instantiate (moedaEspecial, transform.position, transform.rotation);
go.transform.parent = transform;
}
}
//This is maybe the source of the problem. The player calls this method for all coins within magnet range every frame. Without it the coins attracted by the magnet would have two movements at once and often get suck at a point in space.
public void getHookedByMagnet()
{
reachedPosition = true;
}
}
And as far as I know somethings about programming, when you change something in one place, you can bet all your money that it will create other problems, I'm foreseeing one right now. The player is able to collect one "super-magnet" powerup that increases its magnet range and power for a time frame. When the code of the coin movement by the magnet get changed to the coin script, what will the best way to collect this data (stored on player?). Can't be GetComponent, as it would make this effort to improve performance useless.
Thank you very much for your help in this.
Answer by garryking · Sep 01, 2014 at 10:56 PM
public void magneticCoins()
{
float magnetForce = 1;
float magnetDistance = 2;
int coinLayer = LayerMask.NameToLayer("coin");
Vector3 here = transform.position;
Collider [] tempCoins = Physics.OverlapSphere (here, magnetDistance, 1 << coinLayer);
foreach (Collider obj in tempCoins)
{
Vector3 coinPos = obj.transform.position;
float dist = Vector3.Distance (here, coinPos);
if (dist < magnetDistance)
{
obj.GetComponent<MoedaController>().getHookedByMagnet();
obj.transform.position = Vector3.MoveTowards (coinPos, here, Time.deltaTime * magnetForce * (magnetDistance / dist));
}
}
}
tha's it ?