Save Mesh to Asset and then connect its reference to MeshFilter

Hey, I’ve been trying to create a prefab of some my game maps with an Editor script, however as the objects inside my map are read at runtime, when I save that prefab it “forgets” the meshes and textures. I was thinking about saving those meshes that are created at runtime as assets and then connect that newly created asset to my MeshFilter like if I would use Resources.Load just after saving the asset.

Is there any other way of doing such thing instead of:

  • Saving the mesh to disk as .asset or .obj
  • Loading that .asset with Resources.Load

Hope you guys can help me with this little problem! Thanks

Assets must be created and exist on disk before any other asset can persistently refer to them:

Hey!

It works, kinda… I can see now the meshes are stored however they’re all pink. I’m guessing I’ll have to do the same for the textures, right? But how I’d do that?

string mapName = Path.GetFileNameWithoutExtension(mapObject.name);
string localPath = Path.Combine("Assets", "Resources", "Prefabs", "Data", "Maps", mapName);
Directory.CreateDirectory(localPath);

var filters = mapObject.GetComponentsInChildren<MeshFilter>();
foreach(var filter in filters) {
        var id = filter.mesh.GetInstanceID();
        AssetDatabase.CreateAsset(filter.mesh, Path.Combine(localPath, id + ".asset"));
}

localPath = AssetDatabase.GenerateUniqueAssetPath(localPath + ".prefab");

PrefabUtility.SaveAsPrefabAssetAndConnect(mapObject, localPath, InteractionMode.UserAction);

Close… you need to make the texture, put it on disk, make the material, assign the texture to its slot, write that material to disk, then finally put the material reference into the renderer(s) that need it.

Even though the mapObject already has everything rendered at the scene with everything assigned? Gonna try either way

No asset on disk can refer to any asset in scene, for obvious reasons.

Sorry, they’re not obvious to me… would you point me to some docs regarding that? Also I have tried this, I’m afraid I did not understand exactly what you meant… Is there a way of skipping writing the Material to disk? Some of these materials have more than one texture and that would be a pain to manage since I’m requesting all Renderers at once.

        string mapName = Path.GetFileNameWithoutExtension(mapObject.name);
        string localPath = Path.Combine("Assets", "Resources", "Prefabs", "Data", "Maps", mapName);
        Directory.CreateDirectory(localPath);

        var filters = mapObject.GetComponentsInChildren<MeshFilter>();
        var renderers = mapObject.GetComponentsInChildren<MeshRenderer>();
        for (int i = 0; i < filters.Length; i++) {
            var filter = filters[i];
            var renderer = renderers[i];

            var progress = i * 1f / filters.Length;
            if (EditorUtility.DisplayCancelableProgressBar("UnityRO", $"Saving meshes - {progress * 100}%", progress)) {
                break;
            }

            var originalTexture = renderer.material.mainTexture as Texture2D;
            var id = filter.mesh.GetInstanceID();

            try {
                var texture = new Texture2D(originalTexture.width, originalTexture.height);
                texture.SetPixels(originalTexture.GetPixels());
                texture.Apply();
                texture.name = $"{id}";
               
                AssetDatabase.CreateAsset(texture, Path.Combine(localPath, id + "_texture.asset"));

                renderer.material.mainTexture = texture;
            } catch {
                Debug.LogError("Error saving " + id + "_texture.asset");
            }

            AssetDatabase.CreateAsset(filter.mesh, Path.Combine(localPath, id + ".asset"));
        }

        localPath = AssetDatabase.GenerateUniqueAssetPath(localPath + ".prefab");
        PrefabUtility.SaveAsPrefabAssetAndConnect(mapObject, localPath, InteractionMode.UserAction);
        EditorUtility.ClearProgressBar();
        EditorApplication.ExitPlaymode();

This is what I got so far and the map is there but it raised some concerns like, every texture is saved for each time their mesh appears, even though they are repeated. Do you think doing this is even feasible?

If something is on disk it can be copied and sent anywhere in the world.

If you have some pointer on your disk pointing to an asset NOT on your disk but in your memory in your scene, how would that be meaningfully represented in the asset on the disk?

That’s why Unity requires you to write assets to disk first, which fundamentally changes their character by adding them to the asset database, which then can be referenced.

That just sounds like having a central way of knowing what you wrote so you don’t keep writing it. That would depend if you cared about the image by name, or if you wanted to do some kind of content-aware hash in case it changes and needs to be resaved.