- 投稿日:2019-10-02T23:05:48+09:00
uGUIよりも手前にメッシュのオブジェクトを描画したい
Canvasの設定を変更する
3種類のRender Modeを使い分ける
- Screen Space - Overlay
- Screen Space - Camera
- World Space
① Screen Space - Overlay
常にUIが最前面で描画されます。
Canvas同士の描画順はSort Orderで制御することが可能です。
Sort Orderの値が大きいほど手前に描画されますUIが最前面張り付きになるので、このモードの時にはMeshをuGUIよりも手前に描画することはできません。
MeshやParticleの描画順を前後させたい時にはScreen Space - Camera、もしくはWorld Spaceを使う必要があります。
Sort Orderが出てきた後で紛らわしいですが、Sprite RendererやParticle System、Mesh RendererにはSorting LayerとOrder in Layerという2つのパラメーターが存在します。
これによってそれぞれの描画順を制御することができます。
Sorting Layer & Order in Layer
階層としてはSorting Layerの中にOrder in Layerがあります。
なのでSorting Layerで描画順を大別し、Order in LayerでLayer内の描画順を細かく制御します。
Sorting Layerは初期状態ではDefaultしかないので自分で追加してあげる必要があります。
BackとForwardを足しました。
上にあるほど奥に、下にあるほど手前に描画されます。Sorting Layerが設定されていないと、基本的にはZ値を基に描画順は変わります。
カメラより遠いものを先(奥)に描画し、近いものは後(手前)に描画します。真ん中の画像をDefaultをBackにし、他はDefaultのままにするとこのようになります。
描画順がZ値に関係なく、Sorting Layerの順で描画されるようになりました。
全てのSorting LayerをDefaultに戻し、一番奥の画像のOrder in Layerを1にしてみます。
Order in Layerが0のものより手前に描画されるようになりました。
② Screen Space - Camera
Order in Layerを使うためにモードをScreen Space - Cameraに変更します
ここで注意が必要なのがRender CameraがNoneのままだと、Screen Space - Overlayと描画結果が変わらないままになってしまいます。
Warningに書かれているようにScreen Space - Overlayと同じ挙動になります。
Cameraを設定した状態でPlane Distanceを使うことでカメラとUIの間にMeshを描画できるようになります。
Plane DistanceとはカメラとUIの距離を指しており、この値が例えば3だとカメラとUIの間に3m分のスペースがあるのでそこに配置されたオブジェクトはUIよりも手前に描画されます。
③ World Space
これは3次元空間にUIを配置するモードなので他のGameObjectと同じようにZ値を基に描画順が制御されます。
他のモードではUIが画面張り付きだったのに対し、回転した状態で配置することもできます。
普通のGameObjectのような感覚で扱うことが可能になります。このモードでもSorting LayerやOrder in Layerを使って描画順を制御することが可能です。
uGUIよりも手前にメッシュのオブジェクトを描画するには
Screen Space - Cameraか、WorldSpaceを使うことで実現できます。
Mesh Rendererを使ったオブジェクトはSorting Layer、Order in Layerのパラメーターがインスペクター上にありませんが、スクリプトから変更可能です。
テラシュールブログさんの記事が参考になります。
UnityのSpriteとパーティクルとかモデルの描画順番についてSorting LayerやOrder in Layerを使わなくても、基本的には手前に描画されてほしいものをカメラの近くに配置すれば問題ありません。
- 投稿日:2019-10-02T19:58:59+09:00
[Unity]NavMeshを改めておさらい。
UnityのNavMeshを改めておさらいするということで簡単なEnemyもどきが目的地へ行くものを実装していきます。
1,移動できる範囲を設定しよう
地面となる床を配置していきましょう。HierarchyのCreateから3DObject→Planeです。
では早速移動できる範囲、NavMeshを実装していきましょう。
まず、Planeを選択してInspector(詳細)を表示します。
Inspectorにオブジェクトの名前が書かれている部分があります。その右にstaticと書かれている部分があり、staticの近くにある逆三角形をクリックしNavigation Staticと書かれている部分を選択します。
次に、上部にあるWindowタブからWindow→AI→NavigaitonでNavigationタブを出します。
すると、新しくNavigationタブが出てきます。
あとは簡単です。Navigationタブの4つある項目のうちBakeというものを選択します。
そして、下部分にあるBakeというボタンを押すだけです。
すると、Planeに薄い青の四角形が出てきます。
この青い部分が移動できる範囲となります。
これでNavMeshの設定が終了しました。
では次に、実際に動かしてみる、前に・・・。
カメラを少しいじります。
わかりやすいように上から観察するようにしましょう。
HierarchyのMainCameraを選択して以下のTransform情報を記入してください。
こうすると上から観察しているような視点になります。2,Enemyを目的地まで移動させてみる
まず、動いてくれるEnemyを作ります。
先ほどPlaneを生成したときと同じ要領でCapsuleを生成します。
Hierarchy→3DObject→Capsuleで生成します。
わかりやすくするためにCapsuleの名前をEnemyに変えておきましょう。
Capsuleを選択してInspectorを表示します。Inspectorの上部にCapsuleと書いてある部分があるのでそこをEnemyに書き換えます。
・・・まだ少しわかりずらいですね。色も変えてあげましょう。
Projectというタブを選択してファイルのある部分で右クリックをし、Create→Materialと選択します。
作ったマテリアル選択してInspectorからAlbedoという項目を探します。
その右側にある白い四角形をクリックして好きな色を選択してあげます。僕は赤を設定します。色を変えることができたら作ったマテリアルを押しながらHierarchyにいるEnemyまでもっていきます。Enemyの部分に青い枠が出てきたら離してあげます。すると、Enemyの色が変化しているはずです。
こんな感じに。
次に、Enemyを選択してInspectorを開き、一番下にあるAdd Component(要素を追加する)を押します。
Navigationという項目を探し、その中にあるNavMeshAgentを押して追加します。
では次にスクリプトを記入します。
先ほどと同様にEnemyを選択してInspectorを開き、AddComponentからNewScriptを選択します。
名前はNavMeshTestにしてCreateAndAddを押します。
作ったスクリプトを開いたら以下のコードを記入します。
最初から記入されているものに対して追加してある部分はコメントでわかりやすくしています 。NavMeshTest.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; //☆追加 public class NavMeshTest : MonoBehaviour { public NavMeshAgent agent; //☆追加 public GameObject target; //☆追加 目的地 // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { //目的地を設定してあげる agent.SetDestination(target.transform.position);//☆追加 } }すると、EnemyについているNavMeshTestに以下の項目が新しく追加されます。
では、このEnemyの移動先である目的地を作ります。
Create→3DObject→Cubeで四角形を出してあげてScaleをいじってあげます。Positionは床の中ならどこでも大丈夫です。(Yは0が好ましい・・・)
色が見にくいので先ほど作ったマテリアルでも適応させてあげましょう。
すると、このような感じになります。
最後に、EnemyについているNavMeshTestのNoneとなっている場所にEnemyとCubeを入れてあげます。
矢印を参考に選択しながら入れてみてください。あとは再生ボタンを押すとEnemyが目的地となるCubeへ向かっていきます。
終わり
細かく書きすぎてわかりづらくなってしまった部分もありますが、慣れてしまえば実装も簡単になります。
別の機会があったら今度は索敵してくれるEnemyを作っていきたい・・・です。
- 投稿日:2019-10-02T16:18:17+09:00
UnityでxR有効時のカメラのFOVが固定されるのを防止する
現象
Unity 2018.2.21 で、
XRSettings.showDeviceView = false
を用いて、三人称視点で撮影するカメラを置いたときにFOVが固定されてしまう現象に遭遇。原因
VR-HMDによってFOV値が変わるので、各HMD向けに最適化されたFOV値をカメラに上書きするため。
https://docs.unity3d.com/ja/2018.2/ScriptReference/Camera-fieldOfView.html対策
当該カメラコンポーネントに
RenderTexture
を割り当てれば、FOVを勝手に設定されなくなる。RenderTextureが割り当てられておらず、Displayへ表示する設定になっているとVRカメラとして認識されてしまう模様。
英語・日本語記事共に当該現象について言及している記事が見つからず、地味にハマったのでメモを残しておきます。
- 投稿日:2019-10-02T12:53:37+09:00
【Unity】CSVを読み込む
CSVを読み込むには、行を
,
でデータを区切って読み取っていくのが最も簡単な方法ですが、
データの中に,
や改行が入ると途端にめんどくなります。
そこで、CSVHelper
を用いてCSVを読み込みます。
pythonならcsvモジュールで一発なんですけどね...環境
- macOS High Sierra
- Unity 2019.1.8f1
準備
CSVHepler を準備する
NuGet Gallery _ CsvHelper から最新版のバージョンをクリックし、右の
Download package
をクリックしすると、
csvhelper.XX.X.X.nupkg
というファイルがダウンロードされます。そのファイルを右クリックし、
このアプリケーションで開く
>その他...
から/System/Library/CoreServices/Applications
内のアーカイブユーティリティ
で解凍します。次に
csvhelper.12.1.2/lib/netstandard2.0
内のCsvHelper.dll
をUnityのプロジェクトの中のAssets
フォルダ内に入れます。準備はここまで。
CSVを読み込む
ここでは例として、CSVでキャラクターのステータスの管理をしてそれを読み込みます。
用意したCSVは以下です。Name, Hp, Atk, Def player, 250, 250, 200 slime, 510, 180, 140キャラクターのステータスを管理するクラスを作ります。
Character.csusing System.Collections; using System.Collections.Generic; using UnityEngine; namespace CharacterController { public class Character { public string Name { get; set; } public int Hp { get; set; } public int Atk { get; set; } public int Def { get; set; } } }CSVを読み取って、データを読み取ります。
ShowStatus.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class ShowStatus : MonoBehaviour { void Start() { string path = @""; // CSVファイルのパス var csv = Resources.Load(path) as TextAsset; var sr = new System.IO.StringReader(csv.text); var cr = new CsvHelper.CsvReader(sr); cr.Configuration.RegisterClassMap<CharacterMapper>(); var records = cr.GetRecords<CharacterController.Character>(); foreach (var record in records) { Debug.Log($"{record.Name}, {record.Hp}, {record.Atk}, {record.Def}"); } } } public class CharacterMapper : CsvHelper.Configuration.ClassMap<CharacterController.Character> { public CharacterMapper() { Map(x => x.Name).Index(0); Map(x => x.Hp).Index(1); Map(x => x.Atk).Index(2); Map(x => x.Def).Index(3); } }
CharacterMapper
クラスで、CSVの各フィールドがCharacter
クラスのどのプロパティに当たるのかを指定します。実行
適当なゲームオブジェクトにこのスクリプトをアタッチして実行してみます。
先ほどのCSVと見比べて、問題なく出力できているのが確認できました。
- 投稿日:2019-10-02T12:01:29+09:00
[Unity] Spriteを範囲指定付きテクスチャとして使う
経緯
一枚の画像に異なるパーツの画像が入った画像の範囲を指定してテクスチャとして使用したい。どうせならUnity上で SpriteEditor を使って視覚的に範囲指定できると便利。
つまりSpriteをスプライト以外のテクスチャとしても使えれば便利!以前書いた記事 【Unity】 クォータービューのドット絵に深度バッファを適用する でスプライトを3Dモデルに投影する手法を試しましたが、この時 Texture には Sprite 用のものではなく通常のタイプを使っていました。今回は、スプライトのままで3Dオブジェクトに貼り付ける方法を試してみました。
その過程で、通常の3Dモデル用マテリアルのテクスチャにスプライトを使う方法もわかったのでまとめてみます。
Sprite から Texture 範囲を取得(マテリアル汎用)
ググってみたら、わざわざピクセルを書き出す例があったけど、そんな面倒なことしなくても、今回の目的のためには Sprite.texture と Sprite.textureRect がわかれば十分でした。
マテリアルのテクスチャに範囲を設定する
スプライトの元のテクスチャと範囲矩形がわかるので、それをマテリアルに設定してやります。
Material.mainTextureでテクスチャをセットし、Material.mainTextureOffsetとMaterial.mainTextureScaleで範囲指定します。SpriteAnime.csvar renderer = GetComponent<MeshRenderer>(); var material = renderer.material; var texSize = new Vector2(sprite.texture.width, sprite.texture.height); var rect = sprite.textureRect; material.mainTexture = sprite.texture; material.mainTextureOffset = new Vector2(rect.x/ texSize.x, rect.y / texSize.y); material.mainTextureScale = new Vector2(rect.width / texSize.x, rect.height / texSize.y);公式ドキュメントにも書いてありますが、上で使用した Material のプロパティはシェーダーの
_MainTex
を置き換えます。最後の三行はこう書いても同じです。material.SetTexture("_MainTex",sprite.texture); material.SetTextureOffset("_MainTex", new Vector2(rect.x, rect.y)); material.SetTextureScale("_MainTex", new Vector2(1 / rect.width, 1 / rect.height));メインテクスチャ以外にスプライトを使いたい場合は、上記のように名前指定で置き換えられるでしょう。
ついでにスプライトアニメーションやってみた
ただスプライトを貼るだけでは芸がないので、スプライトの配列を設定してアニメーションができるようにしてみました。
SpriteAnime.csusing System.Collections; using System.Collections.Generic; using UnityEngine; public class SpriteAnime : MonoBehaviour { [SerializeField, Header("アニメーションリスト")] private Sprite[] sprites = new Sprite[0]; [SerializeField, Header("アニメーション速度"),Range(1,100)] private float speed = 50f; // Update is called once per frame void Update() { if (sprites.Length < 1) return; var index = (int)Mathf.Repeat(Time.frameCount * speed / 100, sprites.Length); var renderer = GetComponent<MeshRenderer>(); var material = renderer.material; var sprite = sprites[index]; var texSize = new Vector2(sprite.texture.width, sprite.texture.height); var rect = sprite.textureRect; material.mainTexture = sprite.texture; material.mainTextureOffset = new Vector2(rect.x/ texSize.x, rect.y / texSize.y); material.mainTextureScale = new Vector2(rect.width / texSize.x, rect.height / texSize.y); material.SetTexture("_MainTex",sprite.texture); material.SetTextureOffset("_MainTex", new Vector2(rect.x, rect.y)); material.SetTextureScale("_MainTex", new Vector2(1 / rect.width, 1 / rect.height)); renderer.material = material; } }上記をコンポーネントとして適当なゲームオブジェクトに追加します。
結果
Shedクラスで使う
一般的なゲームオブジェクトに対する設定方法がわかったので、今度は Shed クラス(以前の記事で作ったドット絵を直方体メッシュに正射影するクラスです)でやってみます。
もともとクラス内で mesh に UV座標を設定していたので、そこを変えるだけでよいです。
以下は関連する部分だけ抜き出したものです。完全なソースはこちら(GitHub)Shed4Sprite.cspublic Sprite sprite; // ドット絵を保持するマテリアル public Material material; // マテリアルのメインテクスチャサイズ private Vector2Int texSize; // Spriteのテクスチャ領域 private RectInt spriteRect; void Start() { if (!needRestruct) return; needRestruct = true; heplMessage = null; if (fieldsNotReady()) return; spriteRect = GetSpriteRect(); texSize = GetTextureSize(); Mesh mesh = InitializeCube(); var newMaterial = Instantiate(material); newMaterial.SetTexture("_MainTex", sprite.texture); GetComponent<MeshFilter>().sharedMesh = mesh; GetComponent<MeshRenderer>().material = newMaterial; } /// <summary> /// テクスチャサイズを取得 /// </summary> /// <returns></returns> private Vector2Int GetTextureSize() { var tex = sprite.texture; return new Vector2Int(tex.width, tex.height); } /// <summary> /// Spriteのテクスチャ範囲を取得 /// </summary> /// <returns></returns> private RectInt GetSpriteRect() { var rect = sprite.textureRect; return new RectInt((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height); } /// <summary> /// pivotからの相対位置をUV座標に変換する /// </summary> /// <param name="offestX"></param> /// <param name="offsetY"></param> /// <returns></returns> private Vector2 ToUV(float offestX, float offsetY) { var x = pivot.x + offestX + spriteRect.x; var y = pivot.y + offsetY + spriteRect.y; var pos = new Vector2( x / texSize.x, y / texSize.y); //Debug.LogFormat("ToUV:({0},{1})",pos.x, pos.y); return pos; }疑似3Dサイズを自動計算させよう
Shed クラスはドット絵の疑似3Dサイズを指定するようになっていますが、スプライトの画像サイズが無駄な余白を含んでいないと仮定すれば、pivot.xと画像サイズから以下のように導き出せます。
var width = size.x + size.z; var height = size.y + width / 2);せっかく SpriteEditor で視覚的に範囲指定できるので、計算で求められるものは計算して、手入力しなければならないパラメータは極力減らしたいですね。また関連部分だけの抜き出しですが、以下のようにしてみました。
Shed4Sprite.cs[SerializeField, Header("スプライトとpivot.xからsize自動計算")] public bool autoSizeAdjust = true; // マテリアルのメインテクスチャサイズ private Vector2Int texSize; // Spriteのテクスチャ領域 private RectInt spriteRect; // Inspector 表示用の警告メッセージ public string heplMessage; private void doAutoSizeAdjust() { Debug.LogFormat("Texture size:({0},{1})", texSize.x, texSize.y); Debug.LogFormat("Sprite rect:({0},{1})-({2},{3})", spriteRect.x, spriteRect.y, spriteRect.width, spriteRect.height); pivot.y = 0; pivot.x = Mathf.Clamp(pivot.x, 0, spriteRect.width); size.z = pivot.x; size.x = spriteRect.width - pivot.x; size.y = spriteRect.height - spriteRect.width / 2; if (size.y < 0) { heplMessage = "スプライトの高さが足りません。最低でも横幅の半分以上必要です。"; size.y = 0; } } void Start() { if (!needRestruct) return; needRestruct = true; heplMessage = null; if (fieldsNotReady()) return; spriteRect = GetSpriteRect(); texSize = GetTextureSize(); if (autoSizeAdjust) { doAutoSizeAdjust(); } else if (!verifySize()) { heplMessage = "スプライトのサイズは指定の3Dサイズに必要な大きさがありません"; } //... 中略 ... // } private bool verifySize() { var width = size.x + size.z; if (spriteRect.width < width) return false; return spriteRect.height >= size.y + width / 2; }プレビューにこだわる
これで、面倒な座標入力は pivot.x だけになりました。ここまで来たらこれも WYSIWYG にしたい! エディタ拡張でプレビューをカスタマイズして、pivot 位置を視覚的に確認できるようにしてみました。
Shed4SpriteEditor.csusing System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEngine.SceneManagement; using UnityEditor.SceneManagement; [CustomEditor(typeof(Shed4Sprite))] public class Shed4SpriteEditor : Editor { Shed4Sprite shed = null; SerializedProperty sprite; SerializedProperty material; SerializedProperty pivot; SerializedProperty size; SerializedProperty autoAdjust; void OnEnable() { shed = target as Shed4Sprite; sprite = serializedObject.FindProperty("sprite"); material = serializedObject.FindProperty("material"); pivot = serializedObject.FindProperty("pivot"); size = serializedObject.FindProperty("size"); autoAdjust = serializedObject.FindProperty("autoSizeAdjust"); } public override void OnInspectorGUI() { // シリアライズオブジェクトの更新 serializedObject.Update(); EditorGUI.BeginDisabledGroup(true); EditorGUILayout.ObjectField("Script", MonoScript.FromMonoBehaviour((MonoBehaviour)target), typeof(MonoScript), false); EditorGUI.EndDisabledGroup(); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(pivot); EditorGUILayout.PropertyField(sprite); EditorGUILayout.PropertyField(material); EditorGUILayout.PropertyField(autoAdjust); if (autoAdjust.boolValue) { EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(size); EditorGUI.EndDisabledGroup(); } else { EditorGUILayout.PropertyField(size); } // シリアライズオブジェクトのプロパティの変更を更新 serializedObject.ApplyModifiedProperties(); string help = shed.heplMessage; if (help != null && help.Length > 0) { EditorGUILayout.HelpBox(help, MessageType.Warning); } if (EditorGUI.EndChangeCheck()) { shed.UpdateMesh(); } } // プレビューウィンドウを表示するかどうか public override bool HasPreviewGUI() { return true; } private bool ZoomAroundPivot { get { return shed.zoomAroundPivot; } set { shed.zoomAroundPivot = value; } } // プレビューウィンドウのヘッダーバーをカスタムする関数 public override void OnPreviewSettings() { ZoomAroundPivot = GUILayout.Toggle(ZoomAroundPivot, "zoom pivot"); } // プレビューウィンドウで描画させたいものはここで書く public override void OnPreviewGUI(Rect r, GUIStyle background) { if (ZoomAroundPivot) _drawAroundPivot(r); else _drawEntire(r); } private void _drawEntire(Rect r) { Vector2 size2D = shed.Size2D; const int border = 2; var scale = Mathf.Min((r.width - border * 2)/ size2D.x , (r.height - border * 2)/ size2D.y); var centerPos = new Vector2(r.x + r.width / 2, r.y + r.height / 2); var drawSize = size2D * scale; var size3D = ((Vector3)shed.size) * scale; var offsetPos = new Vector2(centerPos.x - border - drawSize.x / 2, centerPos.y -border - drawSize.y / 2); // 3D投影サイズ境界のx,zを赤と緑の外殻線で描く var maxPos = new Vector2(offsetPos.x + drawSize.x + border * 2, offsetPos.y + drawSize.y + border * 2); Handles.DrawSolidRectangleWithOutline(new Rect(offsetPos.x, offsetPos.y, size3D.x + border, border - 1), Color.red, Color.red); Handles.DrawSolidRectangleWithOutline(new Rect(offsetPos.x, offsetPos.y, border - 1, size3D.x / 2 + border), Color.red, Color.red); Handles.DrawSolidRectangleWithOutline(new Rect(offsetPos.x, maxPos.y, size3D.z + border, border - 1), Color.green, Color.green); Handles.DrawSolidRectangleWithOutline(new Rect(offsetPos.x, maxPos.y, border - 1, -size3D.z / 2), Color.green, Color.green); Handles.DrawSolidRectangleWithOutline(new Rect(maxPos.x, maxPos.y, -size3D.x - border, border - 1), Color.red, Color.red); Handles.DrawSolidRectangleWithOutline(new Rect(maxPos.x, maxPos.y, border - 1, -size3D.x / 2 - border), Color.red, Color.red); Handles.DrawSolidRectangleWithOutline(new Rect(maxPos.x, offsetPos.y, -size3D.z - border, border - 1), Color.green, Color.green); Handles.DrawSolidRectangleWithOutline(new Rect(maxPos.x, offsetPos.y, border - 1, size3D.z / 2 + border), Color.green, Color.green); var texture = shed.sprite.texture; var texRect = shed.SpriteRect; var texSize = new Vector2(texture.width, texture.height); //Debug.LogFormat($"({texSize.x},{texSize.y}) , ({texRect.xMin},{texRect.yMin}) -({texRect.xMax}, {texRect.yMax})"); // 中央にスプライト領域を描画 var drawRect = new Rect(offsetPos.x + border, offsetPos.y + border, drawSize.x, drawSize.y); Rect texCoord = new Rect(texRect.x / texSize.x, texRect.y / texSize.y, texRect.width / texSize.x, texRect.height / texSize.y); GUI.DrawTextureWithTexCoords(drawRect, texture, texCoord); } private void _drawAroundPivot(Rect r) { Vector2 size2D = shed.Size2D; const int border = 2; const int zoom = 3; var scale = Mathf.Min(r.width / size2D.x, (r.height - border) / size2D.y); scale = Mathf.Max(3, scale); var hfWidth = r.width / 2; var hfHeight = r.height / 2; // 3D投影サイズ境界のx,zを赤と緑の外殻線で描く Handles.DrawSolidRectangleWithOutline(new Rect(r.x, r.yMax, hfWidth, border - 1), Color.green, Color.green); Handles.DrawSolidRectangleWithOutline(new Rect(r.x + hfWidth, r.yMax, hfWidth, border - 1), Color.red, Color.red); var texture = shed.sprite.texture; var texRect = shed.SpriteRect; var texSize = new Vector2(texture.width, texture.height); //Debug.LogFormat($"({texSize.x},{texSize.y}) , ({texRect.xMin},{texRect.yMin}) -({texRect.xMax}, {texRect.yMax})"); Vector2 pivot = shed.pivot; var clipSize = new Vector2(hfWidth / zoom, (r.height - border) / zoom); Rect texCoord = new Rect( (texRect.x + pivot.x - clipSize.x) / texSize.x, texRect.y / texSize.y, clipSize.x * 2 / texSize.x, clipSize.y / texSize.y); // 中央にスプライト領域を描画 var drawRect = new Rect(r.x, r.y, r.width, r.height - border); GUI.DrawTextureWithTexCoords(drawRect, texture, texCoord); } }結果
pivot を変更すると連動してプレビューが動いて、赤と緑の枠がそれぞれ3D投影サイズの x,z の範囲を示すようにサムネイル画像外縁に表示します。さらに 'zoom pivot' にチェックすると pivot 周辺が拡大表示されてピクセル単位での位置あわせが容易にできるようにしました。
参考記事
エディタ拡張については下記を参考にさせていただきました。
http://baba-s.hatenablog.com/entry/2019/04/10/181000
https://techblog.kayac.com/unity_advent_calendar_2018_16
https://qiita.com/kyourikey/items/7a5f693d1fe17bde5387まとめ
- スプライトの texture と textureRect で元のテクスチャと引用範囲を取得できた
- マテリアルの mainTexture, mainTextureOffset, mainTextureScale で範囲指定付きでテクスチャを設定できた
- スプライトを使った3Dモデルのテクスチャアニメーションができた
- ドット絵正射影用のShedクラスもUV計算時に範囲計算を反映することでスプライトを使用できるようにした
- Shedクラスの3D投影サイズをスプライト領域から自動計算するようにした
- エディタ拡張でShedクラスのプレビューに3D投影サイズがわかりやすく表示できた
- 投稿日:2019-10-02T10:21:19+09:00
【Unity】Xcode11でiOSシミュレータ用のビルドをすると"Duplicate interface definition for class 'CAMetalLayer'"エラーになる
はじめに
いつの間にかiOSシミュレータ用のビルドができなくなって困り、いろいろ調べたメモ。
経緯と症状
Unity-iPhone > Classes > Unity > UnityMetalSupport.h > CAMetalLayer
Duplicate interface definition for class 'CAMetalLayer'
Property has a previous declaration
Property has a previous declaration
Property has a previous declaration
..
Player Settings
のGraphics APIs
の設定をガチャガチャいじってみても解決せず。再現環境
- Unity
- 2018.2.21f1
- 2017.3.1f1
- Xcode11
- Target SDK:Sumulator SDK
原因
Xcode - New Features
・CAMetalLayer is now available in Simulator. (45101325)新しいXcodeではシミュレータでも
CAMetalLayer
というのが使えるようになったらしく、これまでUnityがシミュレータ用ビルド向けにゴニョゴニョしていた部分とコンフリクトを起こしているみたいです。この問題は以下のIssue Trackerにも登録されていました。
Unityのリリースノートをたどったところ、以下のバージョンで対応済みのようです。
- 2019.2.3f1(2019/8/28リリース)
- 2018.4.9f1(2019/9/13リリース)
iOS: Fixed simulator build on xcode11 beta (metal headers were added) - now we use headers if available instead of forcibly declaring symbols (1163876, 1170516)
2017.x系については最近のリリースノートを探しても見当たらなかったので未対応だと思われます(LTSのサポート切れ?)。
対処方法
使用するUnityのバージョンを上げる
先に述べたとおり2019.2.3f1,2018.4.9f1以降では対応済みなので、素直にUnityのバージョンを上げましょう。
ただし、2018.4.9-10は不具合があるとかでアップデートするかどうかは悩みどころ。
Unity2018.4.9はUnityに不具合があるため使用しないでください。
— 時村良平 (@rodostw) September 21, 2019
引数つきのUnityEventの挙動が不安定になるため、Unity公式のEventTriggerやSliderのほか、宴のイベント設定などが正常に動作しません。 #Unity宴https://t.co/B9HaQ99Sa1 pic.twitter.com/EoQgn1ilxv引数つきのUnityEventsが使えなくなるUnity2018.4.9の不具合ですが、Unity2018.4.10でも解消されていませんでした。
— 時村良平 (@rodostw) October 2, 2019
Unity2018.4.9、Unity2018.4.10は使用しないようにしてください。 #Unity宴 https://t.co/8x7ir8a2c2古いXcodeを使う
とりあえずXcode10.3なら問題なくビルドできたので、Unityをアップデートできない人は古いXcodeを使うしかないでしょう。iOS13の対応や今後のこともあるのであまりおすすめはしません。
Xcodeが複数インストールしてあると
Build Settings
からビルドに使用するXcodeのバージョンを選択できます。
ちなみに
Xcode11ではApplication Loaderが同梱されなくなったそうです。
ビルドをApplication LoaderでApp Store ConnectにアップロードするJenkinsおじさんとかがいたら死ぬので対応しましょう。
- 投稿日:2019-10-02T03:40:43+09:00
UnityInjectorのHelloWorld
COM3D2でお馴染みUnityInjector。
適当にDLL形式のプラグインを落としてきてフォルダに突っ込めば、標準機能ではできないあんなことやこんなことができる優れものです。しかし、当然のことながら他人の作ったプラグインだけでは辛抱ならないのが男の子です。というわけで、パパっと作ってしまいたいところですが、アングラなものなこともあってかあまり情報がありません。エンドユーザー向けの情報は結構Wikiとかブログとかにまとまってるんですけどね…ということで、COM3D2でHelloWorldするUnityInjectorプラグインのチュートリアルを書き残したいと思います。COM3D2以外もパスを変えれば、多分同じ手順で行けると思います。
環境構築
dotnetがビルドできるようになればなんでもいいのですが、手っ取り早くかつ最低限の機能はそろっている.NET Coreで開発を行いたいと思います。
.NET Coreはコマンドラインツールでdotnetのプロジェクトビルドをすることができます。GUIがお好みの方はVS入れてください。同じDLLを参照させて、同じコードを書けば同じ結果になると思います。
まずは以下のリンク(Microsoft)から、.NET Core SDKをDLしインストールします。
https://dotnet.microsoft.com/downloadすると
dotnet
コマンドが使えるようになります。cmdかPowerShellで叩いてみてください。うまくいかない場合はパスが通っていない可能性があるのでパスを通しましょう。次に、COM3D2+UnityInjectorがインストールされていない場合はインストールしてください。UnityInjectorはSybarisを入れればフォルダ内に
UnityInjector.dll
があるはずです。プロジェクト生成
適当なプロジェクトフォルダを作ります。ここでは
Com3d2HelloWorld
とします。そしてdotnet
コマンドでプロジェクトを生成します。プロジェクトタイプはclasslib(直接実行しないライブラリのプロジェクト)mkdir Com3d2HelloWorld cd Com3d2HelloWorld dotnet classlibすると、
Com3d2HelloWorld
フォルダ内にCom3d2HelloWorld.csproj
及び、Class1.cs
というファイルが生成されていることが確認できると思います。参照の追加
プラグインがUnityInjector、UnityEngine、そしてCOM3D2のクラスを呼び出すために、プラグインプロジェクトに参照を追加します。尤も、今回はHelloWorldをするだけなのでCOM3D2のクラスは使用しませんが。csprojに追記します。
Com3d2HelloWorld.csproj<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> </PropertyGroup> <!-- ここから追記 --> <ItemGroup> <Reference Include="UnityInjector"> <HintPath>C:\KISS\COM3D2\Sybaris\UnityInjector.dll</HintPath> </Reference> <Reference Include="UnityEngine"> <HintPath>C:\KISS\COM3D2\COM3D2x64_Data\Managed\UnityEngine.dll</HintPath> </Reference> <Reference Include="Assembly-CSharp"><!-- COM3D2クラスが詰まってるDLL --> <HintPath>C:\KISS\COM3D2\COM3D2x64_Data\Managed\Assembly-CSharp.dll</HintPath> </Reference> </ItemGroup> <!-- ここまで--> </Project>パスは各自の環境に合わせてください。
csprojを反映させます。(要らないかも)
dotnet restoreコーディング
Class1.cs
の方にコードを書いていきます。ファイル名、クラス名に制約はありません。(衝突しなければ)
Class1というのはあまりにもなので、Com3d2HelloWorld.cs
にリネームしておきます。そして、次のようなコードを書きます。Com3d2HelloWorld.csusing UnityEngine; using UnityInjector; using UnityInjector.Attributes; namespace Com3d2HelloWorld { /// <summary> /// これはMonoBehaviourで、UnityInjectorによりInstansiateされてシーンに放り出されます。 /// 実際にゲーム内と相互作用をするにはObject.FindObjectOfTypeで所望のGemeObject(メイドさん等)を捕まえて /// そこからGameObject.GetComponentでコンポーネントを捕まえてメンバを書き換えたり実行するというフローになります。 /// PluginFilterは対象とするExeです。この場合64bit版と32bit版を指定しています。ほかにもVR版などもあります。 /// </summary> [PluginFilter("COM3D2x64"), PluginFilter("CM3D2x86"),PluginName("Com3d2HelloWorld"), PluginVersion("0.0.0.0")] public class Com3d2HelloWorld : PluginBase { void OnGUI() { // とりあえず画面にHello world!出しておく // MonoBehaviourなのでOnGUIやUpdate等、Unityのコールバック関数が自由に使えます GUI.Label(new Rect(100, 100, 500, 100), "Hello world! [8846]"); } } }コンパイル&デプロイ
以下のコマンドを実行してビルドします。
dotnet buildすると、
Com3d2HelloWorld\bin\Debug\Com3d2HelloWorld.dll
が出来上がっている筈です。これをCOM3D2のプラグインとして配置してあげればOKです。おそらくSybaris\UnityInjector\
配下ですね。確認
COM3D2を起動します。コンソールに次のように表示されていることを確認します。
Loading Assembly: 'C:\KISS\COM3D2\Sybaris\UnityInjector\Com3d2HelloWorld.dll'ゲーム画面を見ると…
まとめ
要するに
UnityEngine.dll
UnityInjector.dll
Assembly-CSharp.dll
を参照し
PluginFilterAttribute
PluginNameAttribute
PluginVersionAttribute
のついた
PluginBase
派生のMonoBehaviourなクラスを含むDLLを作れば大丈夫です。参考になりそうなの
https://umaiumeunion.github.io/guides_unityinjector/03_writing_plugins
多分これがすべて(書き始めて気づいた)