Update energy 1 by 1 and allow for updating at homescreen?

In this course we just check if enough time has passed and refill all of our points in one go.

I’m trying to set it up so that energy will start refilling once energy != maxEnergy and will add 1 every so often based on the timer.

Sounds like a simple fix right? (maybe it is but boy has it been giving me heck)

We’re just changing it to add 1 energy after the duration right? but…

In this lesson we’re only updating the energy and max energy when we press play and when the game is loaded.

I promise I put a lot of time into trying to get this to work but it’s not going well and I’m not sure seeing the code will help you help me but here you go… (yes I know I got rid of the string ref keys because of all the recoding I was doing. Just trying to get it to work and I’ll fix later lol)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;
using System;

public class MainMenu : MonoBehaviour
{
    [SerializeField] private TMP_Text highScoreText;
    [SerializeField] private TMP_Text energyText;
    [SerializeField] private int maxEnergy = 3;
    [SerializeField] private int energyRechargeDur = 10;

    private DateTime nextEnergyTime;
    private DateTime lastEnergyTime;
    private bool isRestoring = false;

    [SerializeField] private int energy;


    public void Start()
    {
        int highScore = PlayerPrefs.GetInt(ScoreSystem.HighScoreKey, 0);
        UpdateEnergy();


        energyText.text = energy.ToString() + "/" + maxEnergy.ToString();



        if (!PlayerPrefs.HasKey("currentEnergy"))
        {
            PlayerPrefs.SetInt("currentEnergy", maxEnergy);
            Load();
            StartCoroutine(RestoreEnergy());
        }
        Load();

    }
    private void Update()
    {

        if (energy < maxEnergy)
        {
            StartCoroutine(RestoreEnergy());
        }

    }

    public void UseEnergy()
    {
        if (energy >= 1)
        {
            energy--;
            UpdateEnergy();

            if (isRestoring == false)
            {
                if (energy + 1 == maxEnergy)
                {
                    nextEnergyTime = AddDuration(DateTime.Now, energyRechargeDur);
                }

                StartCoroutine(RestoreEnergy());
            }
        }
        else
        {
            Debug.Log("Insufficient Energy!!");
        }
    }

    private void Play()
    {
        UseEnergy();
        SceneManager.LoadScene(1);
    }

    private IEnumerator RestoreEnergy()
    {
        UpdateEnergyTimer();
        isRestoring = true;

        while (energy <= maxEnergy)
        {
            DateTime currentDateTime = DateTime.Now;
            DateTime nextDateTime = nextEnergyTime;
            bool isEnergyAdding = false;

            while (currentDateTime > nextDateTime)
            {
                if (energy < maxEnergy)
                {
                    isEnergyAdding = true;
                    energy++;
                    UpdateEnergy();
                    DateTime timeToAdd = lastEnergyTime > nextDateTime ? lastEnergyTime : nextDateTime;
                    nextDateTime = AddDuration(timeToAdd, energyRechargeDur);
                }
                else
                {
                    yield return null;
                }
            }
            if (isEnergyAdding == true)
            {
                lastEnergyTime = DateTime.Now;
                nextEnergyTime = nextDateTime;
            }
            UpdateEnergyTimer();
            UpdateEnergy();
            Save();

        }
        isRestoring = false;
        yield return null;
    }

    private DateTime AddDuration(DateTime datetime, int energyRechargeDur)
    {
        return datetime.AddSeconds(energyRechargeDur);
    }

    private void UpdateEnergyTimer()
    {
        if (energy >= maxEnergy)
        {
            energy = maxEnergy;
            return;
        }

        TimeSpan time = nextEnergyTime - DateTime.Now;
        string timeValue = String.Format("{0:D2:{1:D1}", time.Minutes, time.Seconds);
        return;
    }

    private void UpdateEnergy()
    {
        Save();
        energyText.text = (energy.ToString() + "/" + maxEnergy.ToString());
        return;
    }

    private DateTime StringToDate(string datetime)
    {
        if (String.IsNullOrEmpty(datetime))
        {
            return DateTime.Now;
        }
        else
        {
            return DateTime.Parse(datetime);
        }
    }

    private void Load()
    {
        energy = PlayerPrefs.GetInt("currentEnergy");
        nextEnergyTime = StringToDate(PlayerPrefs.GetString("nextEnergyTime"));
        lastEnergyTime = StringToDate(PlayerPrefs.GetString("lastEnergyTime"));
    }


    private void Save()
    {
        PlayerPrefs.SetInt("energy", energy);
        PlayerPrefs.SetString("nextEnergyTime", nextEnergyTime.ToString());
        PlayerPrefs.SetString("lastEnergyTime", lastEnergyTime.ToString());
    }

}


From previous experience I believe the best way to make this work would be create an energy object and use DontDestroyOnLoad() but I’m still confused as to how I would implement the DateTime player prefs saving. (REALLY wish they’d add DateTime as a saveable component to PlayerPrefs lmao)

Hi DoctorFaux,
I actually worked this out over the weekend. I’m at work at the moment, but when I get home later tonight, I’ll post my solution here.

1 Like

It is saveable, just as a string. You can PutString(dateTime.ToString())
and the retrieve the string and use DateTime.Parse(storedString) to restore it.

1 Like

Ok, here’s my take on the Main Menu, which allows for energy to start regrowing as soon as it is used.
The Notification will only be queued in the event that the energy is empty, and is set to fire when the energy completely restores.

using System;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[RequireComponent(typeof(AndroidNotificationHandler))]

public class MainMenu : MonoBehaviour
{
    [SerializeField] private int maxEnergy=3;
    [SerializeField] private float energyRechargeDuration = 10;

    [SerializeField] TMP_Text livesDisplay;
    private Button button;
    private AndroidNotificationHandler notificationHandler;

    private const string EnergyKey = "Energy";
    private const string EnergyReadyKey = "EnergyReady";

    private int energy;
    private DateTime energyReady;

    private void Start()
    {
        notificationHandler = GetComponent<AndroidNotificationHandler>();
        button = GetComponentInChildren<Button>();
        
        energy = PlayerPrefs.GetInt(EnergyKey, maxEnergy);
        string energyReadyString = PlayerPrefs.GetString(EnergyReadyKey, DateTime.Now.ToString());
        energyReady = DateTime.Parse(energyReadyString);
        //If energy is already at maxEnergy, do nothing.  If energy is low and energy is ready Add until full or
        //no longer past duration
        while (energy < maxEnergy && DateTime.Now > energyReady) 
        {
            AddEnergy();
        }
        UpdateButton();
    }

    private void AddEnergy()
    {
        energy++;
        energyReady = energyReady.AddMinutes(energyRechargeDuration);
        PlayerPrefs.SetInt(EnergyKey, energy);
        PlayerPrefs.SetString(EnergyReadyKey, energyReady.ToString());
        UpdateButton();
    }

    private void UpdateButton()
    {
        button.interactable = energy > 0;
        UpdateLivesDisplay();
        UpdateNotificationHandler();
    }

    private void UpdateLivesDisplay()
    {
        if (energy >= maxEnergy)
        {
            livesDisplay.text = $"Lives: {energy}";
        }
        else
        {
            var delta = energyReady - DateTime.Now;
            livesDisplay.text = $"Lives {energy} ({delta.Minutes}:{delta.Seconds:D2})";
        }
    }

    // I chose to do this in Update instead of with an Invoke so that the timer could be properly updated
    private void Update()
    {
        if (energy >= maxEnergy) return;
        if (DateTime.Now > energyReady)
        {
            AddEnergy();
            UpdateNotificationHandler();
        } 
        UpdateLivesDisplay();
    }

    private void UpdateNotificationHandler()
    {
        #if UNITY_ANDROID
        if (energy ==0)
        {
            DateTime maxEnergyReady = energyReady.AddMinutes(energyRechargeDuration * (maxEnergy - 1));
            notificationHandler.ScheduleNotification(maxEnergyReady);
        }
        else
        {
            notificationHandler.ClearNotifications();
        }
        #endif
    }

    public void Play()
    {
        if (energy <= 0) return;
        PlayerPrefs.SetInt(EnergyKey, energy-1);
        PlayerPrefs.SetString(EnergyReadyKey, energy>=maxEnergy?
            DateTime.Now.AddMinutes(energyRechargeDuration).ToString():
            energyReady.ToString());
        SceneManager.LoadScene(1);
    }
}

3 Likes

Thank you so much for the help Brian! Amazing as always!

This is what I really needed, I somehow didn’t get what the Parse was doing.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms