投稿記事

2022年 01月の記事 (3)

ものららい 2022/01/31 14:19

Unityでコルーチンを使って一定時間後に処理を走るようにするやつ


(前回と変わり映えしないスクショ)

今回はUnityでコルーチンを使って一定時間後に指定の処理を走らせる処理について。
※あくまで自身の学習用なので粗があってもおおめにみてね🦝


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

using System;
using System.Collections;
using UnityEngine;

//コルーチンを使った指定時間後登録した処理をするやつ
public class TimerFunc : MonoBehaviour
{
	IEnumerator _coroutine;
	bool _activeFlag;
	bool _pauseFlag;
	float _setTime;
	float _startTime;
	Action _setFunc;
	
	
	//処理のセット
	public void SetFunc (float time, Action func)
	{
		//処理が設定されているとき
		if(func != null)
		{
			//すでに登録されてる処理を停止
			Stop();
			
			_activeFlag = true;
			_setFunc = func;
			_setTime = time;
			_startTime = Time.realtimeSinceStartup;
			
			//処理の予約
			_coroutine = Coroutine(time, func);
			StartCoroutine(_coroutine);
		}
	}
	
	
	//処理の実行
	IEnumerator Coroutine (float time, Action func)
	{
		//0秒以上であれば指定時間待つ
		if(time > 0)
		{
			yield return new WaitForSeconds(time);
		}
		
		//登録情報の初期化
		Stop();
		
		//登録した処理実行
		func();
	}
	
	
	//処理の停止
	public void Stop ()
	{
		if(_coroutine != null)
		{
			StopCoroutine(_coroutine);
			
			_coroutine = null;
			_activeFlag = false;
			_pauseFlag = false;
			_setTime = 0;
			_startTime = 0;
			_setFunc = null;
		}
	}
	
	
	//処理の一時停止
	public void Pause ()
	{
		if(CheckActive() == true && _pauseFlag == false)
		{
			_pauseFlag = true;
			StopCoroutine(_coroutine);
			
			//経過時間分減らした設定時間を求める
			_setTime -= Time.realtimeSinceStartup - _startTime;
		}
	}
	
	
	//処理の一時停止解除
	public void Unpause ()
	{
		if(CheckActive() == true && _pauseFlag == true)
		{
			_pauseFlag = false;
			
			//処理の再登録
			_coroutine = Coroutine(_setTime, _setFunc);
			StartCoroutine(_coroutine);
		}
	}
	
	
	//動作中か確認
	public bool CheckActive ()
	{
		if(_coroutine != null && _activeFlag == true)
		{
			return true;
		}
		
		return false;
	}
	
	
	//破棄される時に強○停止
	void OnDestroy ()
	{
		Stop();
	}
}

実際に使用するときはこんな感じに記述します。
Main.cs

using System;
using UnityEngine;
using UnityEngine.UI;

public class Main : MonoBehaviour
{
	void Awake ()
	{
		//セット
		TimerFunc _timerFunc = gameObject.AddComponent<TimerFunc>();
		
		//3秒後に処理
		_timerFunc.SetFunc(3, () => 
		{
			Debug.Log("こんにちは!");
		});
	}
}

まず、コルーチンってなぁに?って話ですがちょっと間違いがなく説明できる自信ないので、下記リンクで詳細見てもらえると助かります。
Unity ユーザーマニュアル コルーチン


一応、今回の使用の範囲で平たく平たく説明すると、

yield return new WaitForSeconds(time);

の部分で一度処理の流れ自体を停止して、指定時間後に停止した以降の処理を再開するみたいな物になります。

ただ、停止するといっても順番に処理が行われる通常の流れと違う非同期処理で動いているので最初は若干混乱するかもしれませんが、慣れてしまえば何秒後に何々してその何秒後に何々みたいなUpdateの中に毎フレームたくさん判定とるような処理を書くのをだいぶ減らせると思うので、だいぶすっきりした記述にできると思います。


便利ではありますが、コルーチンの記述自体を使用したい場所のクラスとか色んな箇所に書くと管理が大変なので、こういうタイマー機能を作ってそちらで処理させるのがうっかりさんにはおすすめです。


あと最後に、コルーチンを動かしているときにそのコルーチンの処理のコンポーネントがついてるGameObjectを破棄するとエラーが出るので、動作中の場合は下記の記述みたいに動作を停止してから破棄してください。

//(例
void OnDestroy ()
{
	StopCoroutine("コルーチン");
}

ちなみにコルーチンでこんなこともできます。
0.5秒ごとに呼ばれるループを作ってキャラクターのUVを更新して待機モーションっぽい事をしてます。


実際動作するアセットフォルダも置いておきますので、アセットフォルダを空プロジェクトに入れれば確認できます。(Script/Main.csに使用例があります)

2022_01_31_Assets.zip (76.15kB)

ダウンロード

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

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

ものららい 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)

ダウンロード

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

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

ものららい 2022/01/04 15:13

あけました!


あけましておめでとうございます🐅

おめでたいですが、去年中ゲーム完成させたかったなぁとか力の不足等で色々ずれ込み悔いの残る所ではあったりして…うっ🐄。(今年はがんばります

去年末に+αでやらなきゃいけない事とか今現在足りない物とか色々課題が見えてきたのでとりあえず今年初めはそこらへんを取り組んで強化して行ければなとか思います。

今年もよろしくお願いいたします!

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

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

月別アーカイブ

記事のタグから探す

記事を検索