読者です 読者をやめる 読者になる 読者になる

チリペヂィア

リンクフリー。サンプルコードなどは関連記事内でライセンスについて明示されない限り商用利用なども自由に行って構いませんが、自己責任でお願いします。またこれら日記内容の著作権自体は放棄していません。引用部分については引用元の権利に従ってください。

コリダーキャンセラー

なんて…ひねりのない名前ッ!!

閑話休題。斜面に這わせるネタ第3回で使う予定のクラスを一回ほどはさみます。

たまに自分のコリダー反応だけを一時的に回避したい時があります。でも同じPrefabクローン同士でタグとかレイヤー管理とか面倒くさい。そんなああたにコリダーキャンセラー。子オブジェクトのColliderを列挙してenable一時無効と復帰、IDisposableでusingするだけ簡単呼出し、例外発生にも強い復帰力!

MSDN - IDisposable
開けたら閉じる/繋いだら切る/終わったらすぐ片付ける。それも「絶対に!」といった処理をパターン化するためのお作法。例外のcatch構文に近いものを簡潔に記述できる(*1)。

public class ColliderCanceler : System.IDisposable {
	
	public enum TargetFilter {
		TriggerConditionIgnore = 0, // Trigger設定はとくに評価しない
		TriggerOnColliderCancel = 1, // TriggerがTrueなコリダーだけ一時的にキャンセルする
		TriggerOffColliderCancel = 2, // TriggerがFalseなコリダーだけ一時的にキャンセルする
	}
	
	private Component[] cancel_targets = null;
	private bool[] isCanceled = null;
	
	public ColliderCanceler( Component src ) {
		InitializeTargets(src, TargetFilter.TriggerConditionIgnore);
	}
	
	public ColliderCanceler( Component src, TargetFilter filter ) {
		InitializeTargets(src, filter);
	}
	
	~ColliderCanceler() {
		Dispose();
	}
	
	public void Dispose() {
		if ( cancel_targets == null ) return;
		for ( int idx = 0; idx < cancel_targets.Length; idx++ ) {
			if ( isCanceled[idx] == true ) ((Collider)(cancel_targets[idx])).enabled = true;
		}
		cancel_targets = null;
		isCanceled = null;
	}
	
	private void InitializeTargets( Component src, TargetFilter filter ) {
		cancel_targets = src.GetComponentsInChildren<Collider>();
		if ( cancel_targets == null || 0 == cancel_targets.Length ) return;
		isCanceled = new bool[cancel_targets.Length];
		for ( int idx = 0; idx < cancel_targets.Length; idx++ ) {
			Collider cld = (Collider)cancel_targets[idx];
			
			if ( filter == TargetFilter.TriggerConditionIgnore )
				isCanceled[idx] = cld.enabled;
			else if ( filter == TargetFilter.TriggerOnColliderCancel )
				isCanceled[idx] = ( cld.enabled == true && cld.isTrigger == true);
			else if ( filter == TargetFilter.TriggerOffColliderCancel )
				isCanceled[idx] = ( cld.enabled == true && cld.isTrigger == false);
			
			if ( isCanceled[idx] ) cld.enabled = false;
		}
	}
}

コンストラクタ

ColliderCanceler( Component src, TargetFilter filter )

Component src

親オブジェクトのコンポーネントを指定します。例えば自分自身(MonoBehavior)や、とくに後付けコンポーネントが無ければtransformなどをセット。

TargetFilter filter

Trigger状態を条件に出来ます。各フラグの意味はコード中のコメントを参照。

使い方

public class SampleClass : MonoBehaviour {
	
	// ・・・<略>・・・
	
	void Update() {
		using (ColliderCanceler cc = new ColliderCanceler(this, ColliderCanceler.TargetFilter.TriggerOffColliderCancel)) {
			
			// このスコープ内では自身の子コリダーは停止中。指定したのがTriggerOffColliderCancelなので非Triggerコリダーだけ一時無効中。
			
			Vector3 fwd = transform.TransformDirection(Vector3.forward);
			if (Physics.Raycast(transform.position, fwd, 10)) return;
			
			if ( some_condition ) throw new System.Exception("hoge");
			// こんなふうに途中でreturnしたり例外で飛んでも子コリダーはusing突入前に戻る(:スコープ脱出時にColliderCanceler.Dispose()が呼び出される)。
		}
		
		// ここではもうコリダーが復帰してる
	}
}

*1:最近のVB.Netだと結構細かいルールが多くて面倒だけどC#2.0だととってもラクチン