質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.98%

3Dモデルが壁に引っかかって落ちてこない現象

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 2,375

nishi835

score 13

困っていること

お世話になります。
ちょっと質問なのですが、3Dモデルをジャンプさせて壁にぶつけると、引っかかったようになって落ちてこない現象で困っています。
モデルのColliderをtriggerにすると落ちていくので、当たり判定が引っかかっているのでしょうか。歩いてぶつかっても問題ないのですが。

モデルのColliderの形状はシンプルなBox状で、壁にも当たり判定の凹凸はありません。
壁はProBuilderで作成したものです。

原因に心当たりのある方、ご回答宜しくお願い致します。

Unityのバージョンは2018.2.9f1 Personalです。

イメージ説明

少し長いですが、3Dモデルを動かしているスクリプトの関係のありそうな部分を添付いたします。

    void Update()
    {
        var cameraForward = Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized;  //  カメラが追従するための動作
        Vector3 direction = Vector3.zero;

        // 地面に接地している時
        if (CheckGrounded())
        {
            if (Input.GetAxis("Vertical01") == 0 && Input.GetAxis("Horizontal01") == 0) //  テンキーや3Dスティックの入力(GetAxis)がゼロの時の動作
            {
                animCon.SetBool("Walk", false);
                animCon.SetBool("Run", false);
            }
            else if (!(Input.GetButton("SideRight") || Input.GetButton("SideLeft")) && !(animCon.GetBool("FrontFlip") || animCon.GetBool("BackFlip"))) 
            {
                if (Input.GetButton("Dash"))
                {
                    animCon.SetBool("Run", true);
                    dashTime += Time.deltaTime;

                    endurance -= consRate * Time.deltaTime;

                    if (Input.GetButtonDown("Slide"))
                    {
                        animCon.SetBool("Slide", true);
                    }
                }
                else
                {
                    animCon.SetBool("Run", false);
                    animCon.SetBool("Walk", true);
                    dashTime = 0.0f;
                }

                // フリップ
                if (Input.GetAxis("Flip") > 0)
                {
                    animCon.SetBool("FrontFlip", true);
                }
                else if (Input.GetAxis("Flip") < 0)
                {
                    animCon.SetBool("BackFlip", true);
                }

                direction = cameraForward * Input.GetAxis("Vertical01") + Camera.main.transform.right * Input.GetAxis("Horizontal01");  //  テンキーや3Dスティックの入力(GetAxis)があるとdirectionに値を返す
                Turn(direction);  //  向きを変える動作の処理を実行する(後述)
            }

            moveDirection = direction * moveSpeed;  //移動スピードを向いている方向に与える
            moveDirection.y = 0f;  //Y方向への速度をゼロにする

             if (isWallJump)
            {
                animCon.SetBool("Idle", true);
                isWallJump = false;
            }

            // 着地したらアニメーションパラメータのFallをfalseにする
            animCon.SetBool("Fall", false);
            animCon.SetBool("Jump", false);
            animCon.SetBool("WallJump", false);

            var input = new Vector3(Input.GetAxis("Horizontal01"), 0f, Input.GetAxis("Vertical01"));

            if (input.magnitude > 0f)
            {
                // 着地後移動キーを押したら着地アニメーション終了
                animCon.SetBool("Landing", false);
            }

            var stateInfo = animCon.GetCurrentAnimatorStateInfo(0);

            // ジャンプ
            if (Input.GetButtonDown("Jump") && !(Input.GetButton("SideRight") || Input.GetButton("SideLeft")) && !(animCon.GetBool("FrontFlip") || animCon.GetBool("BackFlip") || stateInfo.IsName("Roll")))
            {
                rb.AddForce(0, jumpPower, 0, ForceMode.Impulse);
                isJump = true;
            }

            // ジャンプ力リセット
            jumpPower = defaultJumpPower;
        }
        else // 空中なら
        {
            // ジャンプアクション後、空中に移動したらジャンプアニメーションを始める
            if (isJump)
            {
                animCon.SetBool("Jump", true);
                isJump = false;
            }

            //animCon.SetBool("isGrounded", false);
            animCon.SetBool("Idle", false);

            if (!animCon.GetBool("Fall"))  // アニメーションパラメータFallがfalseの時で地面との距離が遠かったらFallをtrueにする
            {
                if (!Physics.SphereCast(new Ray(spine.position, Vector3.down), 0.5f, distanceToTheGround, LayerMask.GetMask("Field")))
                {
                    animCon.SetBool("Fall", true);
                }
            }
            else if (animCon.GetBool("Fall"))  // 落下アニメーションの時はレイを飛ばし着地アニメーションにする
            {
                if (Physics.Linecast(spine.position, spine.position + Vector3.down * distanceToLanding, LayerMask.GetMask("Field")) && !isRoll)
                {
                    animCon.SetBool("Landing", true);
                }
            }

            if (Input.GetButtonDown("Jump"))
            {
                isRoll = true;
                animCon.SetBool("Landing", false);
            }

            jumpPower += gravity * Time.deltaTime; // 時間経過とともに壁ジャンプ力を増す
        }

        if (Input.GetButtonDown("Action"))
        {
            if (rader.CanHang())
            {
                //Rayの作成       ↓Rayを飛ばす原点   ↓Rayを飛ばす方向
                Ray ray = new Ray(transform.position, new Vector3(0, -1, 0));

                //Rayが当たったオブジェクトの情報を入れる箱
                RaycastHit hit;

                //Rayの飛ばせる距離
                int distance = 10;
                Physics.Raycast(ray, out hit, distance);

                float h_angle = hit.transform.eulerAngles.y;
                float p_angle = transform.eulerAngles.y;

                for (int i = 0; i < 4; i++)
                {
                    h_angle -= 90 * i;
                    if (h_angle < 0) { h_angle = 360 + h_angle; }
                    float diff = Mathf.Abs(h_angle - p_angle);
                    if (diff > 180) { diff = 360 - diff; }
                    if (diff <= 45)
                    {
                        transform.rotation = Quaternion.Euler(0.0f, h_angle, 0.0f);
                        break;
                    }
                }

                animCon.SetBool("HangWall", true);

                StartCoroutine(DelayMethod(0.65f, () =>
                {
                    transform.position = Vector3.Lerp(transform.position, transform.position + new Vector3(0.0f, 0.0f, -0.15f), 1.0f);
                }));
            }
        }

        if (animCon.GetBool("HangWall"))
        {
            Physics.gravity = new Vector3(0.0f, 0.0f, 0.0f);
        }
        else
        {
            Physics.gravity = new Vector3(0.0f, defaultGravity, 0.0f);
        }

        switch(animClip)
        {
            case AnimClip.GetHit:
                if(animCon.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f)
                {
                    animClip = AnimClip.Idle;
                    animCon.CrossFadeInFixedTime(AnimClip.Idle.ToString(), 0.5f);
                }
                break;
        }

        // 移動
        //charaCon.Move(moveDirection * Time.deltaTime);  //CharacterControllerの付いているこのオブジェクトを移動させる処理
        rb.AddForce(moveDirection.x * moveSpeed, 0, moveDirection.z * moveSpeed);
    }

    // 汎用コルーチン
    private IEnumerator DelayMethod(float waitTime, Action action)
    {
        yield return new WaitForSeconds(waitTime);
        action();
    }

    // 向きを変える動作の処理
    void Turn(Vector3 direction)
    {
        Quaternion q = Quaternion.LookRotation(direction);          // 向きたい方角をQuaternion型に直す
        transform.rotation = Quaternion.RotateTowards(transform.rotation, q, turnSpeed * Time.deltaTime);   // 向きを q に向けてじわ~っと変化させる.
    }

    // スタミナを返す
    public int Endurance()
    {
        return (int)endurance;
    }

    // 着地判定
    public bool CheckGrounded()
    {
        //CharacterControlle.IsGroundedがtrueならRaycastを使わずに判定終了
        //if (charaCon.isGrounded) { return true; }

        //放つ光線の初期位置と姿勢
        //若干身体にめり込ませた位置から発射しないと正しく判定できない時がある
        var ray = new Ray(this.transform.position + new Vector3(0.0f, 0.1f, -0.1f), Vector3.down);

        //探索距離
        var tolerance = 0.3f;

        //Raycastがhitするかどうかで判定
        //地面にのみ衝突するようにレイヤを指定する
        return Physics.Raycast(ray, tolerance, LayerMask.GetMask("Field"));
    }

    // 壁ジャンプ
    private void OnControllerColliderHit(ControllerColliderHit hit)
    {
        if (!CheckGrounded())
        //if (!charaCon.isGrounded)
        {
            if (hit.normal.y < 0.1f)
            {
                if (Input.GetButtonDown("Jump"))
                {
                    animCon.SetBool("WallJump", true);
                    isWallJump = true;

                    float angle = Mathf.Atan2(hit.normal.x, hit.normal.z) * Mathf.Rad2Deg;
                    transform.rotation = Quaternion.Euler(0.0f, angle, 0.0f);
                    moveDirection.x = hit.normal.x * moveSpeed;
                    moveDirection.z = hit.normal.z * moveSpeed;
                    moveDirection.y = jumpPower;
                }
            }
        }
    }

}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • nishi835

    2018/12/10 22:02

    壁に張り付いている状態のCheckGroundedの戻り値はFalseですね。空中にいる判定です。

    キャンセル

  • Bongo

    2018/12/10 22:21

    なるほど...たびたびすみませんが、Updateの最後にある「rb.AddForce(moveDirection.x * moveSpeed, 0, moveDirection.z * moveSpeed);」の直前の行に「Debug.Log(moveDirection.ToString("F6"));」とでも入れてみますと、キー入力なしで壁に貼り付いている状態ではどのような値が出力されるでしょうか?XYZすべて0でしょうか?

    キャンセル

  • nishi835

    2018/12/10 22:46

    (0.841196, 0.000000, 10.000000)となっています。

    キャンセル

回答 1

checkベストアンサー

+1

まだ実際に動作検証したわけではないのですが、現在の作りではキャラクターが移動中にジャンプした場合、空中でも移動していた方向に力が加わり続けているように思われます。
そうだとすると、もし壁に向かって走り込んでジャンプし壁に飛びつくと、そのままキャラクターが壁に押しつけられた状態になり、壁とキャラクターの間の摩擦力によって貼り付いている可能性が考えられます。

壁とキャラクターの摩擦をなくせば張り付き防止に効果がありそうですが、そうすると今度は地上を歩いている時もつるつる滑って不都合かもしれません。
キャラクターにコライダーを追加して、メインのコライダーよりも垂直方向を少し小さく、水平方向を少し大きくしてやり、キャラクターの側面だけにコライダーが露出する状態にして、そのコライダーのPhysic Materialを摩擦なしにする...Dynamic FrictionとStatic Frictionを0、Friction CombineをMinimumにするというのはどうでしょうか。

あるいは、コード修正による張り付き対策として思いつくのは、空中で衝突検出した場合に衝突面の法線を調べ(ご質問者さんが壁ジャンプ実装部分で行っているのと同様です)、側面衝突であればmoveDirectionVector3.zeroに書き換える...とかでしょうか。こちらの方がコライダー追加よりも手軽かもしれませんね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/11 16:43 編集

    後者の、「空中で衝突検出した場合に衝突面の法線を調べ、側面衝突であればmoveDirectionをVector3.zeroに書き換える」を採用したところ、上手くいきました。
    壁ジャンプと両立するのに少し苦労しましたが、そちらも一応解決しました。
    丁寧なご対応ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる