読者です 読者をやめる 読者になる 読者になる

太郎Work

Unityとかで困ったこと等を残しておきます

Unityを使って水っぽい表現作ってみた

Unity 3D 作ってみた

今GravityDAZE2をやっているのですが、水表現が面白いことになっていたので自分でUnityを使って実装してみました


背景が殆ど無いので分かりにくいと思いますが…

シェーダ

モデルの裏側をキャプチャしたものを使用して法線で歪ませて描画する方法で実装しました

GrabPass { "_WaterGrab" }

GrabPassを記述すると描画直前のバッファを_GrabTextureにコピーしてくれますが、上のようにTexture名を指定することでこのGrabPassが初めて呼ばれたときのみコピーされるみたいです。

例えばこのシェーダで4個メッシュを描画すると1個めが描画されたときのみGrabPassが走り、2個め以降は1個めで使用したGrabTextureを再利用する
これで負荷を最小限にできます!

あとは一般的なスクリーン投影計算とリム計算ぐらいです。

half kScale = 1.7777;
half2 enc;
enc = i.viewNormal.xy / (i.viewNormal.z+1);
enc /= kScale;
i.viewNormal.xy = enc * 0.5;

フラグメントシェーダ内のこの処理は法線方向をスクリーンでいい感じに見えるように変換する計算式です
UnityCG.cgincのEncodeViewNormalStereo関数を持ってきました
Unityの法線テクスチャ描画時に呼ばれている関数なのですが、使ってみたら結構いい感じになったのでこれにしました。

Cloth

水の揺れ表現にはClothを使ってみました
f:id:tarowork:20170130022406p:plain

パラメータはちょこちょこっと変えていい感じにして外部から少しだけ力を加えて動くようになっています。
頂点設定は一部の頂点を動きにくい設定にして凸凹感が出やすいようにしました。
多分ここをきれいに設定するともっと柔らかい歪みになると思います。
0.1だと動かなすぎるし大きい値だと動きすぎて本来の一から離れすぎてしまうので0.15~0.2ぐらいがちょうどよかったです

Cloth工夫次第でいろいろできそうな予感がします


これ使って何か作りたい

UnityEditor再生開始時のイベント取得

Unity UnityEditor

UnityEditor再生時のイベント

実行、非実行時でモデルプレビュー出来るツールを作成しているのですが、再生時にモデルを一旦リセットして再生成するスクリプトを作成したい

ということでこんなスクリプトを書くと

f:id:tarowork:20160301174309p:plain

ここのボタンを押す度にOnPlayModeStateChangedメソッドが呼ばれるようになります

void OnEnable ()
{
    EditorApplication.playmodeStateChanged += OnPlayModeStateChanged;
}
void OnDisable ()
{
    EditorApplication.playmodeStateChanged -= OnPlayModeStateChanged;
}
void OnPlayModeStateChanged ()
{
    Debug.Log("Changed");
}

ここで問題なのが思っているよりも多くこのイベントが発行されるということです しかし、このメソッド内で

EditorApplication.isPlaying

EditorApplication.isPlayingOrWillChangePlaymode

の値を見るとより詳細なStateが確認できます

  1. 再生ボタン押下
  2. OnChanged : isPlaying=false, isPlayingOrWillChangePlaymode=true ※このタイミングで生成すると実行開始時にCleanupエラーが出る
  3. 開始するまで少し待つあれ 実行ログ出力開始
  4. OnChanged : isPlaying=true, isPlayingOrWillChangePlaymode=true ※実行開始、生成すると通常通り動作する
  5. 停止ボタン押下
  6. OnChanged : isPlaying=true, isPlayingOrWillChangePlaymode=false ※停止開始、生成するとあれ
  7. 完全停止待つ
  8. OnChanged : isPlaying=false, isPlayingOrWillChangePlaymode=false ※完全停止Editorの挙動開始

つまりisPlaying==isPlayingOrWillChangePlaymodeを見ることで実行、Editorに完全移行したかどうかが取れ、逆の場合は遷移する直前の処理が書けます

このままだとPauseボタンでも呼ばれてしまうので上記の条件が一致したらフラグを立てておくことでPauseを無視することができます でもOnDisableとOnEnableだけで上手く頑張れば出来るっぽい雰囲気 とりあえずこのプロパティと組み合わせれば厳密にStateを取れたという感じ

gist.github.com

Unity5.3でExecuteInEditModeとDontDestroyOnLoadを同時に使用した際の挙動

Unity5.3にアップデートしてかなり発見しにくいバグ?を見つけたのでメモ

問題

[ExecuteInEditMode]
public class Test : MonoBehaviour {
    void Awake() {
        DontDestroyOnLoad (gameObject);
    }
}

こんなクラスがあった時にEditor上でHierarchyにスクリプトを追加しようとすると表示されず、GameObject毎どこかに行ってしまいます。 FindObjectOfTypeを使用するとどこかに存在していることは分かるのですが、困ったことにどこにも見つかりません。

原因

原因はDontDestroyOnLoadの挙動が変更されているからのようです。 このメソッドが呼び出された直後、普段は隠蔽されている”DontDestroyOnLoad”シーンにGameObjectが移動される挙動になっていました。

gameObject.scene.name

でログを出力するとDontDestroyOnLoadシーンに飛ばされていることが分かります。

このシーンは実行直前にHierarchy上で確認することが出来ます。 f:id:tarowork:20151215221022p:plain

(実行が開始すると消えるので動画キャプチャから抜き出しました)

解決策

解決策としては実行している時のみ指定をすれば一応動作します

if (Application.isPlaying) {
    DontDestroyOnLoad (gameObject);
}

滅茶苦茶ハマりました…

ScriptableObjectのメンバリネーム

C# Unity UnityEditor

ScriptableObjectを使用している時に変数定義を変えたくなることがあると思います。
例えばpublicで定義していたけどやはりprivateのSerializeFieldにしてスクリプトからは読み込み専用にしたくなった等
その時は

[System.Serializable]
public class Data
{
  [SerializeField]
  [UnityEngine.Serialization.FormerlySerializedAs ("name")]
  private string _name;
}

これで変数名の移行が行われます。
この状態でプロジェクトの保存を行うと_nameに値が移行されていることを確認することが出来ます。
全ての関連ファイルの変更が行われたことを確認できたらFormerlySerializedAs属性は消しても大丈夫です。

※これに限らないですがPrefabと同じようにSaveProjectを実行しないと見た目は差し替わっていてもファイルに書き込まれていないため、書き込まれていることを確認してから属性削除は行う必要があります。

コマンド引数を指定しつつUnityを複数起動する方法

Unity

クローン後の初プロジェクトオープン時PC&Macでプロジェクトが開かれてしまい、そこからiOSにswitchPlatformするのも時間がかかりすぎるのでコマンドラインから起動する方法
そして複数起動も出来る方法です

open -n /Applications/Unity/Unity.app --args -buildTarget iPhone -projectPath projectpath

--args をつけるのがミソみたいです。

ターゲットの周りをくるくる回るサンプル

Unity

なんとなく作ったので記事にします
f:id:tarowork:20150630191903g:plain:w150
gist.github.com

ターゲットの周りを常に一定間隔で回るサンプルです。カメラのコンポーネントに付けてみてください
クォータニオン先輩使えば回転量定義できて座標変換が簡単に実装できます。

UnityEditorのObjectFieldでフォルダのみ入れる

UnityEditor Unity

もっと簡単な方法あれば教えて下さい><

Object folder;
var newFolder = EditorGUILayout.ObjectField (folder, typeof(Object), false);
var path = AssetDatabase.GetAssetPath (newFolder);
if (AssetDatabase.IsValidFolder (path)) {
	folder = newFolder;
} else {
	folder = null;
}

どこかにありそうですが、パッと見つからず、試しにそれっぽく使ってみたら上手く行ったのでメモとして残しました。