MEBIUSTOSのブログ

主にUnityに関する技術的な事を書いていきます。 Twitter @xflutexx

LeapMotionでハンドオブジェクトをプーリング | Unity3D

LeapMotionのHandControllerは手がセンサー内に入るとハンドオブジェクトを生成し、センサー外にでると破棄します。この生成と破棄のコストが気になりだしたら夜も寝れなくなったので、プーリングできるように改造しようと決意。

「なんかHandControllerにDestroy Handsっていうチェックボックスあるよ?これでいいんじゃない?」とか思いますよね。自分も思いました。で、実際にチェックを入れてみると。。。まぁ、そうなるわけです。誰が得するんだこの仕様。。。

LeapMotionのCore Assetsを改造

そんなわけで、Destroy Handsチェックボックスにチェックが入っていなかった場合はプーリングするように改造しました。改造元のバージョンは「Unity Core Assets v2.3.1」です。

ターゲットソースはHandController.csになります。CreateHand関数、DestroyHand関数及びCreateHand関数のコール元となるUpdateHandModels関数にも手をいれました。これで、手がセンサー範囲外にでるとハンドオブジェクトのGameObjectがDisable化され、再度センサー内に手がもどってくるとActive化されて再利用されるようになりました。
(本当はGameObjectの位置をカメラ範囲外に出すことで対応したかったんですが、色々あってGameObjectのactiveを弄ってます。)

一応ソースは下記の通りです。もし利用する場合はコメント文や関数名から場所を見つけて、コピペで置き換えて下さい。当然自己責任で!

UpdateHandModels関数

// Create the hand and initialized it if it doesn't exist yet.
if (!all_hands.ContainsKey(leap_hand.Id) || all_hands[leap_hand.Id] == null) {
    int handNumber = (mirrorZAxis != leap_hand.IsLeft) ? 0 : 1;
    bool isRigidModel = model.GetType().Equals(typeof(RigidHand));
    bool isPooled = isRigidModel ? handRigidModels[handNumber] != null : handRiggedModels[handNumber] != null;
    HandModel new_hand = CreateHand(model, handNumber);
    if (!isPooled) {
        new_hand.SetLeapHand(leap_hand);
        new_hand.MirrorZAxis(mirrorZAxis);
        new_hand.SetController(this);

        // Set scaling based on reference hand.
        float hand_scale = MM_TO_M * leap_hand.PalmWidth / new_hand.handModelPalmWidth;
        new_hand.transform.localScale = hand_scale * transform.lossyScale;

        new_hand.InitHand();
        new_hand.UpdateHand();
    }
    new_hand.gameObject.SetActive(true);
    all_hands[leap_hand.Id] = new_hand;
} else {

CreateHand関数

/** Creates a new HandModel instance. */
HandModel[] handRigidModels = new HandModel[20];
HandModel[] handRiggedModels = new HandModel[20];
protected HandModel CreateHand(HandModel model, int handNumber) {
    bool isRigidModel = model.GetType().Equals(typeof(RigidHand));
    HandModel hand_model = isRigidModel ? handRigidModels[handNumber] : handRiggedModels[handNumber];

    if (hand_model == null) {
        hand_model = Instantiate(model, transform.position, transform.rotation)
                                as HandModel;
        hand_model.gameObject.SetActive(true);
        Leap.Utils.IgnoreCollisions(hand_model.gameObject, gameObject);
        if (handParent != null) {
            hand_model.transform.SetParent(handParent.transform);
        }
        if (isRigidModel)
            handRigidModels[handNumber] = hand_model;
        else
            handRiggedModels[handNumber] = hand_model;
    }
    return hand_model;
}

DestroyHand関数

/** 
* Destroys a HandModel instance if HandController.destroyHands is true (the default).
* If you set destroyHands to false, you must destroy the hand instances elsewhere in your code.
*/
protected void DestroyHand(HandModel hand_model) {
    if (destroyHands) {
        bool isRigidModel = hand_model.GetType().Equals(typeof(RigidHand));
        if (isRigidModel) {
            for (int i = 0; i < 2; i++) {
                if (handRigidModels[i] != null && handRigidModels[i].GetInstanceID() == hand_model.GetInstanceID()) {
                    handRigidModels[i] = null;
                    break;
                }
            }
        } else {
            for (int i = 0; i < 2; i++) {
                if (handRiggedModels[i] != null && handRiggedModels[i].GetInstanceID() == hand_model.GetInstanceID()) {
                    handRiggedModels[i] = null;
                    break;
                }
            }
        }

        hand_model.SetLeapHand(null);
        if (hand_model != null)
            Destroy(hand_model.gameObject);
    } else {
        hand_model.SetLeapHand(null);
        hand_model.gameObject.SetActive(false);
    }
}