1

Currently I'm using ScriptableObjects as a template for things like character's faction, weapons, etc. Using faction as an example the FactionSO is a container for CharacterStats class for a character like move speed/ damage multipliers and so on. The Character class references to the FactionSO and it looks like so:

[System.Serializable]
public class Character
{
    public string characterName;
    public int level;
    public FactionSO factionSO;
    public CharacterStats stats;
}

This way I can create new Factions easily and assign reference to FactionSO when a character is created. When you start a new game with a chosen character the Stats are initialized from FactionSO's CharacterStats so the FactionSO is untouched.

It worked but when I want to save Character class with ToJSON utility, the reference to ScriptableObject is serialized as InstanceID, which means each time I restart the editor the reference is lost. In a built application it instead serializes to m_fileID and m_pathID which I don't know how vulnerable it is. Cannot using the same save file between editor and build is another issue.

I tried using Addressables' AssetReference which works but then I need to create a singleton database to load assets and create a separate SavableCharacter class which has AssetReferene FactionRef instead of FactionSO.

public class Database : MonoBehaviour
{
    public List<AssetReference> factionRef;
    public List<FactionDatabase> factionDatabase;
    public static Database instance {get; private set;}
    void Awake()
    {
        if(instance!=null){
            Destroy(this.gameObject);
            return;
        }
        instance = this;
        DontDestroyOnLoad(this.gameObject);   
        factionDatabase.Clear(); 
        foreach(var asset in factionRef){
            asset.LoadAssetAsync<FactionSO>().Completed += (aHandle)=>{
                FactionDatabase newFaction = new FactionDatabase(aHandle.Result, asset);
                factionDatabase.Add(newFaction);
            };           
        }
    }
    public FactionSO GetFactionSOFromRef(AssetReference asset){
        return factionDatabase.FirstOrDefault(i=> i.factionRef.AssetGUID==asset.AssetGUID).factionSO;
    }
    public AssetReference GetFactionRefFromSO(WeaponSO weaponSO){
        return factionDatabase.FirstOrDefault(i=> i.factionSO==factionSO).factionRef;
    }
}
[System.Serializable]
public class FactionDatabase{
    public FactionSO factionSO;
    public AssetReference factionRef;
    public FactionDatabase(FactionSO factionSO, AssetReference factionRef)
    {
        this.factionSO = factionSO;
        this.factionRef = factionRef;
    }
}

and add constructor each time I load/save the game that converts Character to SavableCharacter and vice versa:

[System.Serializable]
public class SavableCharacter
{
    public string characterName;
    public int level;
    public AssetReference factionSORef;
    public CharacterStats stats;
    public SavableCharacter(Character character){
        this.characterName = character.characterName;
        this.level = character.level;
        this.factionSORef = AssetSOLookup.instance.GetFactionRefFromSO(character.factionSO);;
    }
}

which works as intended but I feel it's really messy and what if I want to also have List saved to the character which has reference to ScriptableObject of weapons nested inside?

[System.Serializable]
public class WeaponProficiency{
    public WeaponSO weaponSO;
    public int proficiencyLevel;
    }
}

I feel like I'm having to much fun putting everything into SOs and I'm paying for it. To summarize the questions:

  1. Is there a way to make ScriptableObjects turns into AssetReference when you serialize it probably with ISerializationCallbackReceiver? so that I don't have to manually create another savable class
  2. Is my approach even the right one? There is option of assigning IDs to everything but then I need to change all references. Currently I can simply reference character.factionSO.factionName to get which faction the character belongs in.
saranw71
  • 11
  • 2
  • 1
    Why just don't replace factionSO with FacionID in your serializable character data? You can easily provide needed SO to the character on creation by saved ID. – Morion Mar 09 '23 at 18:00
  • Exactly. You don't need it. So long as whoever's loading or creating the Character has access to the SO, which probably means some system/controller/whatever, you can just query said data, or even just pass the ref as a parameter, or have a CharacterSystem.GetFactionsSO, if you need it (which I doubt). Also the way assets are referenced is by guid(asset) and file_id(within asset), but internally these are cached into instance IDs on editor startup for perf. – NPatch Mar 09 '23 at 22:49

0 Answers0