UnityAssetsディレクトリ内のフォルダを複製するときに参照も移す方法

# はじめに
Unityのリソースをディレクトリごと複製することはよくあることだと思います。
例えばスキルのカットインや、イベントをフォルダごと複製することがあるかと思います。


フォルダの中にプレファブや画像、アニメーションなどのリソースが入っていて参照をフォルダの中で完結させて、複製した後は画像の差し替えだけで終わらせたいです。

しかし、普通に複製しただけだと複製されたディレクトリ内にあるプレファブが持つ参照はオリジナルのディレクトリ内のファイルになってしまいます。

ですので、複製した際は手動で参照を変更するという面倒な作業が必要になります。
参照が少ない場合でしたら問題ないですが、大量にある場合は骨が折れる作業になります。

そこで私は**複製と参照を切り替える**ためのパッケージを作成しました。

https://github.com/k-okawa/DirectoryDuplicator

OpenUPMにも登録しています。

https://openupm.com/packages/com.bg.directoryduplicator/?subPage=readme

# インストール方法
Unity2022.2以上のバージョンに対応しています。

## PackageManager
### GitHubから直接インストール
Window/Package Managerを開き、add package from git URL...で以下を入力して追加してください。

```
https://github.com/k-okawa/DirectoryDuplicator.git?path=Assets/Bg/DirectoryDuplicator
```

### OpenUPMでインストール

OpenUPM-CLIを事前にインストールする必要があります。

https://openupm.com/docs/getting-started.html#installing-openupm-cli

インストール後に対象のプロジェクトのルートディレクトリ(Assetsディレクトリの上の階層)で以下のコマンドを実行します。

```
openupm add com.bg.directoryduplicator
```


## UnityPackageを展開してインストール

昔ながらの方法ですが、コードに変更を加えたい場合はこちらのほうが便利です。

こちらからダウンロード可能です。

https://github.com/k-okawa/DirectoryDuplicator/releases

# 使い方

使い方はシンプルです。

複製したいディレクトリを選択して、右クリックで表示されるメニューのBg/DuplicateDirectoryWithDependenciesを実行すれば終了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188476/a01a52dc-9cfe-9ba3-d141-2c801734a93c.png)

# ユーティリティ関数

こちらの複製機能を自前のエディタ拡張に組み込みたい場合などにご利用ください。

[DirectoryDuplicator.cs](https://github.com/k-okawa/DirectoryDuplicator/blob/master/Assets/Bg/DirectoryDuplicator/Editor/DirectoryDuplicator.cs)

```csharp
/// <summary>
/// ディレクトリを複製し、対象ディレクトリ内のファイル参照も複製先のディレクトリに移す
/// </summary>
/// <param name="originDirectory">コピー対象ディレクトリ絶対パス</param>
/// <param name="targetDirectory">コピー先のディレクトリ絶対パス</param>
/// <param name="copyExcludeDirectories">コピー対象ディレクトリ内で複製の対象に入れないサブディレクトリを指定</param>
/// <param name="progressCallback">処理進行時に呼ばれるコールバック。戻り値はファイル数の合計と処理済みファイル数</param>
public static Task CopyDirectoryWithDependencies(string originDirectory, string targetDirectory, string[] copyExcludeDirectories = null, Action<(int progress, int total)> progressCallback = null);

/// <summary>
/// ディレクトリを複製する(サブディレクトリも含め)
/// </summary>
/// <param name="originDirectory">コピー対象ディレクトリ絶対パス</param>
/// <param name="targetDirectory">コピー先のディレクトリ絶対パス</param>
/// <param name="copyExcludeDirectories">コピー対象ディレクトリ内で複製の対象に入れないサブディレクトリを指定</param>
public static void CopyDirectory(string originDirectory, string targetDirectory, string[] copyExcludeDirectories = null);

/// <summary>
/// 二つのディレクトリを比較して参照を変更する
/// </summary>
/// <param name="originDirectory">コピー対象ディレクトリ絶対パス</param>
/// <param name="targetDirectory">コピー先のディレクトリ絶対パス</param>
/// <param name="progressCallback">処理進行時に呼ばれるコールバック。戻り値はファイル数の合計と処理済みファイル数</param>
public static Task ChangeGuidToNewFile(string originDirectory, string targetDirectory, Action<(int progress, int total)> progressCallback = null);
```

# 仕組み

UnityのAssetはmetaファイルが生成されてそれぞれ固有のguidを持っています。

また、アニメーションやプレファブ、マテリアルなどのリソースはYAML形式のファイルで表現されていて、その中でguidを指定されています。

つまり、YAML内のguidをすべて差し替えれば参照を変更することが可能です。

始めに通常の複製をし、オリジナルディレクトリ内のguidとコピー先のディレクトリ内のguidを比較できるマップを作成し、オリジナルディレクトリ内のguidと一致するものはコピー先のguidに置き換えています。

# サポートされるファイル
基本的にYaml形式のファイルであればどのファイルでも対象になりますが、処理の都合上拡張子を指定する必要があります。

現段階でサポートされている拡張子は以下です。

```
anim, controller, overrideContoroller, prefab, mat, material, playable, asset, unity
```

もし不足している拡張子があれば、[こちら](https://github.com/k-okawa/DirectoryDuplicator/blob/master/Assets/Bg/DirectoryDuplicator/Editor/DirectoryDuplicator.cs#L69)に追記お願いします。

また可能であればissueとプルリクエストを頂ければ幸いです。

# 外部ライブラリ

YAMLを解析するために[YamlDotNet for Unity](https://assetstore.unity.com/packages/tools/integration/yamldotnet-for-unity-36292?locale=ja-JP)をパッケージ内に含ませています。

名前空間を変更しているので、もし既存のプロジェクトにこちらのライブラリを使用している場合でもコンフリクトは起きないはずです。

# 最後に

機能が一つだけのかなりシンプルなパッケージですが、結構役に立つと思います。

複製する際にファイルを直接編集しているので、もしファイル破損が起きたとしても責任はとれないので、ご利用の際は自己責任でお願いします。