マウス?タッチ?両方使いたい!そんなあなたにInterfaceメソッド
ほとんどほったらかしでしたが、こんなブログを参照してくださっている方が少なからずいらっしゃることに驚きと嬉しさと感謝です。
今回はunityでuGUIのbuttonなどを使わず、マウスでクリックやドラッグしたときと、タッチでクリックやドラッグしたときの、どちらの方法でも実行したいときに利用すると便利な実装です。
■今までの実装方法
今までは(少なくとも僕は)uGUIのbuttonなどを使わない場合、マウスをクリックしたときは、UnityEngine.InputクラスのGetmouseButtonとかmousePositionを使用して関数を実行したり、マウスポインターの位置を取得してたりしてました。
また、タッチパネルの(スマホやタブレットPCなど)マルチタッチを想定した実装をするときは、UnityEngineのTouchクラスを使用してマルチタッチしたときの処理を書いていました。
例えば、タブレットPCをターゲットにして実装を行う過程で、「クリックまたはタッチのいずれかのイベントが発行されたとき」に関数を実行したい場合、上記の実装だと書く量が増えて大変です。
■便利なIEventSystemHandlerを継承したInterfaceメソッド
そんなときに便利なのがIEventSystemHandlerを継承したInterfaceメソッドです。
いくつか種類があります。
例えば、クリックまたはタッチであるならIPointerDownHandler,IPointerUpHandlerを使用します。
using UnityEngine; using UnityEngine.EventSystems;
public class Example : MonoBehaviour, IPointerDownHandler, IPointerUpHandler { public void OnPointerDown(PointerEventData pointerEventData) { //クリックし始め Debug.Log("Down!"); }
public void OnPointerUp(PointerEventData pointerEventData) {
//クリックし終わり Debug.Log("Up!"); } }
リファレンスにも書いてある通り、usingステートメントでUnityEngine.EventSystemsとMonoBehaviourの他にIPointerDownHandler,IPointerUpHandlerを継承している点が味噌です。
上記を書いたうえで、それぞれOnPointerDown(PointerEventData pointerEventData),
OnPointerUp(PointerEventData pointerEventData)が使用できるようになります。
その他にもドラッグをしたときを取得したいのならばIBeginDragHandler, IDragHandler, IEndDragHandlerを使用します。
public class DragMe : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { //ドラッグし始め
public void OnBeginDrag(PointerEventData eventData) { Debug.Log("Drag_Start!"); }
//ドラッグ中
public void OnDrag(PointerEventData eventData) { Debug.Log("Draging..."); }
//ドラッグし終わり
public void OnEndDrag(PointerEventData eventData) { Debug.Log("Drag_Stop!"); }
こちらもリファレンスにも書いてある通り、usingステートメントでUnityEngine.EventSystemsとMonoBehaviourの他にIBeginDragHandler, IDragHandler, IEndDragHandlerを継承している点が味噌です。
上記を書いたうえで、それぞれOnBeginDrag(PointerEventData pointerEventData),
OnDrag(PointerEventData pointerEventData),OnEndDrag(PointerEventData pointerEventData)が使用できるようになります。
ドラッグ中には「どれぐらい、どの方向にドラッグされているか」を取得したいなどの場合が多いと思いますが、引数のPointerEventDataにさまざまなデータが格納されていて、それらを参照することで実装できます。
例えば、PointerEventData.deltaはVector2型で前フレームからの移動量を取得できます。
あとは作成したスクリプトを動かしたいオブジェクトなどにアタッチして完成。
■利点
主にマルチタッチの実装の際に「複数のオブジェクトをタッチして、各ポインター(各指)でドラッグしたい」などの場合、GetmouseButtonだと複数のオブジェクトを選択してドラッグできません。(マウスのは基本1つなので)
かといって、Touchクラスを使用すると、どちらがどのポインターでドラッグされているかなどを管理が面倒くさいです。
Interfaceメソッドを使用すれば、スクリプトがアタッチされているオブジェクトに対しての管理や動作が簡単に実装できます。
■まとめ
他にもInspectorからEventSystemをAddコンポーネントして、関数をイベントにリッスンさせる方法もありますが、こちらのほうがスクリプトで完結するのでわかりやすいと思い、まとめてみました。
紹介していないInterfaceメソッドもまだまだありますので、リファレンスを見て活用してみてください。
以上
【Unity】値をループさせたいときはMathf.Repeat
こんな便利なAPIがあったなんて。。。
Mathf.Repeatは第1引数が、0を下回る場合は第2引数を、第2引数を上回る場合は0を返してくれます。
値をループさせたいときに特に重宝します。
値を0~10でループさせたい場合、単純に書くと下記のようになると思います。
float currentValue;
void Update{
if(currentValue > targetValue ){
currentValue= 0f;
}
if(currentValue < 0f){
currentValue = 10f;
}
}
それを下記のようにできます。
float currentValue;
void Update{
currentValue = Mathf.Repeat(currentTime,10f)
}
すごいすっきりしましたね。
注意すべき点は、負の数を第2引数に設定できないことです。
それでも使える場面は多々あると思うので参考までに。
【Unity】続・スクリプトからMaterialをオブジェクトごとに変更ーMaterialPropertyBlock編
久しぶりに更新します。
今回は以前の記事についての補足、応用編です。
前回の記事では、スクリプトから任意のMeshごとにMaterialの情報をSetする方法を記載しました。
この方法より処理が軽くする方法がありました。
複数のMeshのMaterial情報を、毎フレーム更新するような場合には特に効果が見られます。
複数のcubeをMaterialのEmissionの値を変えて光らせたいと思います。
※光っていることが分かりやすいようにPostProcessのBloomを設定しておきます。
1.前回までの方法で光らせてみる
下記のようなスクリプトになると思います。
public class SetMaterialPropertyManager : MonoBehaviour
{
[SerializeField] MeshRenderer objMesh;int emissionID;
[SerializeField] float emissionValue;
void Start()
{
emissionID = Shader.PropertyToID("_EmissionColor");
}void Update()
{
Color emissionColor = new Color(emissionValue, emissionValue, emissionValue);
for (int i = 0; i < objMesh.Length; i++) {objMesh[i].material.SetColor(emissionID, emissionColor);
}
}
実行して emissionValueの値を変えてみると、、、
光りましたね。Batchesは102でした。
2.MaterialPropertyBlockを使用する
「Material.Set~」は新しくMaterialを生成しているので、レンダリングパスが増えて描画処理が重くななるようです。しかし、MaterialPropertyBlockを使用すると、新しくMaterialを生成せず、そのまま情報を流し込めるようです。
※詳しくは参考ページより
MaterialPropertyBlockを使う場合は、下記のようなスクリプトになります。
public class SetMaterialPropertyManager : MonoBehaviour
{
[SerializeField] MeshRenderer objMesh;MaterialPropertyBlock block = null;
int emissionID;[SerializeField] float emissionValue;
void Start()
{
block = new MaterialPropertyBlock(); //初期化
emissionID = Shader.PropertyToID("_EmissionColor");
}void Update()
{
Color emissionColor = new Color(emissionValue, emissionValue, emissionValue);
block.SetColor(emissionID, emissionColor); //materialにSetしたい処理for (int i = 0; i < objMesh.Length; i++) {
objMesh[i].SetPropertyBlock(block);
}
}
}
実行してみると、、、
光りました。
先ほどとやっていることは変わりませんが、Batchesは20になり先ほどの5分の1の処理で済んでいます。
まとめ
Meshごと個別にMaterialの情報を頻繁に変えたい場合はMaterialPropertyBlockを使うと格段に処理が速くなります。使用してみてください。
参考ページ
【Unity】MaterialのPropertyIDについて
UnityのMaterialはEmissionの値やMainTextureのデータなど、様々な値を外部からセットできます。
その時に使うのが、material.Set〇〇みたいな関数です。
・SetInt
・SetFloat
・SetColor
・SetTexture
などなど
例えば、色を変えたいとなった時、
material.SetColor("_Color",new Color(1,1,1));
みたいに書くと思います。
実は上記のようなSet〇〇の第1引数はintでも可能なんです。
で、引数をstringにしていると、それを内部的にintに変換するようで、要はintで指定したほうが処理が早いみたいなんですね。
じゃあ、自分が設定したいPropertyNameのintはどう取得するの?
こちらです。
int propertyID = Shader.PropertyToID("_Color"); //取得したいPropertyName
一回記録しておくだけで良いので、Start関数などで代入しておいて、それを使い回せばおk!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PropertyIdTest: MonoBehaviour {
int colorID;
int emissionID;
void Start(){
colorID = Shader.PropertyToID("_Color");
emissionID = Shader.PropertyToID("_Emission");
}
void SetMaterialColor(Material mat, Color col){
mat.SetColor(colorID,col);
}
void SetMaterialEmission(Material mat, Color col){
mat.SetColor(emissionID ,col);
}
}
書いてる途中で、いい記事を見つけたので、こちらを参考にしたほうがイイかもです。。。
以上。