UNITY メニュー拡張 part2 値を保存する
はじめに
UNITYのメニュー拡張をし Windowの表示は出来ました
ところが、このままでは一度ウインドウを閉じたあと再び開いても設定した値がデフォルトに戻ります
前回設定した値が残っていて欲しい時は多いと思うので、その設定です
方針
EditorWindowの値をファイルに保存すれば良いが、UNITYにはそれを補助する機能がある
ScriptableObjectを継承していれば簡単に yamlの形でAssetとして保存出来る
アセット作成
まず保存するためのAssetを作る必要があるが、おそらくコードで作成させることも出来るとおもうが
エディタ拡張でぱぱっとやってみる
まず保存するデータのクラスを ScriptableObjectを継承し作成する
[CreateAssetMenu] public class TestObject : ScriptableObject { [SerializeField] public string hoge_; }
[CreateAssetMenu] のアトリビュートをつけると、メニューの Asset->Create に表示されるようになる
つまり メニューから、保存先のAssetを作成する
ScriptableObjectを継承するのはSerializeのためである
今回は string hoge_ を保存する
とりあえず上記で 保存するオブジェクトは出来た
シリアライズ&デシリアライズ
先ほど作った TestObjectを使い シリアライズ、デシリアライズを行う
まず、アセットが読みこまれてない場合は読みこむ
if (this.data == null) { this.data = LoadData(); }
中身は、アセット一覧より TestObjectのものを探す
そして、データのアクセスは TestObjectを経由する
EditorGUI.BeginChangeCheck();
EditorGUI.EndChangeCheck();
にて、GUIの値が変更された時のみ、Assetに書き込む
data.hoge = hoge;
EditorUtility.SetDirty(this.data);
public class TestWindow : EditorWindow { [SerializeField] private TestObject data; [MenuItem("Tools/Test")] public static void ShowWindow() { EditorWindow.GetWindow(typeof(TestWindow)); } void OnGUI() { if (this.data == null) { this.data = LoadData(); } EditorGUI.BeginChangeCheck(); string hoge_ = data.hoge_; hoge_ = EditorGUILayout.TextField("HOGE", hoge_); if (EditorGUI.EndChangeCheck()) { data.hoge_ = hoge_; EditorUtility.SetDirty(this.data); } } static TestObject LoadData() { return (TestObject)AssetDatabase.FindAssets("t:ScriptableObject") .Select(guid => AssetDatabase.GUIDToAssetPath(guid)) .Select(path => AssetDatabase.LoadAssetAtPath(path, typeof(TestObject))) .Where(obj => obj != null) .FirstOrDefault(); } }
これで簡単だが、EditorWindowの値をシリアライズさせることができる
UNITY メニュー拡張 part1
コード
https://github.com/YukiMiyatake/UnityLesson/tree/MenuItem_1
はじめに
UNITYでは、インスペクターやメニューを簡単に拡張できる
エディタ拡張により開発効率も大幅に上げる事が可能である
面倒でもこの手の機能は初期に作っておけば、その後の開発期間が大幅に短縮される事が多い
そのため、ぜひとも覚えておきたい
メニュー拡張基本
上記のように メニューにオリジナルの項目を追加できる
メニューは階層化出来、メニュー選択でウインドウ表示することが多い
まず、インスペクタやメニュー等の拡張を エディタ拡張と呼び
Editorフォルダ以下にスクリプトを置く必要がある
メニューの場合は クラスメソッド(static関数)に MenuItemアトリビュートをつければいい
Assets/Menu/Editor/TestMenu.cs
using UnityEngine; using UnityEditor; public class TestMenu { [MenuItem("Test/Menu %a")] public static void ShowMenu() { Debug.Log("ShowMenu"); } }
MenuItemの第一引数に メニューの階層を指定する。
また、文字列に %a などを入れると、ショートカットキーを設定できる
そのあたりの詳細は是非マニュアルを見て欲しい
非常に簡単にメニュー拡張が出来た
MenuItemの引数
MenuItemは3つの引数をもてる
第一引数はString型。メニューの項目名で、ショートカットキーも設定できる
第三引数はint型。表示の優先度
問題が第二引数
同じ名前のメニュー項目がある場合、メニュー表示時に第二引数がtrueのものが先に呼ばれ
falseを返すとそのメニュー項目はDisableになり、trueの場合はEnableになりクリックするともう一つの関数が呼ばれる
つまり メニュー表示時にValidationを行う事ができる
例えば、選択中のオブジェクトにScriptなりコンポーネントを自動で追加するメニュー拡張をしようと思った場合
GameObject以外を選択していたら 無効にすることができる
また、MenuItemの関数には、MenuCommandという引数を取る事が可能
調べたところ、このMenuCommandは Hierarchyの ルートが取れるようだ。まだ何に使うかわかってない。再調査が必要
Selection.activeObject で、現在選択中のオブジェクトが取得できる
ので、今回は下記のものを作る
Test/AddObject %b メニューを作り、選択しているものがGameObject以外は無効にする
GameObjectの場合は
MenuCommandという名前のGameObjectを MenuCommand.contextを親にして作成
NewGameという名前のGameObjectを Selection.activeObjectを親にし作成し RidgedBodyをつける
共にUndoに登録(これをしなければ Ctrl+Zで取り消せない)
[MenuItem("Test/AddObject %b", true)] static bool AddObjectValidate() { // GameObjectか? return Selection.activeObject.GetType() == typeof(GameObject); } [MenuItem("Test/AddObject %b", false)] static void AddObject(MenuCommand command) { GameObject objMenuCommand = new GameObject("MenuCommand"); // Undo登録 Undo.RegisterCreatedObjectUndo(objMenuCommand, "Test_AddObject " + objMenuCommand.name); GameObjectUtility.SetParentAndAlign(objMenuCommand, command.context as GameObject); GameObject obj = new GameObject("NewGame"); GameObjectUtility.SetParentAndAlign(obj, Selection.activeGameObject ); // Undo登録 Undo.RegisterCreatedObjectUndo(obj, "Test_AddObject " + obj.name); Undo.AddComponent<Rigidbody>(obj); }
実行の結果、MenuCommandは Hierarchyルートに、NewGameは選択したGameObject(ここではTest)の下に出来た
なお GameObject以外を選択した場合には MenuがDisableする事も確認できた
UNITYシェーダー研究 Vertexライティング?
コード
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_7
はじめに
前回まではピクセルシェーダにてライティング計算をしていました
これはクオリティの高いライティングになりますが、少し処理が重くなるので
ディフューズカラー、スペキュラを頂点シェーダで計算しようという計画
シェーダー入出力の説明
今まで説明省略してたのを大雑把に
頂点シェーダ(vert)、フラグメントシェーダ(frag)には引数がある
頂点シェーダの引数は構造体で表現し、頂点ごとのパラメータが入ってくる
例えば ポジション、法線、UVなど
そして頂点シェーダの返り値も構造体で、頂点毎の情報を設定する
そして 頂点毎にフラグメントシェーダに
その時 ポジション(SV_POSITION)以外は線形補完されます
UVが保管され、テクスチャマッピングが正しく出来るのはわかると思うが
法線なども補完され、ライティングがスムースになります
シェーダ引数は、型のほかにセマンティクスがあり、目的別に指定します
細かい事は説明しません
たとえば前回は
struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float4 vertexW: TEXCOORD0; float2 uv : TEXCOORD1; float3 normal : TEXCOORD2; };
頂点シェーダの引数は ポジション、法線、UV座標
フラグメントシェーダ引数は ポジション、ワールド座標でのポジション、UV、法線
フラグメントシェーダ引数はピクセルごとに補完される
方針
1個目のライト(ForwardBase)は現状通りのピクセルでのライティングを行う
2個目以降のライト(ForwardAdd)は、頂点シェーダでライト計算を行い ピクセルシェーダでは補完された値を使う
少し精度はわるくなるが、計算量は削減されるはずである
具体的には ディフューズの値を補完することができる
スペキュラに関しては補完することも可能であるが、大きくクオリティが落ちる可能性がある
今回は例として スペキュラも補完して軽くしよう
コード
具体的には ディフューズとスペキュラの計算を頂点シェーダに持っていく
ディフューズとスペキュラの値を フラグメントシェーダに渡す
構造体は下記になる
struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD1; float3 diffuse : TEXCOORD2; float3 specular : TEXCOORD3; };
頂点シェーダの入力に変更なし
フラグメントシェーダの入力は
仕様にて vertexは削れない
ワールド座標のポジションは不要
法線も不要
ディフューズ値とスペキュラ値を追加
v2f vert(appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); float4 vertexW = mul(unity_ObjectToWorld, v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); float3 normal = UnityObjectToWorldNormal(v.normal); float3 L = normalize(_WorldSpaceLightPos0.xyz); float3 V = normalize(_WorldSpaceCameraPos - vertexW.xyz); float3 N = normal; float3 H = normalize(L + V); float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i); float3 NdotL = dot(N, L); o.diffuse = (NdotL*0.5 + 0.5) * lightCol; o.specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz * lightCol; // Half vector return o; } float3 frag(v2f i) : SV_Target { // texture albedo float4 tex = tex2D(_MainTex, i.uv); return i.diffuse * tex + i.specular; }
フラグメントシェーダで計算していたディフューズとスペキュラを頂点シェーダで計算
フラグメントシェーダでは 補完されたディフューズとスペキュラをそのまま利用する
通常
軽量化
心配になるぐらい 変わらなかった(心配
UNITYシェーダ研究 複数ライトの影響
コード
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_6
前回まで
前回はAmbientも入れて だいたいライティング出来た
ただ、複数ライトを置いても1つ目のライトの影響しか受けない
ので、複数ライトの影響をうけるようにする
ライトの強さ
複数ライト対応するまえに、今までのライトではライトカラーの影響はうけるが
ライトの強さの影響が入っていないので追加する
ライトの種類により減衰しないライト(DirectionalLightなど)と 減衰するライト(PointLight等)があり
それらをまとめて計算するマクロが存在するので それを使う
AutoLight.cginc をIncludeし、LIGHT_ATTENUATION(i) でライトの強さが取得できる iはピクセルデータ
それをライトカラーに乗算することで、強さを反映したライト色が計算できる
float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);
ライトカラーは、ディフューズとスペキュラに影響するので、それぞれに乗算する
float3 diffuse = (NdotL*0.5 + 0.5) * lightCol; float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz * lightCol; // Half vector
複数ライトに対応
UNITYでは、キャラに最も影響の強いライトは LightMode=ForwardBaseで計算するが
その他のライトは LightMode=ForwardAddで入ってくる
ので、ForwardAddのパスを増やせばいい
シェーダーの中身は 今回はほぼ、ForwardBaseのものをコピーする
ただし、Ambientは1回しか計算してはいけないので ForwardAddでは計算行わない事と
BlendModeを One Oneなどにしなければ、合成されない
BlendMode設定なし
メインライトのみ
ライト2個(赤ライト追加)
()
複数ライトが反映されるようになった
UNITYシェーダ研究 Ambient
コード
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_5
前回のシェーダ
ディフューズとスペキュラを実装し、だいぶライティングができた
テクスチャマッピングもしている
でも、Ambientも与えていません
Ambientの設定
UNITYのバージョンにより違うけど
最近のバージョンでは
Window-Lighting-Setting から行う
Environment Lightingのあたり
Ambientの取得
Ambinetカラーの取得は定数が用意されているので簡単だ
UNITY_LIGHTMODEL_AMBIENT.rgb;
これを計算式に加えるが
テクスチャ * Ambientの値を今までのものに足せばよいので最終的には
最終カラー = Ambientテクスチャ + Diffuseテクスチャ + スペキュラ
= (Ambient+Diffuse) * テクスチャ + スペキュラ
にした
float3 frag(v2f i) : SV_Target { float3 L = normalize(_WorldSpaceLightPos0.xyz); float3 V = normalize(_WorldSpaceCameraPos - i.vertexW.xyz); float3 N = i.normal; float3 H = normalize(L + V); //Ambient float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; // texture albedo float4 tex = tex2D(_MainTex, i.uv); // Diffuse(HalfLambert) float3 NdotL = dot(N, L); float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb; // Speculer // float3 specular = pow(max(0.0, dot(reflect(-L, N), V)), _Spec1Power) * _Spec1Color.xyz; // reflection float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz; // Half vector return (ambient + diffuse) * tex + specular; }
カレーハッキング
これからは ニジマスの手に入るシーズンになるので、ニジマスカレーを極めようとおもう
1回作ったけど 普通に美味しいのだけど、これからさらにプラスしていく
まず前回はゴールデンカレールウを使った。普通に美味しいけど、ビーフ等にあう、濃厚なルーであった
また スパイスも自分で頑張りたいので、スパイス研究科 中村繭子パイセンや友達のアドバイスもらいつつ
自分のニジマスカレーを完成させたい
知見
一度作った時の知見であるが、野菜の多いカレーが好きだが、歯ざわりや触感が統一されてないと ん?? ってなる
例えばヤングコーン入れたがそれだけ固く ん?? だったので、煮込む野菜以外は 蒸し野菜でトッピングする
ニジマスはおろして骨などを取り除くと食べやすいが、身がくずれたり、骨のエキスが美味しいので、いまはぶつ切りで考える
将来的には、あら汁と分けたり、トッピングで乗せ身が崩れなくするなど 考えたい
デミ系のカレー好きだけど、私の求めるものではないので、ルウにと溶かす野菜は慎重にえらぶ
次への
インドのベンガル地方は川魚のカレーが多いので参考にする
川魚用のスパイス
フェンネル、クミン、ニゲラ、ブラウンマスタード、フェニュグリーク
魚はターメリックと塩とレモン汁で漬けておいて使うこと多い
酸味苦手ではあるが、酸につけて少し骨を柔らかくできるといいな
カレー 上野不忍池 サルガム
何か料理を極めたいのでカレーにしてみた
カレーは好きだし、みんな食べられる
前提として小麦アレルギーなので、ご飯にあうカレー
お米はジャポニカ米がベストだけど、場合によればインディカ米買ってもいい
今は色々なものを作るのが先 敵を知るにはまず己を知る必要がある
不忍池近くの インド&ネパール料理
こういう店は、インド料理ではなくネパールがメインのはず
とりあえず すべて入ってるコース頼む
バターチキンカレー、キーマカレー、サラダ、デザート、ナン、ライス、シシカバブ、タンドリーチキン、ラッシー
バターチキン、キーマカレーは甘い。辛口MAXだけど 私は辛さには強いので物足りないが、とにかく砂糖甘さ
デザートはインドらしく 激甘
ナン、ラッシーは甘さ控えめ
ナンは甘いのが好きだったので、残念
カレーも 味は普通だけど、好みというか目指す方向と大きく違う
そして 小麦アレルギーで速攻おなか壊し、営業に送れるという ダメなパターン・・