けいごのなんとか

Unityユーザーとしてのブログ。ギリギリ路線走ってます。

Unity5.6b2で追加されたTreeViewを使ってみる

2016年12月20日にUnity5.6b2がリリースされました。

unity3d.com

このバージョンでツリー階層を表現するためのTreeViewの機能が追加されています。

リリースノートより。

Editor: TreeView IMGUI Control, which can display hierarchical data that can be expanded and collapsed. Additionally it lets you create list views and multi-column tables for Editor tools.

このTreeViewは、今までヒエラルキーウィンドウで使われていた機能です。今まで内部のみの機能だったものがユーザーも扱えるようになったということです。

下の図はヒエラルキーウィンドウです。このように階層状態でGUIを表示するための機能がTreeViewです。 f:id:anchan828:20161222230233p:plain

TreeViewを使ってみる

では早速、TreeViewを使ってみましょう。

TreeViewについては、スクリプトリファレンスに詳しく掲載されています。ゆっくりと読み解いていきましょう。

docs.unity3d.com

TreeView で必要なクラス

TreeViewでは3つのクラスが必要です。

クラス 説明
TreeView
TreeViewItem TreeViewに表示する要素
TreeViewState TreeViewやTreeViewItemの状態を管理するクラス

とりあえず使ってみる(シンプルな作成例)

TreeViewクラスは抽象クラスです。まずはTreeViewクラスを継承した ExampleTreeView クラスを作成します。

using UnityEditor.IMGUI.Controls;

public class ExampleTreeView : TreeView
{
    public ExampleTreeView(TreeViewState state) : base(state)
    {
    }

    protected override TreeViewItem BuildRoot()
    {
        throw new System.NotImplementedException();
    }
}

BuildRoot はツリー階層のルートとなる要素を作成するためのメソッドです。 BuildRootメソッド内に次のコードを追加します。

protected override TreeViewItem BuildRoot()
{
    var root = new TreeViewItem(id: 1, depth: -1, displayName: "ルート");

    var parent = new TreeViewItem(id: 2, depth: 0, displayName: "親その1");

    root.children = new List<TreeViewItem>
    {
        parent,
        new TreeViewItem(id: 3, depth: 0, displayName: "親その2"),
    };

    parent.children = new List<TreeViewItem>
    {
        new TreeViewItem(id: 4, depth: 1, displayName: "子供"),
    };

    return root;
}

BuildRootメソッド内の処理を図にするとこんな感じ。

f:id:anchan828:20161222233813p:plain

各要素にユニークなID(id)とインデントの深さ(depth)と表示名(displayName)を設定しています。

これで、TreeViewItemの準備ができたので次はTreeViewの設定に移ります。

まずは、適当な EditorWindow を作成しましょう。

using UnityEditor;

public class ExampleEditorWindow : EditorWindow
{
    [MenuItem("Window/ExampleEditorWindow")]
    static void Open()
    {
        GetWindow<ExampleEditorWindow>();
    }
}

そして OnEnable メソッド内で TreeViewState と ExampleTreeView オブジェクトを生成します。 その際に TreeView の初期化のために Reload を呼び出しておきます。

private TreeViewState state;
private ExampleTreeView treeView;


void OnEnable()
{
    state = new TreeViewState();
    treeView = new ExampleTreeView(state);
    treeView.Reload();
}

そして TreeView を OnGUI メソッド内で呼び出します。

void OnGUI()
{
    treeView.OnGUI(new Rect(0, 0, position.width, position.height));
}

これで TreeView を表示するための準備ができました。次のコードはExampleEditorWindowの完全なコードです。

using UnityEditor;
using UnityEditor.IMGUI.Controls;
using UnityEngine;

public class ExampleEditorWindow : EditorWindow
{
    [MenuItem("Window/ExampleEditorWindow")]
    static void Open()
    {
        GetWindow<ExampleEditorWindow>();
    }

    private ExampleTreeView treeView;
    private TreeViewState state;

    void OnEnable()
    {
        state = new TreeViewState();
        treeView = new ExampleTreeView(state);
        treeView.Reload();
    }

    void OnGUI()
    {
        treeView.OnGUI(new Rect(0, 0, position.width, position.height));
    }
}

ルートとして設定した TreeViewItem オブジェクトは表示されないので注意してください。

f:id:anchan828:20161223000729g:plain

もちろん、カーソルで選択もできますしキーボードによる移動も可能です。

f:id:anchan828:20161223000949g:plain

意外と高機能

ツリー階層で表示するだけかと思いきや他2つの機能もあります。

検索機能

今回作成したサンプルを使用した場合、 treeView.searchString に検索文字を代入するだけで検索機能を使うことができます。

    void OnGUI()
    {
        treeView.searchString = EditorGUILayout.TextField(treeView.searchString);

        treeView.OnGUI(new Rect(0, 20, position.width, position.height));
    }

f:id:anchan828:20161223010456g:plain

マルチカラム機能

HTMLの table といえば想像しやすいでしょうか。テーブルのヘッダーのようなものを作成することができます。

ヘッダーは自由にリサイズすることができます。また、カラムごとにソートをすることもできます。(次の図はソート処理を書いてないため、ソートされていません)

f:id:anchan828:20161223012757g:plain

このマルチカラム機能は、まだ開発段階のような感じがしており、上記のツイートのような実装をする場合は自分でTreeViewを拡張しなければいけません。

まとめ

ツリー階層を表現するためのTreeViewがついに実装(正確には公開)され、今まで EditorGUILayout.FoldoutEditorGUI.indentLevel を駆使して作成していたツリー階層が楽に作成できるようになりました。ですが、意外と高機能なため使いこなすのには時間がかかりそうです。

TreeViewについてより詳しい情報を知りたい場合は、フォーラムを参照してください。サンプルプロジェクトもあるのでまずはそちらを動かしてみるのもいいでしょう。

New TreeView API - https://forum.unity3d.com/threads/new-treeview-api.447169/

いいか、エクセルの代わりに使うなんてことを考えるんじゃないぞ地獄を見るぞ。

Unityまとめを作成したキッカケ

Unity系の技術エントリーをまとめたWebサービス「Unityまとめ」をリリースしました。

Unityまとめの使い方は別エントリーで紹介しています。

anchan828.hatenablog.jp

Unityまとめを作成するキッカケ

Unityまとめを作るに至った経緯をつらつらと書いていきます。

キッカケその1

Unityの強みの1つとして「Unityは、わからない所があってもググれば情報を見つけられる」があります。 この強みは非常に協力で、現場では何度も助けられているかと思います。

Unityが流行りだしてから(定義が難しいけど2012年くらいから?)多くのエントリーが公開されてきました。 多くのエントリーがありますが、月日が流れるほど情報の正確性は失われていきます。 そこで私たちは、Googleの検索オプション(つまりググり力)を駆使して目的のエントリーを見つけています。

ググり力が高い人であれば問題ありませんが、みんながググり力を持っているわけでもありません。

ググり力がなくても情報を見つけられる場所が必要だ!

キッカケその2

RSSリーダーを使って日々Unity系の情報を収集していました。ですが、Unityに関係のないエントリーまで拾ってきてしまい情報の集収に少し手間がかかってしまっています。また、新たなフィードを探して登録するのも面倒です。

キッカケその3

Unity初心者が情報収集できる場所(とりあえずここ見とけばOK)というところがありません。(ちなみに質問できる場所は「Unityユーザー助け合い所」。雑談は「Unityユーザー雑談所」などがある)テラシュールブログは神

キッカケその4

Unityユーザーをターゲットとする作品を作ったけど告知できる場所が少ない(Unityユーザー雑談所に投げて終わり)

キッカケその5

的確にブログにユーザーを誘導して、アクセス数やブロガーのやる気向上を狙いたい。

キッカケその6

みんなが中毒になる毎日使ってくれるWebサービスを作りたい。

まとめ

まだまだキッカケや思うことはありますが、Unityユーザーだけではなくエントリーを書いているブロガーの皆さんのことも十分に考えてWebサービスを作成しているつもりです。(まとめ系って人の成果物を食いものにしているみたいで複雑な気持ちになりやすいんですよね...)

Unityまとめの使い方

Unity系の技術エントリーをまとめたWebサービス「Unityまとめ」をリリースしました。

unity-matome.com

Unityまとめを作成するに至ったキッカケは別エントリーで紹介しています。

anchan828.hatenablog.jp

Unityまとめとは

ネット上にある、Unity系のエントリーのみを収集して掲載したキュレーションサイトです。 毎日、Unity系のエントリーが登録されていくので情報収集をするのに役立ちます。

Unityまとめの使い方

簡単な使い方を動画で説明しています。(Youtubeニコニコ動画には同じ動画をアップしています)

最新エントリー

f:id:anchan828:20161104193400p:plain

最新のエントリーはトップページに掲載されています。毎日約25エントリーが追加されています。

https://unity-matome.com/

ランキング

f:id:anchan828:20161104193500p:plain

「今日」「週間」「月間」「全期間」という範囲でランキングを掲載しています。

https://unity-matome.com/ranking/today

カテゴリ

f:id:anchan828:20161104193841p:plain

エントリーはカテゴライズされています。特定の分野のエントリーを見たいときに使用してください。

(例)アセットバンドル - https://unity-matome.com/category/91409d34660e22362bcb2384

検索

特定のワードでエントリーを検索することができます。

f:id:anchan828:20161104194100p:plain

(例)アフィリエイト - https://unity-matome.com/search/アフィリエイト

RSS

最新エントリー・カテゴリごとにRSSを用意しています。普段RSSリーダーを使っている人は是非RSSを登録してみてください。

最新エントリーRSS - https://unity-matome.com/feed (例)エディター拡張RSS - https://unity-matome.com/category/f7a8b8e02b96a6ba28908eb0/feed

Twitterアカウント

Twitterアカウントを用意しています。 最新エントリーが登録されたときにつぶやかれる仕組みです。

twitter.com

Unityまとめの今後

ひとまず、皆さんに使っていただける最低ラインはクリアしたかな〜という感じです。でもまだまだやりたいことはあります。

「この情報古いです」ボタン

前置き: 私もライターとして情報を発信しているのでこの機能は心苦しい。

月日が流れるごとに情報は古くなってしまいます。古くなった情報を残しておくと、それを読んだユーザーは古い情報を覚えてしまい、問題解決のために多くの時間を割いてしまうかもしれません。古い情報は役に立つどころか毒になってしまう可能性があります。

そのリスクを減らすためにも、ユーザーから「このエントリーの情報は古いよ」という報告をもらうことでUnityまとめには掲載しない仕組みを作りたいと思っています。

ブックマーク(お気に入り)機能

Twitterアカウント(@unity_matome)を見ていると、ツイートをお気に入りにしている人を多く見かけました。やはり、「後で見たいとき」としてブックマークとかしたいですよね。

RSSカスタマイズ機能

Unity系のエントリーのみが配信されるRSSは便利ですが、さらにフィルタリングして特定の情報に絞りたい。

ログイン機能

ブックマーク(お気に入り)機能やRSSカスタマイズ機能を実現するには、ユーザー情報と紐付けた方が管理しやすくなります。

新しい価値のあるコンテンツ作成

いわゆるキュレーターがやるような、いろいろな情報を集めて整理して新しいコンテンツを生み出すことをしたい。「チュートリアルまとめ」とか「アセットバンドルまとめ」とか。

英語の情報を日本語でお届けもできたらいいですね。Unity AnswersやUnity Forumにはお宝情報がいっぱいです。

まとめページ

Neverまとめみたいにまとめページを作成したい。Qiitaみたいに編集リクエストとか出せるように。後々はユーザーが自由に作成できるようになれるといいな。

広告枠提供

広告枠を販売します。「Unityに関する広告」のみを掲載しようと思っています。

例えば、「Unity系のプラグイン作成」であったり「Unityイベント告知」であったり、通常の広告配信では効果の薄い部分が、Unityまとめなら最も効率よく広告配信することが可能になります(Unityユーザーしか見てないですからね)。

今のところ「インプレッション/クリック数」による課金体制と「アフィリエイト」による体制を考えています。

最も簡単で問題が起きにくいのが「アフィリエイト」。既にUnity系の広告を配信している人(企業)は A8.net などのアフィリエイトサービスを使用しているかもしれません。

もしそうであれば、私がアフィリエイトサービス経由でアフィリエイトURLを取得し、Unityまとめに「インフィード広告」として掲載することができます。

アフィリエイト経由での広告はすでに稼働しており、現在は Techacademy さまの広告が配信されています。 f:id:anchan828:20161104200801p:plain

もしアフィリエイト経由でUnityまとめに広告を載せたい!という方は お問い合せ - https://unity-matome.com/contact から連絡をお願いします。

「インプレッション/クリック数」による課金体制を今整えています。

現在、広告配信システムを作成しており、Twitter広告のように誰でも広告配信ができるようにしたいと思っています。

f:id:anchan828:20161104201705p:plain

ただ、広告配信システムはお金周りに関するものなので実装に結構時間がかかりそうです。既存の広告配信システムを探してみましたが、これが結構高額で個人では手が出せないんですよね。何かいい情報ないかな。

まとめ

Unityまとめは今後も進化していきます。Unityユーザーにとって無くてはならないサイトにしていきたいなと思っていますのでよろしくお願いします。

何かありましたら「お問い合せ https://unity-matome.com/contact 」から連絡をお願いします。

エディター拡張入門のWeb版を無償公開しました

エディター拡張入門を2015年の夏コミで販売開始してから7ヶ月が過ぎました。

anchan828.github.io

最近の売上について語ると、今までで700部ほど売れました。今は1日に1部は売れています。 売上スピードが鈍化してきている今、必要としている人たちにはある程度手に渡ったのではないかと思います。

このまま、たまに告知して書籍の売上を上げるのもいいですが、私は「これからエディター拡張を触ろうとしている予備軍のみなさん」に向けて情報を公開することにしました。

「エディター拡張で悩んだらこのサイト!」みたいな Googleで検索すると、この本のページがヒットする状態を創りあげたいと思います。

Web版の無償公開によって多くの人たちの役に立てることを願っております。 Unityのバージョンアップは驚異的な速さで行われていきますが、この本は Unity5.3 に対応しているのでまだまだ役に立ちますよ。

あ、でも電子書籍のPDF版は販売を継続しますので、よろしかったら購入お願いします! ほんとうに役に立った時にでも買ってやってください。

anchan828.github.io

ビルド後にHierarchyウィンドウのツリーの開閉がすべてリセットされてしまう問題をどうにかする

ビルド後にHierarchyウィンドウのツリー状態がすべて閉じてしまう問題

以下の仕様によって起こっている問題

  • ヒエラルキーのツリー状の開閉状態はインスタンスIDで管理されている。
  • シーンファイルを開き直すとゲームオブジェクトのインスタンスIDは変更される
  • ビルド時には自動でシーンを開きながらビルドしている。ビルド後にはビルド直前のシーンを開いている状態だが、正確には再度開き直したシーン。

対策

HierarchyWindowHelper っていうの作った。

ヒエラルキーウィンドウからツリー情報を取得して、ツリー状の開閉状態がリセットされたビルド後に再度適用する。

BuildSettingsから行うビルドは制御出来ないので、「BuildPipeline.BuildPlayer」を使ってどうにか対処する。

gist.github.com

UnityEditor上でEditorWindowごとにスクショ (Mac)

f:id:anchan828:20150211152054p:plain

こんなふうにEditorWindowまるごとスクショを撮る

表示されているEditorWindow全部スクショ

ScreenCapture.Capture<EditorWindow> ();

表示されているHogeWindowスクショ

ScreenCapture.Capture<HogeWindow> ();

なんかEditorWindow.positionがずれてる値持ってる時があるんだけどなんでだろ... (Unity4.6.2)

あれ?Application.LoadLevelAdditiveAsyncで複数シーンの非同時ロードが可能になってる?

検証で試したコード

Unity4.5ではasyncOperation2.progressは0を返し続けるけど、Unity5だと0.9を返す。

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour
{
    AsyncOperation asyncOperation1;
    AsyncOperation asyncOperation2;

    IEnumerator Start ()
    {
        asyncOperation1 = Application.LoadLevelAdditiveAsync (1);
        asyncOperation1.allowSceneActivation = false;
                
        while (asyncOperation1.progress < 0.9f) {
            yield return new WaitForEndOfFrame ();
        }

        Debug.Log ("done: asyncOperation1");
        asyncOperation2 = Application.LoadLevelAdditiveAsync (2);
        asyncOperation2.allowSceneActivation = false;

        while (asyncOperation2.progress < 0.9f) {
            Debug.Log (asyncOperation2.progress);
            yield return new WaitForEndOfFrame ();
        }

        Debug.Log ("done: asyncOperation2");
    }

    void OnGUI ()
    {
        if (GUILayout.Button ("Additive")) {
            asyncOperation1.allowSceneActivation = true;
            asyncOperation2.allowSceneActivation = true;
        }
    }
    
}