タグ別アーカイブ: Unity:Editor

icon

[Unity3d]インスペクタ上にはあるのにスクリプトからでは見つからないプロパティにアクセスする

きっかけ

Trailの色をスクリプトから変更できないか探していたところ、StackOverFlowに以下の様な質問が。

Trail Renderer Colors [C#]

I am creating a Trail Renderer that is modified in script (width, life, material, etc.) I was wondering if there is a way to change the colors array in script as well.

I’ve tried getting the game objects “colors” variable but it says that does not exist even though it is in blue.

This is what I tried:

回答として

The colors array isn’t accessible from the script API, unfortunately.

とあったりするので諦めかけつつスクロールさせると…

続きを読む

[Unity3D][Unity:Editor]Editor拡張会のまとめの続き

Editor拡張会のまとめの続き

エラい時間が空いたどころか、年が明けてしまいました。今年も宜しくお願いします。
それとは別で講習中にとっていたメモが消失してしまって問題内容とか解答とかが分からなくなってしまったのでこのシリーズいったんやめようと思いますです。
テーマやコードはupされているのでそれにリンクを張る形をとった上で今回でいちど閉じたいと思います。

[wp_ad_camp_1]

Q 3

  • お題:GenericMenu
    • EditorWindowにコンテキストメニューを表示し「CreateCube」を選択してCubeを生成しなさい

やること

  • 右クリックのイベントを取得しなければならい。
  • イベントはOnGUI内で確認できる。
  • メニューはGenericMenuオブジェクトをnewする。
  • アイテムを追加する。
  • ShowAsContext()を忘れずに。

createCubeはprimitiveを使う。

GameObject.CreatePrimitive(PrimitiveType.Cube);

GenericMenuとは何か

  • GenericMenu
    GenericMenuとはコンテキストメニューとかドロップダウンメニューとかを提供するクラス。

  • AddItemでアイテムを追加して項目を増やす。

  • AddDisableItemはグレーアウト。
  • AddSeparator
  • DropDown
  • GetItemCountでアイテム数を確認。
  • ShowAsContextで実際に表示を行う。

GUIContentとは何か

以降のお題と回答例

Q 3 Pratical

解答例

Q 4

  • お題:CustomEditor

Q 4 Pratical

Q 5

  • お題:Gizmo

Q 5 Pratical

Q 6

  • お題:Handles

Q 6 Pratical

Q 7

  • お題:ScriptableObject & AssetDatabase

Q 7 Pratical

Q 8

  • お題:PostProcessor

Q 8 Pratical

Q 9

  • お題:EditorApplication & EditorUtility

Q 9 Pratical

Q 10

  • お題:Undo & Redo

Q 10 Pratical

ありません。

C#ショートコードプログラミング 第2版 (MSDNプログラミング)

[Unity3D][C#][Unity:Editor]GUILayout/EditorGUILayout/ObjectFieldまとめ

UnityEditorの出題2とGUILayoutやらEditorGUILayoutやら

引き続き、Editor拡張の会の出題と解答を参考に作成してます。

CodeIQにも出題されているのでやってみると面白いと思います。
日本Androidの会 秋葉原支部 Unity部 安藤 圭吾さんの問題に挑戦中!

[wp_ad_camp_1]

Q 2

  • お題:EditorWindow
    • ウィンドウを出現させて各パラメータの調整項目を出現させてね!
  1. EditorWindow継承クラスをGetWindow、またはCreateWindowで出現させる。
  2. OnGUIイベント内でEditorGUILayoutを使ってパラメータの反映をさせるためのUIを作成する記述をする。

GUILayoutはUnityの用意したゲーム画面用のGUIでやたらと重いクラス。
とはいえ3Dをバリバリ使う事が無ければそれほど気にならない。
GUILayoutにEditro部分のように描画させようとしても記述が増えてしまう。

EditorGUILayoutに名前部分の記述をさせるとこうなる

this.name = EditorGUILayout.TextField("名前",this.name);
EditorGUILayoutだとこんなにシンプル。
実際に表示される内容は、項目名と入力ボックス。入力をすると自動的に参照先に上書きされる。

GUILayoutに同じ事やらせるとこうなる

GUILayout.BeginHorizontal();<br />
GUILayout.Label("名前");<br />
this.str = GUILayout.TextField("Default Name");<br />
GUILayout.EndHorizontal();

お分かりいただけただろうか…。長い。

ただ、UnityEngine内にあるし、ゲーム画面側にも描画できるので覚えて居た方が良い。
実機デバッグでむちゃくちゃ重宝する。

そしてwindow.Show();は忘れずに。

Q 2 Pratical

  • 追加:基礎編に追加でテクスチャをD&Dする矩形を設けてくださいな。
    解答例

できませんでした。

回答例から何をすべきだったかを導きだす。
– テクスチャ格納メンバの追加
– テクスチャ格納領域の表示

テクスチャはUnityEngine.Texture2Dを使用することで解決できるが、問題はDrag&Dropする領域。
EditorGUILayoutを見てみるとオブジェクトを格納できるのは1つだけ。
EditorGUILayout.ObjectFieldを参照してみる。

実際、出題中もここは確認したのだけれど、そもそも格納メンバがTexture2Dというところまで到達できていなかったので、この項目を使うべきだとは思わなかった。これはUnityが用意しているObject型に対する造詣がまだまだ甘かったということの証左。精進します。

“`
Texture2D texture;

texture = (Texture2D)EditorGUILayout.ObjectField(texture, typeof(Texture2D), false, GUILayout.Width(64), GUILayout.Height(64));

“`

回答例をみてみると、EditorGUILayout.ObjectFieldの表示サイズを変更している。
第四引数、第五引数だ。これについては後述する。

ObjectFieldの引数について

ドキュメントの丸写しだけれども。
– label
– フィールドの戦闘にだすためのラベル
– obj
– 実際にターゲットになるオブジェクト
– objType
– オブジェクトのタイプ
– allowSceneObjects
– シーン内のオブジェクトを受け入れるか否か。後述。
– options
– 次の項目で解説。

allowSceneObjectsについて

これをfalseにすると、assetの一部のオブジェクトを参照していた場合、assetはシーン内の参照を保持する事は出来ない。
– ※訳あってんのか…?

試しにGameObjectを格納することができるフィールドを用意して試してみたところ、Trueにするとシーン内(Hierarchy)内からD&Dすることができ、selectボタンをクリックした際もAssetsとSceneが選択できる。
逆にfalseにするとDrag&Dropをしようにも矩形は反応してくれないし、selectボタンをクリックしてもAssetsのみしかタブが表示されない。

つまり、そういうこと。

Gitポケットリファレンス

SVN or Git + Unity でやっておくこと

save prefabのショートカットキーを作成

prefabはApplyしてもファイルシステムへの保存が即時なされるわけではない。
変更後にすぐに git や Subversion にコミットしても変化が無かったり、空のプレファブをコミットしてしまったりで混乱を招くことがある。
これを回避するにはコミット前にUnityを終了する必要がある。
終了時に未保存のPrefabはちゃんとOS側のファイルシステムへ書き込まれる。

しかしそんなことを毎度操作してたら効率が悪いので特定のコマンドを実行するショートカットを用意する。

サンプル

      using UnityEngine;
      using UnityEditor;

      public class Util : Editor
      {
      #if UNITY_EDITOR
           [UnityEditor.MenuItem("Edit/SavePrefab %&s")]
           static void SavePrefab(){
                AssetDatabase.SaveAssets();
           }
      #endif
      }

gist

Menu Item のショートカットキー

ショートカットキーとして次の文字を特殊なキーの代わりに設定できる。
%#s で Command + Shift + s といった具合。

  • % : Command or Windows key
  • # : Shift key
  • &: Alt key

Unity Scripting Reference MenuItem

metaファイルの生成とPrefabデータのテキスト化

通常、Library下にUnityプロジェクト用ファイルが生成されるが、AssetServerを使うのでないのであれば、各Prefabに何がアタッチされているのかとか、インスペクタ上ではどんんあ値が入力されているか、とかはmetaファイルに格納される。このmetaファイルは各リソースファイルと同じところに置く必要がある。

これをしないとコミットしたプレファブのインスペクタが初期値(コードで指定した値)になる。

metaファイルの生成方法は次の通り。

Edit -> Project Settings -> EditorでInspector上にEditor Settingsが表示される。
この VersionControl の項目を MetaFiles にし、AssetSerializationForce Text にする。

これでprefabもTextベースになるのでDiffツール等で変更箇所が目視できるようになる。

Unityライブラリ辞典 ランタイム編

[Unity3D][UnityEditor]ScriptableWizardが思ったよりも使えなかった話

[wp_ad_camp_1]

ScriptableWizardとは

ScriptableWizardは、一つのスクリプトをベースにオブジェクトを生成したい時等に利用するエディタ。EditorWindowを継承しているのでEditorWindowで出来る事は全部できるけども、独自に予めUIが用意されているのでOnGUIを仕込んではいけない。

主な用途は何か?

正直、何に使えるのか良くわからない。パラメータ違いのプレファブを大量生産するのに向いてるかと言えば、createボタンでウィンドウがきえてしまうしイマイチ不明。

もしあるとしたら、メインのEditorWindowのサブウィンドウとして出現させる等の用法。
EditorWindow内の特定のボタンをクリックしたら出現し、Wizard内の特定パラメータを変動させた上でCreateをしたら値を取得させる関数をEditorWindow側に命令するとか。
だとしてもOnGUIが使えないのでどうせならサブウィンドウもEditorWindowで生成し、各種ボタンを用意した方が用途が広い。

ただ、エラーを出したり、ヘルプを出したりできるので、特定の値を厳密に入力させたり、文字列による曖昧な入力が必要な場合等は効果的なのかもしれない。
Unityで作るスマートフォン3Dゲーム開発講座 Unity4対応

サンプル

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

public class TestWizard : ScriptableWizard {

 public string gstring;
 public int gint;
 private string pstring;
 private int pint;


 [UnityEditor.MenuItem("Custom/OHGAKI/TestWizard")]
 static void WillAppear()
 {
  ScriptableWizard.DisplayWizard("Test Wizard Title in DisplayWizard method.",typeof(TestWizard),"createButtonName","otherButtonName");
 }

 void OnWizardCreate()
 {
  GameObject newGameObj = new GameObject("new GameObj");
  newGameObj.AddComponent("SphereCollider");
  newGameObj.AddComponent("MeshFilter");
        newGameObj.AddComponent("MeshRenderer");
 }

 void OnWizardOtherButton()
 {
  Debug.Log("otherButtonName clicked");
 }

 void OnWizardUpdate()
 {
  helpString = "type "abc" in gstring.";
  if(gstring == "abc"){
   isValid = true;
   errorString = "";
  }else{
   isValid = false;
   errorString = "gstring is not "abc".";
  }
  
  position = new Rect(200f,200f,300f,300f);
 }
}

サンプルの解説

 [UnityEditor.MenuItem("Custom/OHGAKI/TestWizard")]

でメニュー項目を作成し、その際の起動ハンドラを次の行に記述してるのはメニュー起動系の通例通り。

起動時のスクリプトも通例通りStaticの関数として宣言。

関数内で実行しているのは

ScriptableWizard.DisplayWizard()

で、引数が3つある場合は一つ目がタイトル、二つ目がメインのボタン、三つ目がその他のボタンの文字列としてウィンドウが生成される。

この関数は引数の数や種類に応じて色々とやってることが変化するので、リファレンスを参照。

http://ws.cis.sojo-u.ac.jp/~izumi/Unity_Documentation_jp/Documentation/ScriptReference/ScriptableWizard.html

  • OnWizardCreate()はボタン押された時に駆動するイベント。
  • OnWizardOtherButton()はotherButtonを出現させていた場合の生成されるボタンが押された際に呼び出されるイベント。
  • helpStringは常時表示用の文字列を格納する。
  • errorStringはエラーっぽい表示させたい文字列を格納する。中身が空だとエラーマークは出て来ない。
  • OnWizardUpdate()は項目の内容が変更すると駆動する。

ScriptableWizard

また、サンプルでは特定の文字が入った場合にerrorString等をアップデートしたり、ボタンをisValidをtrueにすることで有効にしてる。
isValidはfalseにしておくとボタンが押せないグレーアウト状態にさせることができる。

今日はここまで。

[Unity3D][UnityEditor]EditorWindowの最小構成からボタン配置やらサブウィンドウの生成やら

[wp_ad_camp_1]

最小構成

ファイルをProjectのRootにEditorフォルダを作成し、その中に各種エディター的機能を使用するクラスを格納させておく必要がある。
(Asset/Editor/)
EditorはUnityEditorライブラリを使用し、ウィンドウを表示するのであればEditorWindowを継承させておく。
下のコードが最小構成。画面上部にメニュー項目が追加される。
それをクリックするとデバッグログに文字列を表示する。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class _OGKEditor : EditorWindow {

 [UnityEditor.MenuItem("Custom/OHGAKI/EdtiorTest")]
 static void abcde(){
  Debug.Log("init"); 
 }

}

メニュー項目への表示は[]で囲われた範囲で、ディレクトリ階層を指定できる。
その次の行にあたるstatic関数が呼ばれる。この時の関数名は何でもいい。

Windowの出現

コードを次の様に書き換える。
変更した場所はDebag.Logから自身のクラスの型でEditorWindowによるGetWindowクラスで生成したwindowをキャストする。

public class _OGKEditor : EditorWindow {

 [UnityEditor.MenuItem("Custom/OHGAKI/EdtiorTest")]
 static void abcde(){
  _OGKEditor window = (_OGKEditor)EditorWindow.GetWindow (typeof (_OGKEditor));
 }

 void OnGUI(){
  if(GUILayout.Button("button")){
   //ボタン押した時の動作を記述
  }
 }
}

次の関数がウィンドウ生成している。サンプルは格納後に即座に開放して参照を切っているが、生成はちゃんとなされているのでOnGUI()で指定した動作は行われる。
EditorWindow.GetWindow( typeof() );
このウィンドウだけでウィンドウそのものを生成することはできるけど、各種初期設定をすることになるので、自身の型で参照を保ったまま、各種プロパティを変更しておく方が望ましい。

EditorWindowオブジェクトのプロパティやメソッドは次に一覧されている。
http://docs.unity3d.com/Documentation/ScriptReference/EditorWindow.html

プロパティ一覧

  • wantsMouseMove
    • BOOL
    • マウスイベントを取得するか否かを指定。
  • autoRepaintOnSceneChange
  • minSize
    • Vector2
    • ウィンドウの最小サイズ
  • maxSize
    • Vector2
    • ウィンドウの最大サイズ
  • title
    • string
    • ウィンドウのタブ上に表示される文字列
  • position
    • Rect
    • ウィンドウの初期位置と初期サイズ

試しに追加した初期値

 static void abcde(){
  _OGKEditor window = EditorWindow.GetWindow (typeof (_OGKEditor));
  window.minSize = new Vector2(300f,300f);
  GUIContent content = new GUIContent("__abcde__");
  window.ShowNotification(content);
  window.title = "org editor";
  window.position = new Rect(300f,300f,300f,300f);
 }

Window生成直後にNoticeを表示

初期化関数であるabcd()を次の様に追加してみる。

 static void abcde(){
  _OGKEditor window = EditorWindow.GetWindow (typeof (_OGKEditor));
  window.minSize = new Vector2(300,200);
  GUIContent content = new GUIContent("__abcde__");
  window.ShowNotification(content);
 }

すると、ウィンドウ生成直後に適当なサイズで文字列を表示できる。

UnityEditorWindowWithNotice

この文字列は数秒で消えるので注意喚起とか、複数のウィンドウを開いたり閉じたりさせる場合に現在のプレファブ名だとかを表示されることで、意識的に編集できるようになり、ヒューマンエラーを防止できる。
開いた時でなくても、windowに参照さえあれば次の様に記述してイベント事に確認したいことを表示することができる。

  GUIContent content = new GUIContent("Do not take my potato!");
  window.ShowNotification(content);

表示できるのはイベントが発生したときや独自の通知を受けた時。

EditorWindowクラス側が用意しているイベントは次の通り。

  • OnGUI Implement your own editor GUI here.
    • 描画されたときに走る。
    • ウィンドウ内のレイアウトを切ったりするのもここ。
  • Update Called 100 times per second on all visible windows.
    • 表示中のウィンドウは漏れなく秒間100回呼び出される。
  • OnInspectorUpdate OnInspectorUpdate is called at 10 frames per second to give the inspector a chance to update
    • 秒間10回の頻度で更新されるインスペクタ上のUpdate関数。
  • OnDestroy OnDestroy is called when the EditorWindow is closed.
    • windowを閉じた時に呼ばれる。
  • OnSelectionChange Called whenever the selection has changed.
    • SceneやHierarchy内でクリックする等して選択が変更される度に呼ばれる。
    • ウィンドウとは無関係で呼ばれる。
    • Selectionクラスから色々取得できる。
  • OnFocus Called when the window gets keyboard focus.
  • OnLostFocus Called when the window loses keyboard focus.
    • アクティブになるとOnFocusが、非アクティブになるとOnLostFocusが呼ばれます。
  • OnHierarchyChange Called whenever the scene hierarchy has changed.
    • ヒエラルキーが変わったら呼ばれる。ヒエラルキーの変動で読み直しすべきデータがあるのであれば読み直しを記述する。
  • OnProjectChange Called whenever the project has changed.
    • プロジェクトが変わったら呼ばれる。
    • データを読み込んで何かを表示しているのであれば、ここに読み直しの処理等を記述する。

OnSelectionChangeのサンプル

次のコードで選択しているオブジェクトのIDをずらっと表示できる。
オブジェクトを選択する都度呼ばれる。

 void OnSelectionChange(){
  foreach(int a in Selection.instanceIDs ){
   Debug.Log("id = "+ a); 
  }
 }

http://docs.unity3d.com/Documentation/ScriptReference/EditorWindow.OnSelectionChange.html

ウィンドウからさらにウィンドウを出現させる

サブウィンドウ用にクラス内クラスを用意。

 public class SubWindow: EditorWindow
 {
  public static SubWindow WillAppear(){
   
   SubWindow window = (SubWindow)EditorWindow.GetWindow(typeof(SubWindow));
   window.minSize = new Vector2(50f,100f);
   window.title = "SubWindow";
   return window;
  }
  void OnGUI(){
   GUILayout.Label("SubWindow Here"); 
  }
 }

で、親ウィンドウ側にはボタンから呼び出す様に次ぎのように記述を変更、追加する。

 void OnGUI(){
  if(GUILayout.Button("button"+num)){
   ButtonPushed();
  }
 }
 void ButtonPushed(){
  ++num;
  subWindow = SubWindow.WillAppear();
 }

SubWindowクラスに定義したWillAppearを呼び出す。
呼び出されたサブクラス側はサイズやらタイトルが差し替えられて以下のように出現する。

!!

うごごご。動かない。
エラー内容は次のとおり。

Instance of SubWindow couldn’t be created because there is no script with that name.

SubWindowのインスタンスは作られませんでしたよ。なぜならその名前でかかれたスクリプトなんざねーからだ!

サブウィンドウは別ファイルで定義

先に記述したクラス内クラスを独立したクラスとして定義しなおす。
次の様に適当なEditorWindow継承クラスを定義して、Editorディレクトリ下に入れておく。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class SubWindow: EditorWindow
{
 public static SubWindow WillAppear(){
  
  SubWindow window = (SubWindow)EditorWindow.GetWindow(typeof(SubWindow));
  window.minSize = new Vector2(50f,100f);
  window.title = "SubWindow";
  return window;
 }
 void OnGUI(){
  GUILayout.Label("SubWindow Here"); 
 }
}

UnityEditorWindowWithSubWindow

と、今日はここまで。

ゲームの作り方  Unityで覚える遊びのアルゴリズム

おまけ

自身を無限に生成しつづけるブラクラみたいのつくれないかな?と思ったのだけれどEditorWindowクラスは代々シングルトンみたいです。
以下のサンプルはボタン付きウィンドウを表示してボタンが押されると自身を生成するもの。
生成されたとたん、もともとあったウィンドウは削除される。
解りやすくする為に生成される都度、位置を変更している。

using UnityEngine;
using UnityEditor;
using System.Collections;

public class _BrowserCrasher : EditorWindow {

 public _BrowserCrasher parent;

 [UnityEditor.MenuItem("Custom/OHGAKI/bracra")]
 public static void init()
 {
  _BrowserCrasher window = initWithPosition(new Vector2(100f,100f));
  window.Show();
 }

 public static _BrowserCrasher initWithPosition(Vector2 pos)
 {
  _BrowserCrasher window = EditorWindow.GetWindow(typeof(_BrowserCrasher));
  window.position = new Rect( pos.x+10f,pos.y+10f,100f,100f);
  return window;
 }

 void OnGUI()
 {
  if(GUILayout.Button("button"))
  {
   _BrowserCrasher window = initWithPosition(new Vector2( this.position.x ,this.position.y));
   window.parent = this;
   
  }
 }
}

サンプル全容

メインウィンドウ

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

public class _OGKEditor : EditorWindow {
	
	private int num = 0;
	private SubWindow subWindow;
	
	[UnityEditor.MenuItem("Custom/OHGAKI/EdtiorTest")]
	static void abcde(){
		_OGKEditor window = EditorWindow.GetWindow (typeof(_OGKEditor));
		window.minSize = new Vector2(300f,300f);
		window.title = "org editor";
		window.position = new Rect(300f,300f,300f,300f);
		GUIContent content = new GUIContent("__abcde__");
		window.ShowNotification(content);
	}
	
	void OnGUI(){
		if(GUILayout.Button("button"+num)){
			ButtonPushed();
		}
	}
	void ButtonPushed(){
		++num;
		subWindow = SubWindow.WillAppear();
	}
	
	
	//On!!
//	void OnSelectionChange(){
//		foreach(int a in Selection.instanceIDs ){
//			Debug.Log("id = "+ a);	
//		}
//	}
//	
//	void OnFocus(){
//		Debug.Log("OnFocus");
//	}
//	void OnLostFocus(){
//		Debug.Log("OnLostFocus");
//	}
	
	
}

サブウィンドウ

using UnityEngine;
using UnityEditor;
using System.Collections;

public class SubWindow: EditorWindow
{
	public static SubWindow WillAppear(){
		
		SubWindow window = (SubWindow)EditorWindow.GetWindow(typeof(SubWindow));
		window.minSize = new Vector2(50f,100f);
		window.title = "SubWindow";
		return window;
	}
	void OnGUI(){
		GUILayout.Label("SubWindow Here");	
	}
}