【Unity】Unity内で魚を泳がせよう UniAquariumというエディター拡張をつくってみた

2024/02/03に公開

つくったもの

1a3ce99dbad557ae2e9e3f61f785d4b7.gif

UnityのVisualElement下で動く水族館を作ってみました。
HtmlのCanvas風に描画できるPainter2Dというものを使用しています。

完成品はこちら

https://github.com/Garume/UniAquarium

また、今回作成したものは[ネタ]VSCodeで魚を飼って仕事中に癒されることにしたをほとんど参考にしています。

使い方

要件

  • Unity 2022.1 or later

インストール

1.Window > Package ManagerからPackage Managerを開く
2.「+」ボタン > Add package from git URLをクリック
3. 以下を入力してインストール

https://github.com/Garume/UniAquarium.git?path=/Assets/UniAquarium

開き方

ツールバーから Window > Aquariumでアクアリウムウィンドウを開けます。

機能

餌あげ

ウィンドウ上の任意の場所をクリックすると餌をあげることができます。
魚が餌を食べに行く様子をみると癒されますね。
6cc0eade3a232596be1dc028a2d34e78.gif

衝撃波

ウィンドウ上の任意の場所をクリックすると餌をあげることができます。
魚が衝撃波をよけます。かわいい。
756f394d0574921db130b7615f0cd055.gif

魚が群れを成して動く

本家でも説明されていますが、群れを再現するために Boids Algorithm というアルゴリズムを採用しています。

詳しくは以下の記事を読んでみてください。
Wikipedia
水族館を作ろう:BOIDの基本

カスタム設定

デフォルト状態では魚やクラゲの数が決まっていますが、これらの設定を独自にカスタマイズできます。
設定用のScriptable Objectを提供しており、これを使用します。

ツールバーのAssets > Create > UniAquarium Settings から作成することができます。
Assets以下であればどこに保存しても大丈夫です。

a8b2fd80d8bc89cb5d27a8791be0b4bd.png

Fish Settingsの要素数が1のときは通常通り。
1以上の場合自動的にBoidが適用されて群れを成して動くようになります。

自分好みに設定したら、Aquarium Windowをリロードしましょう。
リロードはウィンドウ右上の3点リーダー > Reloadからできます。

409c10bd298d7723b2fe670bcc0dc503.png

Aquarium Component

Window以外にもアクアリウムを描画できるようにVisualElementを継承したAquarium Componentを用意しています。

このComponentを任意のコンテナに追加することで好きな場所にアクアリウムを表示させることができます。

試しにヒエラルキーウィンドウへ描画させてみましょう。

以下のコードをAssets以下の任意のスクリプトに記述してください。

アセンブリが切られているため、参照を追加する必要があることに注意。

using System.Reflection;
using UniAquarium.Aquarium;
using UnityEditor;
using UnityEngine.UIElements;

[InitializeOnLoad]
public class HierarchyWindowHook
{
    private static VisualElement _rootVisualElement;

    static HierarchyWindowHook()
    {
        EditorApplication.update += Update;
    }

    private static void Update()
    {
        if (_rootVisualElement != null) return;
        
        // get SceneHierarchyWindow.
        var hierarchyWindowType = typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow");
        var hierarchyWindow = EditorWindow.GetWindow(hierarchyWindowType);

        // get VisualElement using reflection.
        if (hierarchyWindow == null) return;
        var fieldInfo = hierarchyWindowType.GetField("m_SceneHierarchy",
            BindingFlags.NonPublic | BindingFlags.Instance);
        if (fieldInfo == null) return;
        var sceneHierarchy = fieldInfo.GetValue(hierarchyWindow);
        var sceneHierarchyType = sceneHierarchy.GetType();
        var visualElementFieldInfo = sceneHierarchyType.GetField("m_EditorWindow",
            BindingFlags.NonPublic | BindingFlags.Instance);
        if (visualElementFieldInfo == null) return;

        var treeView = visualElementFieldInfo.GetValue(sceneHierarchy);
        var root = treeView as EditorWindow;

        _rootVisualElement = root.rootVisualElement;
        
        // Setting it to false disables clicks and other actions.
        var aquariumComponent = new AquariumComponent(false);
        _rootVisualElement.Add(aquariumComponent);
        aquariumComponent.Enable();
    }
}

保存したらヒエラルキーウィンドウを見てみましょう。
974dc08edc6087847922347b928b8745.gif

いい感じに表示できていますね!

リフレクションを使用しているためやたら長いですが、重要なのは次の3行です。

var aquariumComponent = new AquariumComponent(false);
_rootVisualElement.Add(aquariumComponent);
aquariumComponent.Enable();

AquariumComponentをインスタンス化して、Visual Elementに追加、Enable関数を呼んでいるだけです。
インスタンス化の際にfalseを引数にとっているのは、クリック等のインタラクティブを無効化するためです。
これをしないと、本来表示されていたヒエラルキー内のアイテムを触ることができません。

内部的には、

pickingMode = PickingMode.Ignore;

https://github.com/Garume/UniAquarium/blob/5efb13763d22f10a0c9a7ea32a5503a98f0cc16c/Assets/UniAquarium/Editor/Core/Components/CanvasSceneComponent.cs#L41

を指定しているだけです。

さいごに

Painter2Dを使用してアクアリウムを作成してみました。

描画しているものはがっつり本家になってしまいましたが、設計面ではかなり考えさせられるものが多かったです。

よかったら⭐をいただけると嬉しいです。

https://github.com/Garume/UniAquarium/tree/master

Discussion