【Unity】テクニック集合2

21990 ワード

転送、アドレスを保持してください:http://blog.csdn.net/stalendp/article/details/46707079
関連記事:【Unity】テクニック集合
1.デバッグ関連
Debug.DrawRay(transform.position, newDir, Color.red);

2.uGuiのレベルの問題.(CanvasのDraw Order of Elements参照)
UGUIの要素のレンダリング順序はHierarchyの順序と一致し、上から下へレンダリングされます(前のものを上書きします).スクリプトではtransformのSetAsFirstSibling,SetAsLastSibling,and SetSiblingIndex.関数で順序を設定できます.たとえば、次の世代コードでは、現在選択されているオブジェクトを上に置きます.
curSelected.transform.SetAsLastSibling();

3.カメラ移動コード
public class DragMap : MonoBehaviour {
	
	public Vector3 speed = Vector3.zero;
	public bool isFlying = false;
	public float threshold = 0.2f;

	Camera mainCam;
	Vector3 startPos;
	int groundLayer;

	Vector3[] speedCache ;
	int idx = 0;
	bool isDragging = false;

	// Use this for initialization
	void Start () {
		groundLayer = 1 << LayerMask.NameToLayer ("ground");
		mainCam = Camera.main;
		speedCache = new Vector3[5];
		isFlying = false;
	}
	
	// Update is called once per frame
	void Update () {
		if (Input.GetMouseButtonDown (0)) {
			RaycastHit hit;
			if (Physics.Raycast (mainCam.ScreenPointToRay (Input.mousePosition), out hit, Mathf.Infinity, groundLayer)) {  
				startPos = hit.point;
				speed = Vector3.zero;
				idx = 0;
				for(int i=0; i<speedCache.Length; i++) {
					speedCache[i] = Vector3.zero;
				}
				isDragging = true;
			}

		} else if(Input.GetMouseButtonUp(0)) {
			speed = Vector3.zero;
			foreach(Vector3 s in speedCache) {
				speed += s;
			}
			speed /= 5;
			isFlying = speed.magnitude > threshold;
			isDragging = false;
		}

		if (isDragging) {
			RaycastHit hit;
			if (Physics.Raycast (mainCam.ScreenPointToRay (Input.mousePosition), out hit, Mathf.Infinity, groundLayer)) {  
				Vector3 offset = hit.point - startPos;
				mainCam.transform.position -= offset;
				speedCache [idx++ % 5] = offset;
			}
		} else if (isFlying) {
			speed *= 0.9f;
			isFlying = speed.magnitude > threshold;
			mainCam.transform.position -= speed;
		}
	}
}

4.協同的な使い方(IEnumerator)
IEnumerator Start() {
	Debug.Log ("zero: " + Time.time);
	yield return StartCoroutine (mywait (3f));
	Debug.Log ("one: " + Time.time);
	yield return StartCoroutine (mywait (2f));
	Debug.Log ("two: " + Time.time);
}

Coroutinesについては、1)Coroutines–More than you want to know:http://twistedoakstudios.com/blog/Post83_coroutines-more-than-you-want-to-know 2)Unity coroutine (Coroutine) principle deeply again:http://www.programering.com/a/MTOzgjNwATI.html 3)Wrapping Unity C# Coroutines for Exception Handling, Value Retrieval, and Locking:http://www.zingweb.com/blog/2013/02/05/unity-coroutine-wrapperCoroutineの実装リファレンス:https://github.com/rozgo/Unity.Coroutine/blob/master/Coroutine.cs
5.コールバック関連:UnityEventとSystem.ActionとSystem.Func、そしてC#でサポートされているevent;いずれもdelegateで、関数ポインタに相当します.前者には戻り値がなく、後者には戻り値があります.その中でUnityEventとSystem.Actionは基本的に同じですが、UnityEventはシーケンス化できます.最も顕著な利点は、Inspectorで編集できることです(例えば、新しいUIコントロールScrollRectのOnValueChangedプロパティなど).両者の違いは、ここでの議論を参照してください.
5a) System.Funcの使い方:
public struct AstarWorkItem {
	//....
	public System.Func<bool, bool> update;
	//....
	public AstarWorkItem (System.Func<bool, bool> update) {
		this.update = update;
	}
}
//...
AddWorkItem (new AstarWorkItem (delegate (bool force) {
	InternalOnPostScan ();
	return true;
}));

以下のように簡単に書くこともできます.
public struct AstarWorkItem {
	public System.Func<bool, bool> update;
}
// ...
AddWorkItem (new AstarWorkItem () {
	update = (force) => {
		InternalOnPostScan ();
		return true;
	}
});
5 b)UnityEventの使い方
[Serializable]
public class ScrollRectEvent : UnityEvent<Vector2> {}

// class members
[SerializeField]
private ScrollRectEvent m_OnValueChanged = new ScrollRectEvent();
public ScrollRectEvent onValueChanged { get { return m_OnValueChanged; } set { m_OnValueChanged = value; } }

// invoke ...
onValueChanged.AddListener (offset => {
    Debug.Log("ScrollRect is changed, offset is : " + offset.ToString());
});

5 c)C#システムのeventの使い方:Events Tutorial
//    
public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)  { 
	if(PropertyChanged != null) 
		PropertyChanged(this, new global::System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
}
//     
xx.OnPropertyChanged(@"name");
//     
PropertyChanged += (sender, e) => {
	Debug.Log(e.PropertyName + " is changed!!");
};

6.Raycastでtrigger Collidersを無視する方法:
Edit > Project Settings > Physics > Uncheck "Raycasts Hit Triggers"
7.パーティクルシステムの動作:
// stop particalSystem
waterEffect.Stop();

// restart particalSystem
waterEffect.Simulate(0);
waterEffect.Play();

8.C#の特殊なオペレータ:https://msdn.microsoft.com/en-us/library/6a71f45d.aspx
ここでNullableType,参考:https://msdn.microsoft.com/en-us/library/1t3y8s4s.aspx
8-1)Boxing nullable types:  int?  float?  double? 基本タイプをobjectタイプに変更する.その値はnullであってもよいし、関連する変換操作であってもよい.
//## Example 1: Nullable objects and their boxed counterpart can be tested for null:
bool? b = null;
object boxedB = b;
if (b == null) {
	// True.
}
if (boxedB == null) {
	// Also true.
}


//## Example 2: Boxed nullable types fully support the functionality of the underlying type:
double? d = 44.4;
object iBoxed = d;
// Access IConvertible interface implemented by double.
IConvertible ic = (IConvertible)iBoxed;
int i = ic.ToInt32(null);
string str = ic.ToString();

8-2)  Null-conditional Operators:  ?.  ?[]オブジェクトにアクセスする前にnullを判断すると、コードを簡略化できます.C#6の新しい特性は、Unityでは使えないようです
int? length = customers?.Length; // null if customers is null 
Customer first = customers?[0];  // null if customers is null
int? count = customers?[0]?.Orders?.Count();  
                                  // null if customers, the first customer, or Orders is null
var handler = this.PropertyChanged;
if (handler != null)
    handler(…)

===   ==》

PropertyChanged?.Invoke(e)

8-3)  Null-coalescing Operator:  x ?? y
class NullCoalesce
{
    static int? GetNullableInt()
    {
        return null;
    }

    static string GetStringValue()
    {
        return null;
    }

    static void Main()
    {
        int? x = null;

        // Set y to the value of x if x is NOT null; otherwise, 
        // if x = null, set y to -1. 
        int y = x ?? -1;

        // Assign i to return value of the method if the method's result 
        // is NOT null; otherwise, if the result is null, set i to the 
        // default value of int. 
        int i = GetNullableInt() ?? default(int);

        string s = GetStringValue();
        // Display the value of s if s is NOT null; otherwise,  
        // display the string "Unspecified".
        Console.WriteLine(s ?? "Unspecified");
    }
}
8-4) How to: Safely Cast from bool? to bool
bool? b = null;
if (b) // Error CS0266.
{
}

===  ==>>

bool? test = null;
// Other code that may or may not
// give a value to test.
if(!test.HasValue) //check for a value
{
    // Assume that IsInitialized
    // returns either true or false.
    test = IsInitialized();
}
if((bool)test) //now this cast is safe
{
   // Do something.
}

9.Unityにおける「一例」の一般的な実現方案:
public static EventSystem current { get; private set; }
...
protected void OnEnable()
{
    if (EventSystem.current == null)
        EventSystem.current = this;
#if UNITY_EDITOR
    else
    {
        Debug.LogWarning("Multiple EventSystems in scene... this is not supported");
    }
#endif
}

10.変数を初期化する簡単な方法
PointerEventData ped = new PointerEventData (EventSystem.current);
ped.position = Input.mousePosition;
EventSystem.current.RaycastAll (ped, hits);

===   ==>

EventSystem.current.RaycastAll (new PointerEventData (EventSystem.current) {
	position = Input.mousePosition
}, hits);

11.Unityにおける新しいUIのメッセージ処理方式(EventSystemにおけるRaycastにより現在のUIを取得し、メッセージを送信する)
詳細については、次の文書を参照してください.http://gregandaduck.blogspot.com/2015/02/using-unitys-c-message-system-unity-46.html
メッセージ受信者は、次のように実装されます.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;

// refer to http://gregandaduck.blogspot.com/2015/02/using-unitys-c-message-system-unity-46.html
public interface IMyMouseOver : IEventSystemHandler {
	void mouseOver(string str);
	void mouseLeaving();
}

public class MyDisplayText : MonoBehaviour, IMyMouseOver {
	public Text text;
	public void mouseOver(string str) {
		if (!text.enabled) {
			text.text = str;
			text.enabled = true;
		}
	}
	public void mouseLeaving() {
		if (text.enabled) {
			text.enabled = false;
		}
	}
}

メッセージ送信者は、以下のように実装される.
using UnityEngine;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;

public class MyMouseOver : MonoBehaviour {
	GameObject prevObject;
	void Update () {
		GameObject target = getMouseOver ();
		if (target) {
			prevObject = target;
			ExecuteEvents.Execute<IMyMouseOver> (target, null, (handle, data) => {
				handle.mouseOver ("Mouse Over Active, frame: " + Time.frameCount);
			});
		} else {
			if(prevObject) {
				ExecuteEvents.Execute<IMyMouseOver>(prevObject, null, (handle, data) =>{
					handle.mouseLeaving();
				});
				prevObject = null;
			}
		}
	}

	private GameObject getMouseOver() {
		List<RaycastResult> hits = new List<RaycastResult>();
		EventSystem.current.RaycastAll (new PointerEventData (EventSystem.current) {
			position = Input.mousePosition
		}, hits);
		foreach (RaycastResult rr in hits) {
			GameObject go = rr.gameObject;
			if(go)
				return go;
		}
		return null;
	}
}

12.C#でJavaの匿名クラスの書き方をシミュレートする(Javascriptの味がする):
public class HpBarHandler {  //      struct    delegate       
	public System.Action<float> changeHp;
	public System.Action hidde;
	public System.Action show;
	public System.Action destory;
}

public void registerHPBarEvent(HpBarHandler _hphandler) {
	hphandler = _hphandler;
}

fightUnit.registerHPBarEvent (new HpBarHandler () {
	changeHp = hp => {
		size.x = 200 * hp;
		if(rectTrans) {
			rectTrans.sizeDelta = size;
			if(hp<=0) {
				GameObject.Destroy (gameObject);
			}
		}
	},
	hidde = delegate{
		gameObject.SetActive(false);
	},
	show = delegate {
		gameObject.SetActive(true);
	},
	destory = delegate {
		GameObject.Destroy (gameObject);
	}
});

13.仕事中に常にある単位の初期化に遭遇した場合、他の単位に依存してまだ初期化されていないため、Null ReferenceExceptionが発生します.これはすべての初期化がStartメソッドに書かれているため,コード初期化の前後順序により上記の問題が発生する.解決策は初期化を段階的に行うことである.NullReferenceExceptionを引き起こすことができるのは、GetCompent、Findなどの操作で、AwakeやOnEnableメソッドなどに書かれています.論理初期化はStartの中程度に書きます.以下はScript Lifecycleの概略図です.公式ドキュメントは次のとおりです.http://docs.unity3d.com/Manual/ExecutionOrder.html
【Unity】技巧集合2_第1张图片
14.文字の書式設定
//List<Vector3> uvList = ...;
Debug.Log (string.Format("frame: {0:0.00}, uvs: {1}, {2}, {3}, {4}",
                         0.125f,
                         uvList[0].ToString("0.000"), 
                         uvList[1].ToString("0.000"), 
                         uvList[2].ToString("0.000"), 
                         uvList[3].ToString("0.000") 
                         ));

How to format a number 1234567 into 1,234,567 in C#?
int number = 123456;
number.ToString ("N0"); //123,456
//  
number.ToString ("#,##0");

より多くの文字列をフォーマットする操作:
a) https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx
b) http://www.dotnetperls.com/string-format
15.RenderTextureをファイルとして保存する(参考:http://answers.unity3d.com/questions/27968/getpixels-of-rendertexture.html)
//   RenderTexture   
public void saveRT2File(RenderTexture srcRenderTex) {
	RenderTexture.active = srcRenderTex;
	Texture2D outputTex = new Texture2D (srcRenderTex.width, srcRenderTex.height, TextureFormat.RGB24, false);
	outputTex.ReadPixels(new Rect(0, 0, srcRenderTex.width, srcRenderTex.height), 0, 0);
	outputTex.Apply ();
		
	string path = Application.persistentDataPath + "/savedTex.png";
	System.IO.File.WriteAllBytes( path, outputTex.EncodeToPNG() );
	RenderTexture.active = null;
}

16.2つのベクトルを乗算:
Vector result = Vector3.Scale(new Vector3(1, 2, 3), new Vector3(2, 3, 4)); //    (2,6,12)

17.C#でのDictionaryの初期化:
Dictionary<string, UnityAction> dict = new Dictionary<string, UnityAction> () {
	{"btnExpedition", delegate{ Debug.Log("btnExpedition");} },
	{"btnFormation", delegate{ Debug.Log("btnFormation");} }
};

18.MonoBehaviourサブクラスで親クラスのOnEnableメソッドを非表示にします.原則として、文法的な面ではサブクラスHideの親のメソッドを制限することはできませんが、そうすれば、コンパイラは108番の異常を提示します.私の解決策は、この異常をエラーに変換することです.これはコンパイラパラメータを設定する必要があります.Unityにおけるコンパイラパラメータの設定方法は、Assetsルートディレクトリの下にsmcsを追加することである.rspファイル:http://docs.unity3d.com/Manual/PlatformDependentCompilation.html
a)Assetsルートノードの下にsmcsを作成する.rspファイル、/warnaserror+:108
コンパイラパラメータの詳細については、以下を参照してください.https://msdn.microsoft.com/en-us/library/406xhdz3.aspx
b)親クラスで上書きする関数を記述し、アクセス権はprotectedである.例えば:
public abstract class AMView : MonoBehaviour {
	protected void OnEnable() {
		this.bind ();
		InitBeforeStart ();
	}

	protected virtual void InitBeforeStart() {}
}

c)サブクラスにOnEnableメソッドがある場合,コンパイラはエラーを報告する.
19.Unity 3 dでデバイスを間違えた方法、参考:Any tips for debuging Android?19-1)androidのLogcatメソッド、コマンドライン:adb logcat-s Unity、もちろんログが多い場合は出力をファイルにインポートできます.19-2)MonoDeveloperのRemote Debugを使用:Attaching MonoDevelop Debugger To an Android device 19-3)いくつかのプラグインを使用
20.マスタースレッドでコードを実行する方法.Unityコードでネットワーク層を自分でカプセル化し、データが戻ってきたらコールバックで通知すると、「StartCoroutine_Auto can only be called from the main thread.」などのエラーは、ネットワークが別のスレッドで実行されているためです.次の記事では、Main thread coroutine using Queueの簡単なコードについて説明します.
private readonly Queue<System.Action> _ExecuteOnMainThread = new Queue<System.Action>();
protected void Update() {
	lock (((ICollection)_ExecuteOnMainThread).SyncRoot) {
		while (_ExecuteOnMainThread.Count > 0) {
			_ExecuteOnMainThread.Dequeue().Invoke();
		}
	}
}

public static void ExecuteOnMainThread(System.Action action) {
	if (current != null) {
		lock (((ICollection)current._ExecuteOnMainThread).SyncRoot) {
			current._ExecuteOnMainThread.Enqueue (action);
		}

	} else {
		throw new System.Exception ("The System is not inited!");
	}
}

public new Coroutine StartCoroutine (IEnumerator routine) {
	ExecuteOnMainThread (delegate {
		base.StartCoroutine (routine);
	});
	return null;
}

21.UnityのAssertにバグがあるようです.次のコードがあります.
Unit unit = null;
// style 1
Assert.IsTrue (unitTable.TryGetValue (id, out unit), "Cannot get value from Unit sheet with id: " + id);
return unit;

// style 2
if (unitTable.TryGetValue (id, out unit)) {
	return unit;
}
throw new System.Exception (string.Format("Unit with id '{0}' is not found in cache!", id));

このうちstyle 1は、PCでは問題なく、携帯電話ではいつもunitが空になっています(携帯電話のメモリが緊張しているはずで、すぐにゴミ回収されていると思います);style 2の両方がOKです.
22.Windowsでprotobufを使用している場合、protobufのバイナリファイルをTortoiseSVNで更新した場合、svnで変更される可能性があります.主にsvnツールでは、バイナリ・ファイルがテキスト・モードであると判断してeolモード(end-of-line)をオンにした場合、改行文字が変更されることがあります.ソリューションは師eol-styleがfalseで、バイナリ・フォーマットに設定されています.tortoiseSVNでは、変更するファイルを右クリックしてpropertiesメニューを選択し、ポップアップ・ダイアログ・ボックスで次のように変更します.
【Unity】技巧集合2_第2张图片
23.不規則ボタンのカスタマイズ(関連11条参照)
一般的なゲームでは地図の中のある領域をクリック領域に変え、これらの領域は一般的に不規則である(下図参照).UnityではPhysic 2 DとPolygonCollider 2 Dを組み合わせて要求を達成することができ、Physics 2 Dをメッセージソースとし、PolygonCollider 2 Dが存在するオブジェクトをメッセージ受信者とする.関連コードは以下の通りである.
【Unity】技巧集合2_第3张图片
メッセージソース:
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class AmMapRaycaster : MonoBehaviour {
	private GameObject selectObj = null;

	void Update () {
		if (Input.GetMouseButtonDown (0)) {
			selectObj = getMapButton ();
		} else if (Input.GetMouseButtonUp (0)) {
			if(selectObj==getMapButton() && selectObj!=null) {
				ExecuteEvents.Execute<IPointerClickHandler>(selectObj, null, (handler, eventData) => {
					handler.OnPointerClick(null);
				});
			}
			selectObj = null;
		}
	}

	private GameObject getMapButton() { 
		Collider2D h = Physics2D.OverlapPoint(Input.mousePosition);  
		if(h) {  
			return h.gameObject;
		}  
		return null;  
	}  
}
メッセージ受信者(buttonと同様):
using System;
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;

[RequireComponent(typeof(Collider2D))]
public class MapButton : MonoBehaviour, IPointerClickHandler {
	[Serializable]
	public class ButtonClickedEvent : UnityEvent {}

	[SerializeField]
	private ButtonClickedEvent m_OnClick = new ButtonClickedEvent();

	public ButtonClickedEvent onClick
	{
		get { return m_OnClick; }
		set { m_OnClick = value; }
	}

	public void OnPointerClick (PointerEventData eventData) {
		m_OnClick.Invoke();
	}
}

次の操作を行います.
MapButton mapButton = GetComponent<MapButton>();
mapButton.onClick.AddListener (delegate {
	Debug.Log("you click the map area!");
});

24.C#正規表現の例:
using System.Text.RegularExpressions;

string longStr = "xxx";

Regex regex = new Regex (@"(Assets/A2Game/.*?([^\./]+)\.unity)", RegexOptions.IgnoreCase);  
Match m = regex.Match(longStr);  
if(m.Success) {  
	Debug.LogFormat("group1: {0}, group2: {1}", m.Groups[1].ToString(), m.Groups[2].ToString());  
}  

25.エディタでAnimatorControllerのすべてのStateを取得します.
public static void addAllAnimators() {
	UnityEditor.Animations.AnimatorController ac = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController> ("Assets/OriginalResources/character/1036/1036.controller");
	List<AnimatorState> states = new List<AnimatorState> ();
	getAllAnimClips (ac.layers [0].stateMachine, states);
	Debug.Log (">>>" + ac.name);
	foreach (var s in states) {
		Debug.LogFormat("name: {0}, motion: {1}", s.name, s.motion.ToString());
	}
}
private static void getAllAnimClips(AnimatorStateMachine sm, List<AnimatorState> states) {
	System.Array.ForEach(sm.states, s=>states.Add(s.state));
	System.Array.ForEach(sm.stateMachines, v=>getAllAnimClips(v.stateMachine, states));
} 

26.Unityで特定のジオメトリを描画する
public void Update() {
	draw ();
}
void draw() {
	var verticies = new Vector3[6] {
		new Vector3(0f, 0f, 0f),		// 0
		new Vector3(0f, 1f, 0f),		// 1
		new Vector3(1f, 0f, 0f),		// 2
		new Vector3(0.5f, -0.5f, 0f),	// 3
		new Vector3(-0.5f, -0.5f, 0f),	// 4
		new Vector3(-1f, 0f, 0f)		// 5
	};
	
	var triangles = new int[15] {  //      
		2, 0, 1,
		3, 0, 2,
		4, 0, 3,
		5, 0, 4,
		1, 0, 5,
	};
	
	var m = new Mesh();
	m.vertices = verticies;
	m.triangles = triangles;
	m.RecalculateNormals();
	Matrix4x4 mtx = Matrix4x4.TRS (Vector3.zero, Quaternion.identity, Vector3.one * 1.9f);
	
	Graphics.DrawMesh (m, mtx, mat, 2);
}

27.カスタムuGUIコントロール(具体例は、『【Unity】新しいUIシステムテクニック』の第2点を参照)
public class GuideMask : Graphic {
	/// <summary>
	/// Fill the vertex buffer data.
	/// </summary>
	protected override void OnPopulateMesh(Mesh m)
	{
		var r = GetPixelAdjustedRect();
		var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);
		
		Color32 color32 = color;
		using (var vh = new VertexHelper())
		{
			vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(0f, 0f));
			vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(0f, 1f));
			vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(1f, 1f));
			vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(1f, 0f));
			
			vh.AddTriangle(0, 1, 2);
			vh.AddTriangle(2, 3, 0);
			vh.FillMesh(m);
		}
	}
}

注意:Unity 5.3以降、上記の関数を「void OnPopulateMesh(VertexHelper vh)」で置き換えることをお勧めします(パラメータの変化に注意し、Meshの操作をVertexHelperにカプセル化しました)
28.iosパッケージの注意点:
unity 4を使っています.6発iosパッケージの時、導出したxcode工程の間違いを発見した.実はxcodeがあまりにも新しいため、7.2で、unityはまだ7.1しかサポートしていませんが、両者の互換性が悪く、多くのerrorの出現を招きました.だから、iosパッケージを出すときはxcodeのバージョンとunityが合っているかどうかに注意してください.そうしないと、上に時間がかかりすぎます.覚えておいてください.
関連記事:【Unity】テクニック集合