How to make RPG stat system?

I am attempting to make a game similar to skyrim (I know you’re not supposed to do that as a solo dev, but I feel like im experienced enough to make a very small RPG, and also I dont really care how good the final result is) and I am at the point in development were I have a very solid plan, but no clue how to execute said plan.
Basically I just want to know how I would go about creating a system where the player has a set of stats, which can be accessed by other scripts, the player can gain xp which levels them up and gives them skill points to assign to skills that are seperate from stats, but also accesible from other scripts, and one of the players stats can be permanantly increased every few level ups.
The system I have in place now uses a scriptable object to handle the players stats, but apparently that doesnt play nice with saving and loading games, so I really have no clue what to do.
Sorry if this question is a little vague, I just want some idea of how I would approach this problem.

Thanks!

Hello @Dodoologist,

Using Scriptable Objects to save datas doesn’t work, you’ll get some moment where your stats will be messed up so it’s not recommended, what I do is using the Scriptable Object to store every infos allowing me to calculate the player stats and use the PlayerPrefs to save the stats levels, here is a little example:

//In my scriptable object

public int attackBase;
public int attackStepPerLevel;

public int healthBase;
public int healthStepPerLevel;

public int GetHealth(int level) {
    return this.healthBase + (this.healthStepPerLevel * level);
}

public int GetAttack(int level) {
    return this.attackBase + (this.attackStepPerLevel * level);
}

So for example when the player throws a fireball I’ll access the Scriptable Objects to setup the fireball damages like this:

fireball.damages = this.playerStatsSO.GetAttack(PlayerPrefs.GetInt("PLAYER_ATTACK_LEVEL"));

So if I want to increase the player attack stat I can simply do that:

int currentAttack = PlayerPrefs.GetInt("PLAYER_ATTACK_LEVEL");
PlayerPrefs.SetInt("PLAYER_ATTACK_LEVEL", currentAttack + 1);

I’m currently making a game inspired by Skyrim and Palworld so I could share some tips with you if you want

I hope it helps.

Hi @Dodoologist. Json is basically the industry standard for this sort of thing. Here is some code that uses Newtonsoft.Json to do load/store within the correct place for a Unity project.

using System.IO;
using UnityEngine;
using Newtonsoft.Json;

public class JsonLoader<T> where T : new() {
    readonly string dir;
    readonly JsonSerializerSettings settings = new();

    public JsonLoader(string dir = null)
    {
        if (dir == null) {
            this.dir = Application.persistentDataPath;
        }
        else if (dir.Contains(Application.persistentDataPath)) {
            this.dir = dir;
        }
        else {
            this.dir = Path.Combine(Application.persistentDataPath, dir);
        }
        if (!Directory.Exists(this.dir)) {
            Directory.CreateDirectory(this.dir);
        }
        settings.ObjectCreationHandling = ObjectCreationHandling.Replace;
    }

    public T Load(string path)
    {
        try {
            var jsonFile = Path.Combine(dir, path);
            var jsonString = File.ReadAllText(jsonFile);
            var result = JsonConvert.DeserializeObject<T>(
                jsonString, settings);
            if (result == null) {
                return new T();
            }
            return result;
        }
        catch (FileNotFoundException) {
            return new T();
        }
    }

    public void Store(string path, T obj)
    {
        var jsonFile = Path.Combine(dir, path);
        var jsonString = JsonConvert.SerializeObject(obj, settings);
        File.WriteAllText(jsonFile, jsonString);
    }
}

And here’s how I would use it…

// Given some data struct
public struct PlayerData
{
    public int Health;
    public int Strength;
}

// Create a loader for the "Data" directory 
JsonLoader<PlayerData> loader = new("Data");

// To load previously saved "Data/Player" file
var playerData = loader.Load("Player");

// To save the values of playerData to "Data/Player" file
loader.Store("Player", playerData);