ものららい 2022/01/28 17:00

UnityでQuadのテクスチャUVを切り替える的なやつ


ちょっとゲーム開発がBGMで止まっていてネタがないので、振り返りもかねて自分のために今まで作った機能についてのおさらいしようかなと思います。
※あくまで自分のためのやってることなので、コードが見にくかったり、コードの書き方の癖が…とか、そもそももっといい方法があるとか、説明文がワカメ的なのは優しく流してください。🐄



今回はUnityでQuadのテクスチャUVを切り替える的な処理について。

とりあえずソース。
MeshUvControl.cs

using UnityEngine;

#if UNITY_EDITOR
using UnityEditor;
#endif

//QuadメッシュのUV操作
public class MeshUvControl : MonoBehaviour
{
	//広げ設定
	float _spread = 0;
	
	//マスの位置とマスの最大数
	public int offsetX = 0;
	public int offsetY = 0;
	public int lengthX = 1;
	public int lengthY = 1;
	
	//設定保存用の変数なのでエディタ内では非表示
	[HideInInspector] public int saveOffsetX = 0;
	[HideInInspector] public int saveOffsetY = 0;
	[HideInInspector] public int saveLengthX = 0;
	[HideInInspector] public int saveLengthY = 0;
	
	Mesh _mesh;
	Vector2[] _uv;
	float _uvWidth;
	float _uvHeight;
	
	
	//エディタの時のみ動作
	#if UNITY_EDITOR
	void OnValidate ()
	{
		EditorApplication.delayCall = () => 
		{
			if(!EditorApplication.isPlaying)
			{
				Init();
			}
		};
	}
	#endif
	
	
	void Awake ()
	{
		Init();
	}
	
	
	//初期化
	void Init ()
	{
		//メッシュの取得
		MeshFilter _meshFilter = GetComponent<MeshFilter>();
		_mesh = _meshFilter.sharedMesh;
		
		//メッシュが無い場合か初回セットの時はメッシュを生成する
		if(_mesh == null || _mesh.name != "DummyMesh")
		{
			//メッシュの生成
			_mesh = new Mesh();
			_mesh.name = "DummyMesh";
			
			//頂点
			Vector3[] _vertices = new Vector3[4];
			_vertices[0].Set(-0.5f, -0.5f, 0);
			_vertices[1].Set(0.5f, -0.5f, 0);
			_vertices[2].Set(-0.5f, 0.5f, 0);
			_vertices[3].Set(0.5f, 0.5f, 0);
			_mesh.vertices = _vertices;
			
			//頂点インデックス
			_mesh.triangles = new int[] {0, 3, 1, 3, 0, 2};
			
			//法線
			Vector3[] _normals = new Vector3[4];
			_normals[0].Set(0, 0, -1);
			_normals[1].Set(0, 0, -1);
			_normals[2].Set(0, 0, -1);
			_normals[3].Set(0, 0, -1);
			_mesh.normals = _normals;
			
			//メッシュのセット
			_meshFilter.sharedMesh = _mesh;
			
			//設定保存用の変数初期化
			saveOffsetX = 0;
			saveOffsetY = 0;
			saveLengthX = 0;
			saveLengthY = 0;
		}
		
		//UV更新用配列
		_uv = new Vector2[4];
		
		//あらかじめ一マスの大きさを求める
		_uvWidth = (1f / (float)lengthX) - _spread;
		_uvHeight = (1f / (float)lengthY) - _spread;
		
		//UV更新
		UpdateUV();
	}
	
	
	//UV更新
	public void UpdateUV ()
	{
		//一つでも設定が違っていれば処理
		if(
			offsetX != saveOffsetX || 
			offsetY != saveOffsetY || 
			lengthX != saveLengthX || 
			lengthY != saveLengthY
		)
		{
			//設定の保存
			saveOffsetX = offsetX;
			saveOffsetY = offsetY;
			saveLengthX = lengthX;
			saveLengthY = lengthY;
			
			//表示するUV座標を求める
			float _x = (float)offsetX / (float)lengthX;
			float _y = (float)(lengthY - 1 - offsetY) / (float)lengthY;
			float _width = _x + _uvWidth;
			float _height = _y + _uvHeight;
			_x += _spread;
			_y += _spread;
			
			//新しいUV値のセット
			_uv[0].Set(_x, _y);
			_uv[1].Set(_width, _y);
			_uv[2].Set(_x, _height);
			_uv[3].Set(_width, _height);
			_mesh.uv = _uv;
		}
	}
}

やってることは非常にシンプルでメッシュに対して一度Quadを再構成しつつUV座標を設定したoffsetとlengthの値をもとに指定したマスを表示してる感じです。

lengthの値が各X、Yのマスの最大値の設定で、offsetは実際にマスの位置をX、Yで設定していく形になります。

実際に使用するときは、あらかじめ表示させたいテクスチャの設定したマテリアルをセットしたQuadを生成しておいてから上記のスクリプトを入れると面倒が無く良いと思います。


一応、OnValidateでも処理が走るようになっているので、プレイ中でなくてもエディタ上の編集でffsetとlength数値をいじってもグラフィックに反映されるようになっています。

ですので、エディタで視覚的に設定した状態でプレハブに格納してInstantiateでそのUV値に設定した絵柄のQuad生成するのも簡単にできます。

ただし、シーンに展開されていない状態ではあくまで一時的なメッシュ情報なので、リソースとして存在するシーンに表示されていないプレハブみたいな場合はメッシュ自体は消滅してしまいますが、消滅してもまた数値を変更したり表示すれば再構成されるので特に問題はないと思います。


余談ですが。
何年も前の初期の段階ではマテリアルの方のUV値をいじってましたが、パラメータをいじるとその都度新しいマテリアルを生成してる疑惑がありましてドローコールがめちゃ増えそうな予感がしたので、それならって感じにバッチングが効きドローコールが減らせる期待のあるメッシュのUVデータの方だけをいじる方式に切り替えました。(同一マテリアルであれば描画をまとめてくれるはずなので)

今回のは3D空間上の板ポリの絵柄をマップチップみたいな感じで!っていうのやりたい人はいいかもしれません。(もっといい方法があるかもだけど)


あと、今回の機能を地面の草とかキャラとかに使用したい場合はnormalsの設定値をすべて

_normals[0].Set(0, 0, -1);

じゃなくて

_normals[0].Set(0, 1, 0);

にすると上向きの法線になるので光の当たり方が自然になります。


最後に、実際動作するアセットフォルダも置いておきますので、アセットフォルダを空プロジェクトに入れれば確認できます。

2022_01_28_Assets.zip (72.95kB)

ダウンロード

この記事が良かったらチップを贈って支援しましょう!

チップを贈るにはユーザー登録が必要です。チップについてはこちら

最新の記事

記事のタグから探す

月別アーカイブ

記事を検索