プレイヤーが動いている箱の上に乗る問題

提供: socialakiba wiki
移動: 案内検索

サンプル

上下に動いている箱の場合には、 箱に乗っているプレイヤーと箱の間で頻繁に衝突が起こっていて、 そのたびにプレイヤーの位置が補正される。

しかしanimationによって水平に動いている箱の場合には、 rigidbody の velocity は 0 であって、 つまり animation は速度0の瞬間移動を繰り返しているだけだということになる。

では、animation を使わずに乗っている箱に velocity を与えて動かせば物理エンジンが働いてプレイヤーを箱と一緒に動かしてくれるかというと、 やはりその場合でもプレイヤーはずりずりと箱の上で動いてしまって落ちてしまう。

では摩擦係数の問題かと思い、Physics Material を使ってみるが、やはりずりずり動いてしまってうまくいかない。

衝突が起きたときにプレイヤーを箱の子に、つまりプレイヤーの親を箱にしてしまえばよいという説も見かけるが、 やってみたがうまくいかない。

これは別に unity の物理エンジンがダメだからなのではなく、 実際に動いた箱に人が飛び乗っても起きる現象で、普通は転んでしまうのである。

でいろいろやってみてわかったことは、 箱は rigidbody である必要もなければ velocity を持たせる必要もない。 animation で動かした箱にもプレイヤーを乗せることはできる。 箱の position とプレイヤーの position を連動させれば良いだけだ。

ただ、プレイヤーを箱の上の任意の位置に立たせたり、 箱の上でずりずり動くのを止めたり、 箱の上を移動させられるようにするには若干の手間をかける必要がある。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {
    private bool isOn;
    private Rigidbody player;
    private BoxCollider box;
    private Vector3 relativePos;
    private Vector3 prevPos;

	// Use this for initialization
	void Start () {
          isOn = false;
          box = gameObject.GetComponent<BoxCollider>();
          player = null;
	}

    // Update is called once per frame
    void Update() {
        if (isOn)
        {
            if(0.1f < Vector3.Distance(prevPos, player.position))
            {
                relativePos = player.position - box.transform.position;
            }
            prevPos = player.position = box.transform.position + relativePos;

       }
    }

    void OnTriggerEnter(Collider col)
    {
        if(!isOn && col.gameObject.CompareTag("Player"))
        {
            isOn = true;
            if (!player)
            {
                player = col.gameObject.GetComponent<Rigidbody>();
            }
            relativePos = player.position - box.transform.position;
            player.position = box.transform.position + relativePos;
        }
        Debug.Log("enter");

    }

    void OnTriggerExit(Collider col)
    {
        if (isOn && col.gameObject.CompareTag("Player"))
        {
           isOn = false;
        }
        Debug.Log("exit");
    }
}
Hakonori.png

この例では、箱の上に透明な(box から mesh renderer と mesh filter を削除した)トリガーを乗せてある。 箱で直接当たり判定すると、側面にぶつかったときにも当たってしまう。 トリガーは箱より幅がやや狭く、しかしやや厚みを持たせたほうがよい。

トリガーの上で微少に動いたときにはプレイヤーの位置は変えない。 しかし大きく動いたときにはプレイヤーの位置を変える。 上のスクリプトではそのしきい値を0.1 (10センチ)にしてある。