Steam Achievements

To begin integrating the steam api into your C# Unity project, follow the installation instructions on this page: https://steamworks.github.io/installation/
    Make sure to install the unity package to get the SteamManager monobehavior script. Put this on your GameController object or other object which will not be destroyed across scene loads, such as a dedicated SteamManager object.

After that has been set up, you are now able to start implementing each of your achievements.

THE INITIAL SETUP

1. Create a script which will be dedicated to steam-related logic. This script should derive from MonoBehavior. In this article, I will refer to this script as SteamLogic.cs
        Having all steam-related logic in a single script - and not mixed into other scripts at all - will keep steam-related code maintainable, easy to find, and easy to debug. We will be able to see more of this value later as we get into implementing achievement logic.

Add `using Steamworks;` to the beginning of the script, so the steam API is visible to the script.

Create a test method in SteamLogic.cs:

void Start() {
       if(SteamManager.Initialized) {
              string steamUser = SteamFriends.GetPersonaName();
              Debug.Log(steamUser);
       }
}

Make sure you have Steam running, then run your project to verify that your steam username is output to the console.

Now that the initial setup is complete, lets get into implementing achievements.

IMPLEMENTING ACHIEVEMENTS

In order to update an achievement's stat for a user, we simply need to know the name of the stat as configured in your game's steamworks stat page.
Each achievement has a corresponding stat and a stat maximum defined on the steamworks page. When an achievement's stat reaches the maximum defined by that achievement, the achievement is awarded.
    Consider the following configuration:
        A stat called "PointsScored_STAT"
        An achievement called "PointScorer_ACH", which uses PointsScored_STAT, and a maximum of 50.
        When PointsScored_STAT reaches 50, the PointScorer_ACH will be awarded automatically. There is no need to explicitly call SteamUserStats.SetAchievement for such a case.

    However, depending on your use case, you may want to explicitly award a given achievement using SteamUserStats.SetAchievement
    To support a variety of use cases, our design should easily support setting both stats or achievements directly with minimal tediousness.

First, create a public enum in SteamLogic.cs which lists out each of your achievements. For example:

public enum Achievement
{
       Monsoon_Season,
       Most_Triumphant,
       Slum_Lord,
          ACHIEVEMENT_COUNT //Make sure to include this
}

And define the following structure in SteamLogic.cs

public class Achievement_t
{
       public string name;
       public int max;

       public Achievement_t(string name_, int max_)
         {
                name = name_;
                max = max_;
         }

       public string getStatName(Achievement id)
       {
              //Generate stat name using the enum's name
              return id.ToString() + "_STAT";
        
              //If you want to generate by achievement name, do the following instead
              //return name + "_STAT";
        }
}

This class will be used to store your achievement names, and generate their stat names.

Create an array with an element for each of your Achievements:

Achievement_t[] achieves = new Achievement_t[(int) Achievement.ACHIEVEMENT_COUNT];

To avoid manually having to copy over stat names for each achievement - which can be a huge time-sink - we instead generate the stat name given the achievement enum as text OR the achievement name  - whichever you would prefer. For consistency, use one approach and stick to it within a project.
We do this in the getStatName method of the Achievement_t class.

The configured stat and achievement names in steamworks should follow this pattern

If generating stat name based upon enum text:
    Achievement API name (and string you construct the object with): Monsoon Season
    Stat API name: Monsoon_Season_STAT
    Enum name (must be same as stat name minus the _STAT): Monsoon_Season

If generating stat name based upon achievement name:
    Achievement API name (and string you construct the object with): Monsoon Season
    Stat API name: Monsoon Season_STAT
    Enum name (could be anything): MONSOON_SEASON


This way, we can simply generate the stat name, just given the achievement name or the enum name.
Once the stat and achievements are set up and named correctly on the steamworks page, initialize your achievement array with the names of your achievements.

public void loadAchieves()
{
       achieves[0] = new Achievement_t("Monsoon Season", 1);
       achieves[1] = new Achievement_t("Most Triumphant?", 1);
       achieves[2] = new Achievement_t("Slum Lord", 1);
}

public void AchievementIncrement(Achievements achID, int amnt = 1)
{
    if (SteamManager.Initialized)
    {
           //Get the achievement object given the enum
           AchievementObj ach = achieves[(int)achID];
        
           //Generate stat name using your method of choice
           string stat = AchievementObj.getStatName(achID);

           int value = 0;
           SteamUserStats.GetStat(stat, out value);
           value += amnt;
           SteamUserStats.SetStat(stat, value);
           Debug.Log(stat + " new value: " + value.ToString());

           //Storing doesn't have to be done here - but this is a safe bet. If you intend to modify an achievement's stat every frame, move this elsewhere and store only periodically for efficiency reasons.
           SteamUserStats.StoreStats();

    }
}

public void resetAchieves()
{
       SteamUserStats.ResetAllStats(true);
       SteamUserStats.StoreStats();
}

← Older Post Newer Post →