Unity EditorWindowでPreviewRenderUtilityを使ってみる
みなさんこんばんわ。ちょびえです。今日はUnityのエディタでドハマリしたTipsでも書いておこうかと思います。
PreviewRenderUtilityを使ってメッシュを書いて、マウスイベントを扱ってみよう
UnityのEditorではPreviewRenderUtilityを使うことで任意のカメラ姿勢で描画~テクスチャの取得をすることができます。これを使ってSphereを描画しつつ、ついでにマウスクリックした座標からWorld座標をとってみよう、というのが今回のTipsです。
それでは、さくっとコード行きましょう。
コード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
using UnityEngine; using UnityEditor; public class Preview : EditorWindow { [MenuItem("Example/PreviewRenderUtility")] public static void Init() { var w = EditorWindow.GetWindow<Preview>("Preview"); w.minSize = new Vector2(640, 640); w.wantsMouseMove = true; } private bool init; private PreviewRenderUtility renderer; private Vector2 size = new Vector2(640, 640); public void OnGUI() { if (!init) { renderer = new PreviewRenderUtility(); init = true; } var rect = new Rect(0, 0, size.x, size.y); // PreviewRenderUtilityを使ってPreviewを開始する。 // 毎回カメラ設定がリセットされるのでセットしなおす renderer.BeginPreview(rect, GUIStyle.none); renderer.m_Camera.farClipPlane = 100; renderer.m_Camera.transform.position = new Vector3(0, 20, 0); renderer.m_Camera.transform.rotation = Quaternion.Euler(90, 0, 0); renderer.m_Camera.clearFlags = CameraClearFlags.SolidColor; // 適当にプリミティブを描画する // PreviewRendererUtilityはそのままGameObjectの描画もできますが、せっかくですしDrawMeshのAPI使ってみましょう! var primitivePosition = Vector3.zero; var prim = GameObject.CreatePrimitive(PrimitiveType.Sphere); var mesh = prim.GetComponent<MeshFilter>().sharedMesh; var mat = prim.GetComponent<MeshRenderer>().sharedMaterial; renderer.DrawMesh(mesh, primitivePosition, Quaternion.Euler(0, 0, 0), mat, 0); // クリック時に座標をログに表示する if (Event.current.type == EventType.MouseDown) { Vector3 mousepos = Event.current.mousePosition; // 左下が0なのでなおす。DrawTextureのx,yが0,0でなければよしなにoffsetつけて治してください。 mousepos.y = rect.height - mousepos.y; // ※デフォルトのカメラサイズが1024x1024でテクスチャのサイズと違うので補正する Vector2 ratio = new Vector2(rect.width / renderer.m_Camera.pixelWidth, rect.height / renderer.m_Camera.pixelHeight); mousepos.x /= ratio.x; mousepos.y /= ratio.y; // カメラから対象物の距離をいれる mousepos.z = Vector3.Distance(renderer.m_Camera.transform.position, primitivePosition); var result = renderer.m_Camera.ScreenToWorldPoint(mousepos); Debug.LogFormat("ScreenToWorldPoint: {0}, {1}", result, mousepos); } // レンダリングしてテクスチャとって描画 renderer.m_Camera.Render(); var tex = renderer.EndPreview(); GameObject.DestroyImmediate(prim); GUI.DrawTexture(rect, tex); } } |
上記ファイルをEditorディレクトリに保存して、メニューのExample/PreviewRenderUtilityを実行すると下記のような画面が表示されるとおもいます。
ちゃんとSphereが描画されていますねー。
描画領域をクリックするとWorld座標をログに表示するようにしています。
ドハマリポイント
どうやらPreviewRenderUtilityのカメラの内部サイズ(pixelRect)は1024x1024がデフォルトとなっているようでして、そのままxyの座標系を直したVector3を使ってScreenToWorldPositionを実行してしまうとViewPortで正規化した際にがずれてしまうようです。
やり方はなんでも良いのですが、マウスでクリックした座標をカメラのサイズと合わせることでScreenToWorldPositionで正しいWorld座標が取れるようになります。
最初やったときは意図した値がとれなくて大分はまってしまったので、みなさまの助けになれば。
でわでわ。