(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

(:3[kanのメモ帳]

ScriptableObjectなどのアセットを、metaファイルを変更せずに上書きする 【Unity】【エディタ拡張】


このエントリーをはてなブックマークに追加


この記事でのバージョン
Unity 2018.4.6f1


はじめに

UnityではAssetDatabaseのCreateAssetを使うことで、アセットを簡単に生成する事が出来ます。

//ScriptableObjectChildというScriptableObjectを作成
var child = ScriptableObject.CreateInstance<ScriptableObjectChild> ();

//ScriptableObjectをアセットとして生成
string exportPath = "Assets/ScriptableObjects/Chid.asset";
AssetDatabase.CreateAsset(child, exportPath);

Debug.Log(exportPath + "生成");
f:id:kan_kikuchi:20190901075412j:plain


このCreateAsset、もし生成先に既にアセットがある場合は上書きで保存するのですが、

その際に当然ですがmetaファイルも上書きされます。


基本的にはそれで問題ないのですが、以下のようにどこかで参照されているものを上書きしてしまうと

参照がなくなってMissingと表示されるようになってしまいます。

///ScriptableObjectChildを参照しているScriptableObject
public class ScriptableObjectParent : ScriptableObject {
  
  [SerializeField]
  private ScriptableObjectChild _child = null;
  
}
f:id:kan_kikuchi:20190901092349g:plain


参照を維持したままファイルを更新したい場合は、metaファイルをそのままにすればいいのですが、

残念ならがらUnityにはそういう機能がないようです。(もしある場合はご一報頂けると幸いです……!)


という事で今回は

ScriptableObjectなどをmetaファイルを変更せずに上書きする処理を作ってみました!


CreateAssetWithOverwrite

さっそくScriptableObjectなどをmetaファイルを変更せずに上書きする処理である

CreateAssetWithOverwrite (を実装しているAssetDatabaseExtension)のプログラムです。

ちなみに、UNITY_EDITORで囲っているので、Editorディレクトリ内でなくても使えます。

#if UNITY_EDITOR

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEditor;

/// <summary>
/// AssetDatabaseの拡張クラス
/// </summary>
public static class AssetDatabaseExtension{

  /// <summary>
  /// アセットを上書きで作成する(metaデータはそのまま)
  /// </summary>
  public static void CreateAssetWithOverwrite(UnityEngine.Object asset, string exportPath){
    //アセットが存在しない場合はそのまま作成(metaファイルも新規作成)
    if (!File.Exists(exportPath)) {
      AssetDatabase.CreateAsset(asset, exportPath);
      return;
    }
    
    //仮ファイルを作るためのディレクトリを作成
    var fileName = Path.GetFileName(exportPath);
    var tmpDirectoryPath = Path.Combine(exportPath.Replace(fileName, ""), "tmpDirectory");
    Directory.CreateDirectory(tmpDirectoryPath);

    //仮ファイルを保存
    var tmpFilePath = Path.Combine(tmpDirectoryPath, fileName);
    AssetDatabase.CreateAsset(asset, tmpFilePath);
      
    //仮ファイルを既存のファイルに上書き(metaデータはそのまま)
    FileUtil.ReplaceFile(tmpFilePath, exportPath);
      
    //仮ディレクトリとファイルを削除
    AssetDatabase.DeleteAsset (tmpDirectoryPath);
      
    //データ変更をUnityに伝えるためインポートしなおし
    AssetDatabase.ImportAsset (exportPath);
  }
  
}

#endif


実装内容は至ってシンプルで、実際に上書きするファイルの階層に仮ディレクトリを作成し、

さらにそのディレクトリの中に仮ファイルを作成、そして、そのファイルだけを上書きするというもの。


使い方も簡単で、AssetDatabase.CreateAssetを

AssetDatabaseExtension.CreateAssetWithOverwriteに置き換えるだけ。

//ScriptableObjectChildというScriptableObjectを作成
var child = ScriptableObject.CreateInstance<ScriptableObjectChild> ();
string exportPath = "Assets/ScriptableObjects/Chid.asset";

//ScriptableObjectをアセットとして上書きで生成
AssetDatabaseExtension.CreateAssetWithOverwrite(child, exportPath);
    
Debug.Log(exportPath + "生成");
f:id:kan_kikuchi:20190901092512g:plain


ちなみに別名で仮ファイルを作り、それを置き換えるという方法もありますが、

//一旦別名で保存
string extension = Path.GetExtension(exportPath);
string copyPath = exportPath.Replace(extension, "") + "_Copy" + extension;
AssetDatabase.CreateAsset(asset, copyPath);

//別名で保存したやつを既存のファイルに上書き(metaデータはそのまま)
FileUtil.ReplaceFile (copyPath, exportPath);
AssetDatabase.DeleteAsset (copyPath);//別名ファイルを削除

//データ変更のUnityに伝えるためインポートしなおし
AssetDatabase.ImportAsset (exportPath);


これだと、なぜか上書き後のアセットのm_nameという値が別名の状態になるので、

(実際のアセット名は元アセットと同じ)

f:id:kan_kikuchi:20190901092215j:plain


仮ディレクトリを作って、その下に同名ファイルを作るという方式にしています