Recently I stumbled across the following problem in the context of making save-games:

The Task

  • I have a bunch of GameObjects instantiated from Prefabs
  • They all have some kind of Entity object attached
  • The Entity script manages the GameObject
  • I want to save the DATA of this entity to a savegame file or similar (compressed byte stream, xml, anything)
  • When loading a savegame, I want to get the Prefab from the stored entity data, instantiate the prefab, hydrate its entity with the stored data.
  • game loaded

The Problem:

You cannot get the asset path (be it resources or from asset bundles) from an instantiated prefab when you're not running inside the editor.
So we have to set the path and the name manually, somehow.
Ideally without any additional setup when creating new Prefabs.

Possible Solutions

There are plenty of solutions to this problem, one worse than the other.
And there is bad news: There seems to be no feasible dynamic way of doing this. But we can get close.
I'll highlight a few for you. This is no code-show-off. I will just present some ideas and rough concepts.
Maybe they'll help you on your quest.

Solution 1: Add paths manually to each prefab

One could go and be done with is. Just add a new field to, lets say a "Saveable" MonoBehaviour and read this data to load assets.
There is a lot of room for error here though. Typos, wrong path names and possibly, people that don't need to know about the savegame details could change them ... for whatever reason.
You never know.
So this is not such a good idea.
It could look something like this

// MonoBehaviour
public string assetPath;
public string assetName;

// Saving
public void SaveAsset(Saveable saveable) {
    this.addSaveable(saveable.assetPath, saveable.assetName, saveable.GetSerializedData ());
}
// Loading
public UnityEngine.Object LoadAsset(string path, string name) {
    return MyAssetmanager.Instance.Load (path, name);
}

You see, assetPath and assetName could be changed anywhere from anyone. You cannot be sure that your asset manager (or whatever else you may use) loads the correct thing without explicitly checking.

Solution 2: Rely on a certain folder structure that reflects the Prefab name

Basically, call your prefab "Prefabs/Worlds/Env/Tree_01" and put it in a folder that resembles the prefab name in its structure.
This way your prefabs instance would be called something like "Prefabs/Worlds/Env/Tree_01 (Clone)". Which is okay, one could just cut off the "(Clone)" part.
However, if something in your game changes the instances name, you lost the reference.
So this would be a good solution but is pretty much prone to errors.

// Saving
public void SaveAsset(Saveable saveable) {
    this.addSaveable(saveable.gameObject.name, saveable.GetSerializedData ());
}
// Loading
public UnityEngine.Object LoadAsset(string prefabName) {
    return MyAssetmanager.Instance.Load (prefabName);
}

As shown, this WOULD be a very nice solution if the name would stick.

Solution 3: Use attributes on your Saveable objects

This is my favourite solution so far and I'm also using it for Firewalker.
This has one major pitfall though. You have to have a Saveable object for each Prefab you create.
However, this Saveable object is annotated with an attribute that defines path and name of the Prefab either in the Resources folder or in an Asset Bundle.
It still requires some manual writing, however, this way these things become meta information and don't clutter your MonoBehaviour and are also not changeable just like that.
It's more or less robust and for each Saveable object you can get its asset paths from the attached attribute.
It involves a bit more heavy lifting in the code but only in one place. Here's how it could be done:

[AssetDefinition("prefabs/healing", "Herb Prefab")] // defines a Herb Prefab at the path prefabs/healing, I trust you know how to make custom attributes :)
public class SomePrefab : Saveable {
    // This is actually from saveable, but I put it here for brevity
    public string AssetBundle {
            get {
                var attribs = this.GetType().GetCustomAttributes(typeof(AssetDefinitionAttribute), true);
                if (attribs.Length > 0) {
                    var attrib = attribs[0] as AssetDefinitionAttribute;
                    return attrib.prefabPath; // for our example, this returns "prefabs/healing"
                }
                throw new IllegalStateException("Saveable has no AssetDefinition attribute attached. Type is " + this.GetType());
            }
        }

        public string AssetName {
            get {
                var attribs = this.GetType().GetCustomAttributes(typeof(AssetDefinitionAttribute), true);
                if (attribs.Length > 0) {
                    var attrib = attribs[0] as AssetDefinitionAttribute;
                    return attrib.prefabName; // for our example this returns "Herb Prefab"
                }
                throw new IllegalStateException("Saveable has no AssetDefinition attribute attached. Type is " + this.GetType());
            }
        }
}
// Saving
public void SaveAsset(Saveable saveable) {
    this.addSaveable(saveable.AssetBundle, saveable.AssetName, saveable.GetSerializedData ());
}
// Loading
public UnityEngine.Object LoadAsset(string assetBundle, string assetName) {
    return MyAssetmanager.Instance.Load (assetBundle, assetName);
}

Solution 4: Disregard anything and make data tables!

Something I'm experimenting with. This is a more elaborate solution that requires a lot of "meta-work".
File some data tables (xml, csv, anything like that). These contain mappigns from IDs (possibly numbers) to asset paths and names.
Create an asset loader (Resources or AssetBundles, no matter) that knows which ID to map to what prefab.
And now comes the part where I didn't think it to the end yet:
You'll have to specify the ID somewhere.
Probably in a MonoBehaviour.
And when you do that, you could as well use one of the other, less complicated solutions.
I have no code snippets for that handy.

Alright.
That's it, I hope it helps you a little bit in finding a suitable solution for your savegame purposes!

Cheers,
-df