んななのゲーム開発備忘録

ゲーム制作の進捗と開発メモなど掲載しています。

Unityの衝突判定を数値化して不安定な接触を安定化する(デリゲート編)

f:id:nyanya-nnana:20171201002933p:plain:w500
Unityの衝突判定を数値化して不安定な接触を安定化する
キャラクターが接地しているか判定するときに思った通りにならなくて困ったことはありませんか? 今回の記事ではそんな時に役立つ、接触が不安定な場合でも安定して接触を判定する処理について説明します。

本記事で紹介する接触判定クラスはGithub (https://github.com/nyanya-nnana/CollisionDetector)にソースをあげてあります。

こちらの記事は前記事のUniRxの代わりにDeligateを利用し実装したものとなります。本ソースコード以外はとくにusingしないで使用できるようになりました。

本記事ではDeligateを使用したバージョンの使用方法とその実装について紹介します。 衝突判定の安定化の原理等の共通内容は前記事を参照してください。

手っ取り早く使いたいという方は次の手順をどうぞ。

  • CollisionDetectorコンポーネントを接触判定したいGameObjectにアタッチ
  • 接触対象のタグをチェックする場合はCheckTargetTagをtrueにして対象のタグをTargetTagで設定
  • 接触判定を使用したいスクリプトでCollisionDetectorをGetComponent
  • OnCollisionEnterで処理したいメソッド(戻り値void、引数Collision)をonCollisionEnterSmoothedに追加
  • OnCollisionStayで処理したいメソッド(戻り値void、引数Collision)をonCollisionStaySmoothedに追加
  • OnCollisionExitで処理したいメソッド(戻り値void、引数Collision)をonCollisionExitSmoothedに追加
  • onCollisionStaySmoothedでOnCollisionStayでしたい処理を追加
  • onCollisionExitSmoothedでOnCollisionExitでしたい処理を追加

CollisionDetectorクラスの使用方法

CollisionDetectorの設定

衝突判定をしたいGameObjectにCollisionDetectorをAddComponentしてください。 CollisionDetectorの設定項目は次の通りです。

Check Ohter Tag

衝突したGameObjectのタグをチェックするかしないか設定できます。

Target Tag

Check Other Tagがtrueの場合はここにTargetのタグを設定します。

Cut Off Frequency

衝突判定の安定度。低いほど判定の変化がゆっくりになります。

Detection Threshold Upper/Lower

衝突判定の閾値。2つの差が大きいほど変化しにくくなります。

衝突判定の利用

使用したいスクリプト内で

using NnanaSoft.CollisionDetector;

を宣言してください。 あとはStartかAwake内でGetComponentして 処理したいメソッドを次のようにデリゲートに追加すればおっけーです。 追加するメソッドの戻り値はvoid型、引数はCollision型にしてください。

private void Start()
}
    var SmoothedCollision = GetComponent<CollisionDetector>();

    // OnCollisionEnter
    SmoothedCollision.onCollisionEnterSmoothed += OnCollisionEnterMethod;
    // OnCollisionStay
    SmoothedCollision.onCollisionStaySmoothed += OnCollisionStayMethod;
    // OnCollisionExit
    SmoothedCollision.OnCollisionEnterSmoothed += OnCollisionExitMethod;
}
// OnCollisionEnter
private void OnCollisionEnterMethod(Collision collision)
{
    // ここに処理を記述
    Debug.Log("OnCollisionEnterSmoothed");
}
// OnCollisionStay
private void OnCollisionStayMethod(Collision collision)
{
    // ここに処理を記述
    Debug.Log("OnCollisionStaySmoothed");
}
// OnCollisionEnter
private void OnCollisionExitMethod(Collision collision)
{
    // ここに処理を記述
    Debug.Log("OnCollisionExitSmoothed");
}

使用するとこんな感じになります。


CollisionDetector - デモ

CollisionDetectorクラスの実装

CollisionDetectorクラスのソースコードGithubのコチラをご覧ください。

安定化した衝突判定のイベント通知にはデリゲートを使って実装します。

// 衝突時の処理用デリゲート
public delegate void OnCollisionEnterSmoothedDelegate(Collision collision);
public delegate void OnCollisionStaySmoothedDelegate(Collision collision);
public delegate void OnCollisionExitSmoothedDelegate(Collision collision);
// 衝突時の処理を追加する
public OnCollisionEnterSmoothedDelegate onCollisionEnterSmoothed;
public OnCollisionStaySmoothedDelegate onCollisionStaySmoothed;
public OnCollisionExitSmoothedDelegate onCollisionExitSmoothed;

衝突時の処理用のデリゲートを用意して、そのデリゲートを宣言します。 対応するデリゲートに使用したいメソッドを加えることで該当のイベント時にメソッドが実行されます。

生の衝突判定とその時の衝突情報はOnCollisionEnter、Stay、Exitの処理中に更新して、これを使って衝突判定を数値化します。

// OnCollisionEnter
private void OnCollisionEnter(Collision collision)
{
    // タグが一致しない場合は何もしない
    if (checkOtherTag == false || other.gameObject.CompareTag(targetTag) == true)
    {
        _onCollisionRaw = true;
        _otherOnEnter = collision;
        _otherOnStay = collision;
    }
}
// OnCollisionStay
private void OnCollisionStay(Collision collision)
{
    // タグが一致しない場合は何もしない
    if (checkOtherTag == false || other.gameObject.CompareTag(targetTag) == true)
    {
        _otherOnStay = collision;
    }
}
// OnCollisionExit
private void OnCollisionExit(Collision collision)
{
    // タグが一致しない場合は何もしない
    if (checkOtherTag == false || other.gameObject.CompareTag(targetTag) == true)
    {
        _onCollisionRaw = false;
        _otherOnExit = collision;
    }
}

衝突状態を1、していない状態を0としてFixedUpdate内で衝突判定を数値化します。_filterConstantはローパスフィルタの定数aにあたります。

// 衝突しているかどうかを数値化。_filterConstantが大きいほど1サイクル前の状態を優先
_onCollisionFloat = (1.0f - _filterConstant) * onCollisionCurrent + _filterConstant * _onCollisionFloat;

次のように衝突判定の変化にヒステリシスを持たせて、

if (onCollisionPrevious)
{
    // 1つ前のサイクルで衝突している場合、下限閾値を下回るまで状態を変化させない
    if (_onCollisionFloat <= detectionThresholdLower)
        _onCollision = false;
    else
        _onCollision = true;
}
else
{
    // 1つ前のサイクルで衝突していない場合、上限閾値を上回るまで状態を変化させない
    if (_onCollisionFloat >= detectionThresholdUpper)
        _onCollision = true;
    else
        _onCollision = false;
}

OnCollisionEnter、Stay、Exitになったときにデリゲートメソッドを実行します。

// OnCollisionEnterか、Stayか、Exitか判定
if (_onCollision == onCollisionPrevious)
{
    _onCollisionEnter = false;
    _onCollisionExit = false;
}
else
{
    if (_onCollision)
    {
        // OnNextでイベント通知
        _onCollisionEnter = true;
        OnCollisionEnterSmoothedDelegate(onCollisionEnterSmoothed);
    }
    else
    {
        // OnNextでイベント通知
        _onCollisionExit = true;
        OnCollisionExitSmoothedDelegate(onCollisionExitSmoothed);
    }
}
_onCollisionStay = _onCollision;
// OnNextでイベント通知
if (_onCollisionStay)
{
    OnCollisionStaySmoothedDelegate(onCollisionStaySmoothedDelegate);
}

以上がCollisionDetectorの主な処理です。