I'm making a game in which I want to player collect coins using a magnet here is the coins spawning code
// Spawn Coins In Air
void SpawnCoinsInAir ()
{
float yPos = transform.position.y 1f;
float zPos = transform.position.z 4f;
for (int i = 0 ; i < 10 ; i = 1)
{
int line = i == 0 ?
RandomsPlayerController.Instance.CurrentLineIdx
:
Random.Range(0, RandomsPlayerController.Instance.Lines.Count);
for (int j = 0 ; j < Random.Range(3,6) ; j = 1)
{
zPos = j 10f;
var it = Instantiate(
coinTransform,
new Vector3(RandomsPlayerController.Instance.Lines [line].x,yPos , zPos),
Quaternion.identity
);
// do random rotattion in y
it.transform.DORotate (
Utilities.VectorY (Vector3.zero, Random.Range(-360,360)),
1f,
RotateMode.FastBeyond360
)
.SetEase (Ease.InOutSine);
Destroy (it, actionDuration 1f);
}
}
}
Now I got all my coins in my game and I have to find each coin by its tag and collect them in the shortest distance.
I'm wondering how much cost it takes to find all coins in the
Update
function OR is there any way to do the same thing by keeping performance in mind?
Here is the code
private void UseMagnet ()
{
// collect coins
foreach (var coin in GameObject.FindGameObjectsWithTag ("coin")) continue;
}
private void Update () => UseMagnet();
Thanks in Advance
CodePudding user response:
For smaller games, the cost is insignificant to the player. But it tends to get exponential the larger the search-space it.
How much cost GameObject.FindGameObjects
This is a case-by-case answer, use the profiler to see what is causing the most lag in your game.
Though unity documentation did state For performance reasons, it is recommended to not use this function every frame
.
OR is there any way to do the same thing by keeping performance in mind?
Yes, what you are looking for is called caching.
Create a static list and store all the coins there, like so:
// NOTE: Alternatively, you can turn this into a singleton
public static class GlobalCache {
// Transform or Coin Object.
private static HashSet<Transform> coinCache = new HashSet<Transform>();
public static Transform FetchAnyCoin() {
if (coinCache.Count <= 0) {
// Create a new coin, return it;
// NOTE: Ideally, creation of coins into this cache should be done else-where.
// The 'cache' should only handle storing and get/set requests.
return CreateNewCoin();
}
var result = coinCache.First();
// You can remove the fetched coin from the cache if you like.
coinCache.Remove(result);
return result;
}
}
CodePudding user response:
In contrary to the generic Find
, which you should never use if there is any other option, the FindGameObjectsWithTag
as the name says uses a hashed (pre-indexed) tag which is quite optimized and not too expensive.
Of course there is still other ways to go which are even faster.
I would use a collection so the type itself can keep track of its own instances:
public class Coin : MonoBehaviour
{
private static readonly HashSet<Coin> instances = new ();
public static IEnumerable<Coin> Instances => instances;
private void Awake()
{
// will automatically register itself when coming to live
instances.Add(this);
}
private void OnDestroy()
{
// will automatically unregister itself when destroyed
instances.Remove(this);
}
}
Now you can simply iterate all coins via e.g.
foreach(var coin in Coin.Instances)
{
// check if close enough for your magnet e.g.
if(Vector3.Distance(coin.transform.position, player.transform.position) <= magnetRange)
{
//TODO: e.g. add points ?
Destroy(coin.gameObject);
}
}
CodePudding user response:
It is considered fairly costly and is advised against using it in the Update function.
Essentially what you wish to do is to have a list over all spawned coins, and you control the spawning. This means you could easily add the spawned object to the list when you spawn it, and - if needed - remove them when you destroy them (if you dont remove them, you need to check for null in the list).
Not perfect, but depending on what you need it might work for you (not perfect because you're looping through a list asynchronously as you're adding/removing things from it)
For simplicity's sake, let's add a static (accessible anywhere) list somewhere
public static List<GameObject> SpawnedCoins = new List<GameObject>();
var it = Instantiate<GameObject>(coinTransform, ...);
SpawnedCoins.Add(it);
StartCoroutine(RemoveCoin(it))
IEnumerator RemoveCoin(GameObject coin, float time) {
yield return new WaitForSeconds(time);
SpawnedCoins.Remove(coin);
Destroy(coin);
}
Another class
foreach (var coin in SpawnedCoins) {
// Check for null first, if you Destroy them they will be null in the list
}
Alternatively, Destroy and check for null and then every few seconds clear the list of nulls before running the loop.