- Home /
How can I overwrite .asset using AssetDatabase.CreateAsset?
I have a TestObject class derived from ScriptableObject and an EditorWindow to edit and save it. When I edit the TestObject using the EditorWindow and save it using AssetDatabase.CreateAsset, it works well for the first time.
But if I edit and save again with the window open, it fails with an error message,
Couldn't add object to asset file because the MonoBehaviour 'TestObject' is already an asset at 'Assets/TestObject.asset'!
How can I save my TestObject for the second time? The code is below:
using UnityEngine;
using System.Collections;
using UnityEditor;
public class TestWindow : EditorWindow {
private TestObject m_test_obj;
[MenuItem("MyMenu/ShowWindow")]
public static void Open()
{
GetWindow<TestWindow>();
}
public void OnEnable()
{
m_test_obj = CreateInstance<TestObject>();
}
public void OnGUI()
{
// TestObject has one int variable named 'bar'.
m_test_obj.bar = EditorGUILayout.IntField("test variable", m_test_obj.bar);
if (GUILayout.Button("save"))
{
AssetDatabase.CreateAsset(m_test_obj, "Assets/TestObject.asset");
}
}
}
Answer by terraKote · Mar 05, 2018 at 11:14 AM
I've been wondering around this problem too and I found the following solution. Here's how it should look with your code
if (GUILayout.Button("save"))
{
if (!AssetDatabase.Contains(m_test_obj))
AssetDatabase.CreateAsset(m_test_obj, "Assets/TestObject.asset");
}
It checks if assetDatabase has that object. If there's no object, it creates an asset, otherwise it overwrites existing one, in case you edit the through your editor.
Your code is missing the "otherwise it overwrites existing one" part. Did you forget to add it?
Actually, no. You have that line
// TestObject has one int variable named 'bar'.
m_test_obj.bar = EditorGUILayout.IntField("test variable", m_test_obj.bar);
That assumes that you edit your instance value and it saves it while you edit this. I have similar code which I wrote for my dialogue editor.
private void OnDestroy()
{
foreach (Dialogue dialogue in openedDialogues)
{
string uniquePath = AssetDatabase.GenerateUniqueAssetPath(string.Format("Assets/Resources/Dialogues/{0}.asset", dialogue.name));
if (!AssetDatabase.Contains(dialogue))
{
AssetDatabase.CreateAsset(dialogue, uniquePath);
}
else
{
AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(dialogue), dialogue.name);
}
}
AssetDatabase.Refresh();
}
If you wonder how I open them here's my code
// Load dialogues from resources folder
openedDialogues.AddRange(Resources.LoadAll<Dialogue>("Dialogues")); // That are the previously saved assets.
// Here I set it to a variable but it can be like dialogue[index]
if (GUILayout.Button(dialogue.name))
{
currentDialogue = dialogue;
}
// For example, I edit name of selected dialogue
currentDialogue.name = EditorGUILayout.TextField("Dialogue Name", currentDialogue.name); // it changes and automaticaly saves it.
The code from OnDestroy function is responsible for saving new instances, which don't have .asset file or rewrite the file name if dialogue name was changed. I did it to keep file name same as dialogue name.
O$$anonymous$$, it wasn't clear that the provided code snippet is not standing by itself, but replaces the lines after m_test_obj.bar = ...
. Now it's clear.
Answer by Harinezumi · Mar 05, 2018 at 11:56 AM
This is interesting, because the scripting reference specifically states that it will overwrite existing assets... :D
Similar to how terraKote suggested, you could either try AssetDatabase.SaveAssets()
or delete the asset before saving it. Something like this:
if (GUILayout.Button("Save")) {
// Approach with SaveAssets
if (AssetDatabase.Contains(m_test_obj)) { AssetDatabase.SaveAssets(); }
else { AssetDatabase.CreateAsset(m_test_obj, "Assets/TestObject.asset"); }
// Approach with DeleteAsset
if (AssetDatabase.Contains(m_test_obj)) { AssetDatabase.DeleteAsset("Assets/TestObject.asset"); }
AssetDatabase.CreateAsset(m_test_obj, "Assets/TestObject.asset");
}
EDIT: The manual says that EditorApplication.SaveAssets()
will be deprecated in a future release, so you should not rely on it, but AssetDatabase.SaveAssets()
is NOT the same as EditorApplication.SaveAssets()
, so it should be fine (thanks for @CyRaid for pointing this out).
Hi ! I have same problem.
void Save$$anonymous$$achineData()
{
foreach(GameObject obj in Selection.gameObjects) {
string dataPath = "Assets/RepairSystem/manager/resources/machinesData/data/";
dataPath += "rolnicze/" + obj.name + ".asset";
if (AssetDatabase.Contains($$anonymous$$achinesDesignerWindow.RolniczeInfo)) { AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log(obj.name); }
else
{
AssetDatabase.CreateAsset($$anonymous$$achinesDesignerWindow.RolniczeInfo, dataPath);
}
if (Selection.Contains(obj))
{
if (!obj.GetComponent<Rolnicze>())
obj.AddComponent(typeof(Rolnicze));
obj.AddComponent(typeof(BoxCollider));
obj.GetComponent<Rolnicze>().rolData = $$anonymous$$achinesDesignerWindow.RolniczeInfo;
}
}
}
}
There's any option for create asset for every selected GameObject? When i create asset with selected BOX its ok asset is created. But if i select another gameObject like Sphare only copy my box.asset ins$$anonymous$$d of create the new one for Sphare. Would be nice for multiselecting as well :) Can you help me guys?:)
AssetDatabase.SaveAssets() is not deprecated for those who assumed Harine was talking about AssetDatabase.SaveAssets(), the documentation says:
EditorApplication.SaveAssets will be deprecated in a future release. Please use SaveAssets to maintain future compatibility.
Could you please edit your post to say EditorApplication.SaveAssets will be deprecated, and to use AssetDatabase.SaveAssets ins$$anonymous$$d? :)
Your answer
Follow this Question
Related Questions
Seperating editor and runtime data with ScriptableObjects 1 Answer
Event.pressure, does it work? 0 Answers
Can't change a variable inside the standard RigidbodyFirstPersonController 0 Answers
Custom Inspector: Accessing a reference to another MonoBehaviour? 1 Answer
RenameAsset does not change the name of asset in Project Window in Awake() or OnEnable() 1 Answer