Not saving ScriptableObjects correctly, losing prefab ref

Discussion and help for Easy Save 3
Emolk
Posts: 10
Joined: Tue Nov 19, 2019 8:35 am

Not saving ScriptableObjects correctly, losing prefab ref

Post by Emolk »

Hi. When i save my ScriptableObject of type 'Weapon' i lose two reference. The first i lose is the icon and i also lose references in my 'Attack' (so my weapon can't even fire). To be clear, the ScriptableObject of type 'Weapon' holds another ScriptableObject of type 'Attack'. When saving the weapon, i lose the prefab reference on the attack.

This is what the attack is meant to look like:

Image

This is what the attack looks like when the weapon is saved and loaded (loaded in a different scene):

Image

As you can see, the prefab reference is gone.

I have added Weapon to Types:

Image

As well as the projectile types:

Image

Note the 'Attack' class never shows up in the Types but that's probably because it is abstract?

What exactly i am doing: From my scene 'Loadout', a weapon is selected and saved as such:

Code: Select all

ES3.Save<Weapon>("startingWeapon", startingWeaponChoices[index]);
Then i load a 'Map' scene. After that i load a 'Battle' scene, where i load the weapon:

Code: Select all

Weapon startingWeapon = ES3.Load<Weapon>("startingWeapon");
The issue happens here, the weapon loses its icon and the attacks in the weapon lose their prefab references.

Any help would be greatly appreciated. Thanks.


Relevant classes/types below:

Code: Select all

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class Attack : ScriptableObject
{
    public abstract bool CanAttack(GameObject attacker);
    public abstract void OnAttack(GameObject attacker);
    public float AS = 1f;
    public bool offCD;
}

Code: Select all

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "New Projectile Attack", menuName = "Attack/Projectile/Default")]
public class Attack_Projectile : Attack
{
    public GameObject projectilePrefab;  
    public float force;
    public override bool CanAttack(GameObject attacker)
    {
        return offCD;
    }

    public override void OnAttack(GameObject attacker){
        offCD = false;
        Cooldown(attacker);
        Attack(attacker);
        
    }

    public void Cooldown(GameObject attacker){

        // Check if phantom or not

        if(attacker.tag == "Player"){
            offCD = false; 
            attacker.GetComponent<Cooldowns>().WaitAndDo(AS, () => offCD = true);
        }
    }
    public void Attack(GameObject attacker)
    {
            Vector3 sp = Camera.main.WorldToScreenPoint(attacker.transform.position);
            Vector3 dir = (Input.mousePosition - sp).normalized;
 
            GameObject objectInstance = Instantiate(projectilePrefab,
            attacker.transform.position, Quaternion.Euler(new Vector3(0, 0, 0)));
 
            // Points missile at mouse pos
 
            float rot_z = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
            objectInstance.transform.rotation = Quaternion.Euler(0f, 0f, rot_z - 90);

            objectInstance.GetComponent<Rigidbody2D>().AddForce(dir * force);
    }
}

Code: Select all

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "New Weapon", menuName = "Inventory/Weapon")]
public class Weapon : Equipment
{
    public Attack attack;
    public List<Attack> attacks;

    void OnEnable(){
        attacks = new List<Attack>();
        if(attack!=null){
            addAttack(attack);
        }
    }
   
    public bool CanAttack(GameObject attacker)
    {
        return true;
        // return attack.CanAttack(attacker);
    }

    public void addAttack(Attack attackToAdd){
        attacks.Add(attackToAdd);
    }
   
    public void Attack(GameObject attacker)
    {

        if(attack != null){
            if(attack.CanAttack(attacker))
                attack.OnAttack(attacker);
        }

        foreach (var attackInList in attacks)
        {
            // Debug.Log("Attacking with: " + attackInList.name);
            if(attackInList.CanAttack(attacker))
                attackInList.OnAttack(attacker);
        }
    }
}
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Not saving ScriptableObjects correctly, losing prefab re

Post by Joel »

Hi there,

This is likely because a reference to these objects do not exist in the reference manager of the scene that you are loading from.

Please could you try going to the scene in which you're intending to load and drag the prefab into the References array of the ES3ReferenceMgr Component of the Easy Save 3 Manager. You will also need to delete your save data by going to Window > Easy Save 3 > Tools > Clear Persistent Data Path.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Emolk
Posts: 10
Joined: Tue Nov 19, 2019 8:35 am

Re: Not saving ScriptableObjects correctly, losing prefab re

Post by Emolk »

Joel wrote:Hi there,

This is likely because a reference to these objects do not exist in the reference manager of the scene that you are loading from.

Please could you try going to the scene in which you're intending to load and drag the prefab into the References array of the ES3ReferenceMgr Component of the Easy Save 3 Manager. You will also need to delete your save data by going to Window > Easy Save 3 > Tools > Clear Persistent Data Path.

All the best,
Joel
Hi Joel, i have tried dragging the prefab in but it doesn't seem to do anything. I also tried to drag it into an existing slot to no avail (the slot stays the same). The editor also becomes extremely laggy when i open the reference array as there are a ton of references.

I have looked through the reference array and none of my attack prefabs seem to be there either.

Is there another way i can add the prefabs to the reference manager as dragging and dropping seems to have no effect?
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Not saving ScriptableObjects correctly, losing prefab re

Post by Joel »

Hi there,

Just to check, is there a reference to your prefab in the scene prior to runtime? If not, make sure a script references it in your scene, and then press the Refresh References button on the ES3ReferenceMgr. You might want to try pressing this anyway just to check everything has been added to your scene correctly.

If this doesn't resolve the issue, please could you create a new project which replicates it and private message it to me so I can see what is happening?

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Emolk
Posts: 10
Joined: Tue Nov 19, 2019 8:35 am

Re: Not saving ScriptableObjects correctly, losing prefab re

Post by Emolk »

Joel wrote:Hi there,

Just to check, is there a reference to your prefab in the scene prior to runtime? If not, make sure a script references it in your scene, and then press the Refresh References button on the ES3ReferenceMgr. You might want to try pressing this anyway just to check everything has been added to your scene correctly.

If this doesn't resolve the issue, please could you create a new project which replicates it and private message it to me so I can see what is happening?

All the best,
Joel
Have sent you a PM, thanks.
steve18624
Posts: 5
Joined: Tue Jul 28, 2020 1:58 am

Re: Not saving ScriptableObjects correctly, losing prefab ref

Post by steve18624 »

Hello,

I am experiencing this same issue (i.e. prefab references on SO's missing) and if possible I'd like to know how this thread was resolved. I am instantiating the Scriptable Object though, so it may be a different/similar issue.

Thanks.
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Not saving ScriptableObjects correctly, losing prefab ref

Post by Joel »

Hi Steve,

In this case this problem was resolved by right-clicking the prefab and going to Easy Save 3 > Add Reference(s) to Manager. You should also make sure you're using the latest version of Easy Save.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
steve18624
Posts: 5
Joined: Tue Jul 28, 2020 1:58 am

Re: Not saving ScriptableObjects correctly, losing prefab ref

Post by steve18624 »

Hi Joel,

Thanks. I tried the solution that you mentioned and it didn't solve the problem. I confirmed that I have the latest version. I added the references manually in the inspector and immediately after they were instantiated via script (see below).

When I added the references manually, I noticed that the total reference count increased. It appears that had no impact though.

As mentioned before, my situation was a little bit more complex than the original post. I am instantiating two ScriptableObjects (SO) classes: TeamData and CharacterData. In addition, the instantiated SOs (characters) that are losing the references are in a List<CharacterData> within a Team SO. The instantiated Character SO's only lose the reference fields (i.e. writer.WritePropertyByRef). All the write byType fields are fine.

Works

Code: Select all

writer.WriteProperty("CharacterID", instance.CharacterID, ES3Type_int.Instance);
Doesn't Work

Code: Select all

writer.WritePropertyByRef("CharacterModel", instance.CharacterModel);
ES3 created the ES3Type script and I didn't edit it.

Also, here is the spawn cloning script:

Code: Select all

        TeamData CloneTeamData(TeamData team)
        {
            TeamData clone = Instantiate(team);
            clone.TeamMembers.Clear();

            for(int i=0; i<team.TeamMembers.Count; i++)
            {
                clone.TeamMembers.Add(Instantiate(team.TeamMembers[i]));

                ES3ReferenceMgr.Current.Add(clone.TeamMembers[i]);
            }

            return clone;
        }
Best,

Steve
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: Not saving ScriptableObjects correctly, losing prefab ref

Post by Joel »

Hi Steve,

You're adding your instances to the reference manager, but you're not specifying a reference ID. This means a random reference ID will be assigned to it which will differ between loads. Instead you should provide a reference ID for the second parameter which will uniquely identify each instance and not differ between loads.

For example, you could use the iterator:

Code: Select all

ES3ReferenceMgr.Current.Add(clone.TeamMembers[i], (long)i);
All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
steve18624
Posts: 5
Joined: Tue Jul 28, 2020 1:58 am

Re: Not saving ScriptableObjects correctly, losing prefab ref

Post by steve18624 »

Unfortunately adding a specific referenceID did not work. I confirmed that ES3 was adding it's own unique ID without explicitly inputting one as a parameter, so inputting a parameter seems redundant- unless I'm missing something.

After some debugging and testing, I found a solution. However, it is not ideal and I'm hoping that you can help identify a better solution.

It appears that ES3 does not correctly save references to ScriptableObjects (SO) that are added via code and/or that are not referenced in the scene hierarchy before clicking Play in the Editor. In my setup, the SOs that I need don't exist in the scene hierarchy and aren't referenced until a character is instantiated at runtime (i.e. the Character and CharacterSO are not referenced in the scene hiearchy before clicking Play). To address this, when a character is created, I tried to add the reference to the ES3ReferenceMgr as follows:

Code: Select all

ES3ReferenceMgr.Current.Add(SO);
Adding a reference this way doesn't work. However, I created a SO container and added all the character SOs to it before starting the scene. This solved the problem; however, it is not the ideal solution because I don't want all characters in every scene. Also, I don't know which characters I'll need until the Player selects them. This is the container.

Code: Select all

public class SceneDataManager : MonoSingleton<SceneDataManager>
{
    public List<TeamData> TeamData;
    public List<CharacterData> Characters;
}
Looking at the way ES3 serializes SO's, it appears it views them as unique instances in the scene and does not reference the underlying SO asset itself (i.e. the Object in the Project view).

Code: Select all

	public class ES3UserType_CharacterData : ES3ScriptableObjectType
	{
		public static ES3Type Instance = null;

		public ES3UserType_CharacterData() : base(typeof(VRTBT.CharacterData)){ Instance = this; priority = 1; }
		
The reference manager must link the two (i.e. the SO instance (ES3UserType) and the SO asset in the projects folder); however, it doesn't appear that it's doing its job correctly.

I'm working on testing whether this is an Editor only issue and whether the issue exists in a game build. Any guidance would be appreciated!
Post Reply