Hello, thank you for reading this.
So in my game I have time sensitive time game objects that once created they will be destroyed after X seconds.
Every time I change from one level (scene) to other I save the current position and remaining lifetime of this object so when I go back to that scene I can instantiate that object at the same place lifetime before.
But this is wrong cause I want to know the time since the player left the room so I can decide if this object lifetime was over when the player was not on that level so I do not instantiate it again.
I can only think about save the system time when the player leave the room and them when the player come back I subtract the times to know if I should create it or not but with this logic people can mess the game by changing the system time.
Is there a better way to do so ?
It sorta comes down to what you trust for a time source and how much you care.
For objects that must die at a certain point regardless of even being in-scene, it’s probably best to keep your own global notion of advancing game time, and objects simply have a “I die after this time” value. Floats are fine for timekeeping generally.
When you re-join a scene and the objects are put back into the scene, each one can check the global notion of time and see if they should die, and Destroy() themselves appropriately, either in their Awake() or Start() methods, depending on how you have other dependencies set up.
On instantiation record the time where the object will be destroyed instead of tracking lifetime. Something along these lines:
public float TimeToLive = 60f;
public float TimeWhenDestroy;
void Start()
{
TimeWhenDestroy = Time.time + TimeToLive;
}
void Update()
{
if (Time.time >= TimeWhenDestroy)
{
Destroy(gameObject);
}
}
When you leave the scene you record all the values for TimeWhenDestroy. When you return to the scene you don’t re-instantiate any objects where Time.time >= TimeWhenDestroy.
If this needs to be integrated into a save/load system for between entire game sessions, use DateTime.Now.Ticks (and convert the value to seconds) instead of Time.time.
No no no no!
Use a coroutine.
public Coroutine lifetime;
public float lifespan = Mathf.Infinity; //Set to a value greater than or equal to zero but not infinity to get limited lifespan.
public void OnEnable()
{
if(lifetime== null)
{
lifetime= StartCoroutine(Die());
}
}
public IEnumerator Die()
{
yield return new WaitForSeconds(lifespan);
gameObject.SetActive(false);
lifetime = null;
}
public void OnDisable()
{
//Handle cleanup/object pooling here.
}
Funny enough, you’ve allocating a lot here. IEnumerator and yield instructions are not free, as they do alloc.
Also, this:
public float TimeToLive = 60f;
public float TimeWhenDestroy;
void Start()
{
TimeWhenDestroy = Time.time + TimeToLive;
}
void Update()
{
if (Time.time >= TimeWhenDestroy)
{
Destroy(gameObject);
}
}
Can be changed to:
public float TimeToLive = 60f;
IEnumerator Start()
{
float time = Time.time;
while (Time.time - startTime < TimeToLive) {
yield return null;
}
// Then do the destroy etc or .SetActive(false)
Destroy(gameObject);
}
Although, .Update would be still better, due to no alloc at all.
Also, Destroy sucks. Pool your objects.
I learned something!