0
\$\begingroup\$

I'm making a daily rewards system in my Unity 2D game. For each real-life day the user logs in (not necessarily consecutive) the player earns a Reward Point. Reward Points do two things:

  1. Increase the rarity of what the player may obtain from Reward Chests.
  2. Increase the frequency that a player may open a Reward Chest. The frequency starts at 24 hours, and for each Reward Point, it is decreased by 24 hours. (The frequency a player earns a Reward Point is not increased, just the frequency that the player can open a chest.)

I am asking how to check if a day has passed since the player last opened the game, and, if they have, add 1 to a PlayerPrefs float (this float contains the amount of Reward Points). I am familiar with Time, but I'm not sure how to accomplish my goal with it.

.

\$\endgroup\$
4
  • \$\begingroup\$ PlayerPrefs is not a good place for this data. It's very easy for players to edit, to set themselves to the maximum streak at all times. So I recommend using a server-side solution if you don't want players to exploit this. \$\endgroup\$
    – DMGregory
    May 28, 2021 at 15:24
  • \$\begingroup\$ Hmm... I don't actually have a server or anything. I'm just an indie developer; I do this with 6 other friends. Still, though, thank you for the information about PlayerPrefs. \$\endgroup\$
    – user151165
    May 28, 2021 at 15:25
  • \$\begingroup\$ However, this is just an offline game. I know the player could cheat, but would it matter if they did? It wouldn't affect anyone except themselves. \$\endgroup\$
    – user151165
    May 28, 2021 at 15:29
  • \$\begingroup\$ On startup of the game / scene load or main menu get the current time as timestamp. You store as well the timestamp of last awarded reward. Now all you need to do is subtract them and check if the difference is one day or bigger. As pointed out, easy to cheat if offline or locally. But this logic will work the same if you place the check later on the server and exchange playerpref for a small db \$\endgroup\$
    – Zibelas
    May 28, 2021 at 16:50

1 Answer 1

0
\$\begingroup\$

Note the concerns in the comments that data stored in PlayerPrefs is easy for players to modify, and checks based on local system time can easily be cheated by changing your system clock. If you don't mind this reward being cheated, that's fine - just be aware that it will happen.

If you want to make this harder to cheat, you can spin up a cheap server and database. Make a web request to the server to ask for the reward point value. The server will receive the request, and check this player's row in the database, updating it based on the server time, then send the result back. That puts the clock and storage outside the player's reach.

That said, if you're happy with a cheatable local version, you can do something a bit like this:

// Store the keys to read/write in PlayerPrefs as constants, so we don't make typos.
const string DAY_LAST_PLAYED_ID = "DayLastPlayed";
const string DAYS_PLAYED_ID = "DaysPlayed";

// Keep track of how many days we've played before today.
// Zero for the whole first day of play, one on the second, two on the third...
int _previousDaysPlayedCount;

// Helper method to decode a timestamp from a string we stored in PlayerPrefs.
bool TryReadDate(string text, out System.DateTime date) {
    return System.DateTime.TryParseExact(text, "O",
               System.Globalization.CultureInfo.InvariantCulture,
               System.Globalization.DateTimeStyles.RoundtripKind, out date);
}

// Helper method to encode a timestamp into PlayerPrefs, 
// and save it to disc so it's not lost if we close unexpectedly.
void RecordPlayDay(System.DateTime date) {
    PlayerPrefs.SetString(DAY_LAST_PLAYED_ID,
                date.ToString("O", System.Globalization.CultureInfo.InvariantCulture));
    PlayerPrefs.Save();
}

// Call this function when your game starts up or regains focus, 
// or at intervals during play to credit a day roll-over mid-session.
void UpdateDaysPlayed() {
    // Refresh our day played count from the stored value,
    // or use zero if this is our day and we haven't written it yet.
    _previousDaysPlayedCount = PlayerPrefs.GetInt(DAYS_PLAYED_ID, 0);

    var today = System.DateTime.Now.ToUniversalTime();

    // Get our saved timestamp from the last day we played, and try to parse it.
    var lastPlayString = PlayerPrefs.GetString(DAY_LAST_PLAYED_ID, "");
    System.DateTime lastPlayDate = default;

    if (string.IsNullOrWhiteSpace(lastPlayString)
        || !TryReadDate(lastPlayString, out lastPlayDate)) {
        // If we didn't have a valid date saved, then this is our first day.
        // Write that info as a starting point to compare to later.
        RecordPlayDay(today);
    } else {
        // If we do have a valid date saved from last time, 
        // compare it to the current time. If the difference is at least
        // 24 hours, then we're playing on a new day.
        var difference = today - lastPlayDate;
        if (difference.TotalHours >= 24.0) {
            // It's a new day! Increment our count and save that information.
            _previousDaysPlayedCount++;
            PlayerPrefs.SetInt(DAYS_PLAYED_ID, _previousDaysPlayedCount);
            RecordPlayDay(today);
        }
    }
}

Note that we only update the saved timestamp when it's been a full 24 hours since our last timestamp. That way if you play frequent short sessions multiple times a day, you don't keep kicking the threshold forward without ever ticking it over.

\$\endgroup\$
3
  • \$\begingroup\$ I've been working on securing my game system right now, by the way. I used a tutorial by Brackey's on YouTube to help me change it so that now the data is saved in binary format in a file. Still not perfect, but I think it will be a bit more secure until I set up a webserver. \$\endgroup\$
    – user151165
    May 30, 2021 at 18:07
  • \$\begingroup\$ There's an error in your code. I already fixed the returnSystem. . . typo (I think it mean to be return System. . ., right?, but it also says "expected ;" and "expected }`". I obviously tried placing what it told me to into there, but it didn't really help. Do you know what's \$\endgroup\$
    – user151165
    May 30, 2021 at 18:38
  • \$\begingroup\$ That's not enough information for me to help you. I've adjusted some minor typos above — beyond that, listen to your compiler. \$\endgroup\$
    – DMGregory
    May 30, 2021 at 18:48

You must log in to answer this question.