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

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

ただいまの
回答率

87.49%

物体同士がくっついてしまう

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 515

score 37

前提・実現したいこと

Playerというボールを操作し、敵のボールを場外に落としていくゲームを作っています。

とても大きな敵(同じくぼーる)を出現させplayerにむかって移動します。その時、playerに触れたとき、大きく吹き飛ばします。なお、powerupをplayerが所有していれば吹き飛ばす力が小さくなります。

Big Enemy
Nomal Enemy
Player

がここでは登場しますが、

Massは
Big Enemy   1500
Nomal Enemy   5
Player          5

です。

それぞれColliderのMaterialは

Dynamic Friction 0.6
Static Friction 0.6
Bounciness 1
Friction Combine Average
Bounce Combine Multiply

となっています。

発生している問題・エラーメッセージ

Big Enemyにplayerが接触したとき、吹き飛ばされるはずがくっついて離れなくなってしまうことがあります。

Nomal Enemyも同じように吹き飛ばされるスクリプトを書いてありますが、Nomal Enemyにはこのようなことがありません。
また特に、Big Enemyが登場するとき、上空から落下してくるのですが、落下しGroundに着地した瞬間にPlayerがBig Enemyに触れているとこの問題が起きやすいように感じます。さらに、BigEnemyが出現した瞬間に、それの下に潜り込むように接触しに行くと、それ以降PlayerがBigEnemyから離れなくなりました。

予想

敵の下に潜り込んだ時に起こっているため、BigEnemyから下向きの力が加わって、弾き飛ばされるはずが、地面に押し付けられているようになっているのかもしれません。

該当のソースコード

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

public class BigEnemy : MonoBehaviour
{
    private GameObject player;
    private GameObject mainCamera;

    private Rigidbody bigEnemyRb;

    public float bigEnemySpeed = 10.0f;
    public float blowOffStrengh = 15.0f;

    private bool onGround = false;

    private PlayerController playerControllerScript;

    // Start is called before the first frame update
    void Start()
    {
        player = GameObject.Find("Player");
        mainCamera = GameObject.Find("Main Camera");
        bigEnemyRb = GetComponent<Rigidbody>();
        playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();

    }

    // Update is called once per frame
    void Update()
    {
        if (onGround == true)
        {
            moveToPlayer();
        }
    }

    private void moveToPlayer()
    {
        if (!playerControllerScript.gameOver)
        {
            if (playerControllerScript.readyToRanchSpecialWeapon)
            {
                bigEnemyRb.velocity = Vector3.zero;
            }
            else
            {
                Vector3 lookDirection = (player.transform.position - transform.position).normalized;

                bigEnemyRb.AddForce(lookDirection * bigEnemySpeed);
            }
        }
    }
    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Nomal Enemy"))
        {
            Rigidbody nomalEnemyRigidbody = collision.gameObject.GetComponent<Rigidbody>();
            Vector3 awayFromBigEnemy = collision.gameObject.transform.position - transform.position;

            nomalEnemyRigidbody.AddForce(awayFromBigEnemy * blowOffStrengh, ForceMode.Impulse);
        }

        if (collision.gameObject.CompareTag("Player"))
        {
            Rigidbody nomalEnemyRigidbody = collision.gameObject.GetComponent<Rigidbody>();
            Vector3 awayFromBigEnemy = collision.gameObject.transform.position - transform.position;

            if (playerControllerScript.hasPowerup)
            {
                blowOffStrengh = 10f;
            }
            else
            {
                blowOffStrengh = 30f;
            }
            nomalEnemyRigidbody.AddForce(awayFromBigEnemy * blowOffStrengh, ForceMode.Impulse);
        }

        if (collision.gameObject.CompareTag("Ground"))
        {
            onGround = true;
            StartCoroutine(ShakeCamera());
        }
    }
    IEnumerator ShakeCamera()
    {
        float shakeY = 10f;
        float shakeInterval = 0.1f;
        for (int i = 0; i < 15; i++)
        {
            yield return new WaitForSeconds(shakeInterval);

            mainCamera.transform.Translate(0, shakeY, 0);

            if (i % 2 == 0)
            {
                shakeY = shakeY * 0.5f * -1;

            }

            shakeInterval -= 0.01f;
        }

        mainCamera.transform.position = new Vector3(0, 10, -20);
    }

}
補足 Playerのscript
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    private GameObject focalPoint;
    public GameObject powerupIndicator;
    public GameObject specialWeaponIndicator;
    public GameObject nomalEnemyPrefab;

    private Rigidbody playerRb;
    private Rigidbody nomalEnemyPrefabRb;

    public float powerUpStrengh = 15.0f;
    public float defaltSpeed = 5.0f;

    public bool hasPowerup = false;
    public bool readyToRanchSpecialWeapon = false;
    public bool hasSpecialWeapon = false;
    public bool gameOver = false;
    public static bool cameraForwardType = false;
    public bool onGround;
    public bool blownAwayByPlayerWithSpecialWeapon = false;


    public ParticleSystem bigShockParticle;
    public ParticleSystem smallShockParticle;

    private AudioSource playerAudio;
    public AudioClip highBounceSound;
    public AudioClip coinSound;
    public AudioClip lowBounceSound;
    public AudioClip BigBounceSound;
    public AudioClip getSpecialWeaponSound;

    // Start is called before the first frame update
    void Start()
    {
        Time.timeScale = 1.0f;

        focalPoint = GameObject.Find("Focal Point");

        playerRb = GetComponent<Rigidbody>();
        nomalEnemyPrefabRb = nomalEnemyPrefab.GetComponent<Rigidbody>();

        playerAudio = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {
        Move();

        Jump();

        Destroy();
    }

    private void Move()
    {
        if (!readyToRanchSpecialWeapon)
        {
            float speed;
            if (cameraForwardType)
            {
                float forwardInput = Input.GetAxis("Vertical");

                if (onGround)
                {
                    speed = defaltSpeed;
                }
                else
                {
                    speed = defaltSpeed * 0.3f;
                }

                playerRb.AddForce(focalPoint.transform.forward * speed * forwardInput);
            }
            else
            {
                float verticalInput = Input.GetAxis("Vertical");
                float horizontalInput = Input.GetAxis("Horizontal");

                if (onGround)
                {
                    speed = defaltSpeed;
                }
                else
                {
                    speed = defaltSpeed * 0.3f;
                }
                playerRb.AddForce(focalPoint.transform.forward * speed * verticalInput);
                playerRb.AddForce(focalPoint.transform.right * speed * horizontalInput);
            }
        }
    }




    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("Powerup") && !hasPowerup)
        {
            hasPowerup = true;            
            Destroy(other.gameObject);
            StartCoroutine(PowerupCountdownRoutine());
            StartCoroutine(SetPowerupIndicatorPosition());

            playerAudio.PlayOneShot(coinSound, 0.4f);
        }

        if (other.CompareTag("Special Weapon") && !hasSpecialWeapon && onGround)
        {

            hasSpecialWeapon = true;            
            Destroy(other.gameObject);

            StartCoroutine(SetSpecialWeaponIndicatorPosition());

            StartCoroutine(SetSpecialWeapon());

            playerAudio.PlayOneShot(getSpecialWeaponSound, 1f);
        }
    }

    IEnumerator PowerupCountdownRoutine()
    {
        powerupIndicator.gameObject.SetActive(true);
        yield return new WaitForSeconds(7);
        hasPowerup = false;
        powerupIndicator.gameObject.SetActive(false);
    }

    IEnumerator SetPowerupIndicatorPosition()
    {
        while (hasPowerup)
        {
            powerupIndicator.transform.position = transform.position + new Vector3(0, -0.5f, 0);

            yield return null;
        }
    }



    private void OnCollisionEnter(Collision collision)
    {
        if ((collision.gameObject.CompareTag("Nomal Enemy") || collision.gameObject.CompareTag("Mitosis Enemy")) && hasPowerup)
        {
            Rigidbody nomalEnemyRigidbody = collision.gameObject.GetComponent<Rigidbody>();
            Vector3 awayFromPlayer = collision.gameObject.transform.position - transform.position;

            nomalEnemyRigidbody.AddForce(awayFromPlayer * powerUpStrengh, ForceMode.Impulse);
            smallShockParticle.Play();
            //Debug.Log("Player collided with: " + collision.gameObject.name + " with powerup set to " + hasPowerup);

            playerAudio.PlayOneShot(highBounceSound, 4f);
        }
        else if ((collision.gameObject.CompareTag("Nomal Enemy") || collision.gameObject.CompareTag("Mitosis Enemy")) && !hasPowerup)
        {
            playerAudio.PlayOneShot(lowBounceSound, 0.6f);
        }


    }

}

補足情報(FW/ツールのバージョンなど)

unity 2020.1.2f1
windows10

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • Bongo

    2021/01/31 17:22

    いえいえ、追記ありがとうございます。Playerに対してしか現象が起こらないとなると、プレイヤーのスクリプトが何らかの干渉をしている可能性はないでしょうかね?差し支えなければPlayerのスクリプトもご提示いただけませんでしょうか。
    また、Big Enemy登場時の着地タイミングで発生しやすそうだとのことですが、他にも発生条件に影響しそうな要素はないでしょうか?たとえば、Big Enemyに接触する瞬間にプレイヤーへの入力操作がある場合と、何も入力せずなすがままにした場合とで現象の発生しやすさが変わるでしょうか。

    キャンセル

  • actionstudio

    2021/01/31 19:35

    かなり省略した部分がありますが、問題になりそうな部分を書き加えました。
    追記したように、bigenemyの下に潜り込んだ時に起こってしまっています。

    キャンセル

  • actionstudio

    2021/01/31 22:18

    この質問は今回の問題と直接関係はないのですが、
    コードを書く場合、UnityからC#スクリプトをクリエイトしてダブルクリックで開いて記述し始めますよね。visualStudioをunity上ではなく直接開いて、記述するにはどうすればいいのでしょうか。新規作成で記述をしたいです。
    調べたのですが解決できませんでした。
    もしよろしければ教えてください。

    キャンセル

回答 1

checkベストアンサー

+2

はたしてご質問者さんの状況を再現できているかどうか不確かなものの、模擬シーンを作ってみて挙動を確認してみました。
Big Enemyが地上にある状態でPlayerがBig Enemyに接触したときと、Big Enemyが落下してきてPlayerを押しつぶすような位置関係にあるときでは、接触時の速度のY成分にかなりの差があるように思われました。Big Enemyに押しつぶされる状況ではPlayerが地面に向かって弾き飛ばされることになりますので、地面との摩擦が強烈に働いてPlayerを減速させているんじゃないかと思われました。
そこで、AddForceで吹き飛ばす代わりにVelocityの書き換えで吹っ飛ばすようにしてはいかがかと思いました。これなら速度のY成分に関係なく直接的に速度が設定されますので、摩擦の影響を無視して一定の吹っ飛びになるんじゃないでしょうか。
BigEnemyのスクリプトの吹っ飛ばす部分を、下記のように変更してみてはいかがでしょう。

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Nomal Enemy"))
        {
            Rigidbody nomalEnemyRigidbody = collision.gameObject.GetComponent<Rigidbody>();
            Vector3 awayFromBigEnemy = collision.gameObject.transform.position - transform.position;
            awayFromBigEnemy.y = 0.0f;
            awayFromBigEnemy.Normalize();

            nomalEnemyRigidbody.velocity = awayFromBigEnemy * blowOffStrengh * nomalEnemyRigidbody.mass;
        }

        if (collision.gameObject.CompareTag("Player"))
        {
            Rigidbody nomalEnemyRigidbody = collision.gameObject.GetComponent<Rigidbody>();
            Vector3 awayFromBigEnemy = collision.gameObject.transform.position - transform.position;
            awayFromBigEnemy.y = 0.0f;
            awayFromBigEnemy.Normalize();

            if (playerControllerScript.hasPowerup)
            {
                blowOffStrengh = 10f;
            }
            else
            {
                blowOffStrengh = 30f;
            }
            nomalEnemyRigidbody.velocity = awayFromBigEnemy * blowOffStrengh * nomalEnemyRigidbody.mass;
        }

        if (collision.gameObject.CompareTag("Ground"))
        {
            onGround = true;
            StartCoroutine(ShakeCamera());
        }
    }

なお、これによってPlayerが地面に接している場合の重力による地面への押しつけに基づく摩擦による減速も無効化されるでしょうから、blowOffStrenghをもっと小さくしてやらないと吹っ飛びすぎるかもしれません。

Visual Studio上で新規スクリプトを作成する場合は、ソリューションエクスプローラーのスクリプトを置きたいフォルダ上で「追加」を行えばいいんじゃないでしょうか。Unityに戻るとファイルがインポートされ、スクリプトとして使えるようになるかと思います。

図

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2021/02/01 08:50 編集

    とても分かりやすかったです!ありがとうございます。
    今、試しているのですが少し質問があります。
    ```
    awayFromBigEnemy.Normalize();
    ```
    この部分についてなのですが、これはなぜ必要なのでしょうか。接触したときに力が加わるため、接触したときの距離はそれほど考慮する必要がないように感じたのですが。

    追記
    ```
    awayFromBigEnemy.y = 0.0f;
    ```
    この部分から、下に向かって力が加わらなければ摩擦が大きくなってくっついてるように見える問題が発生しなくなるのではないかと思い、
    ```
    Rigidbody nomalEnemyRigidbody = collision.gameObject.GetComponent<Rigidbody>();
    Vector3 awayFromBigEnemy = collision.gameObject.transform.position - transform.position;

    awayFromBigEnemy.y = 0f;

    nomalEnemyRigidbody.AddForce(awayFromBigEnemy * blowOffStrengh, ForceMode.Impulse);
    ```
    としたところ、うまくできました!
    velocityを使うと物理的に変な挙動になってしまうことがあると調べて分かったので、上に示したやり方にしようと思います。
    ありがとうございました。

    キャンセル

  • 2021/02/01 09:08

    うまくいきましたようで安心しました。投稿しましたコードのNormalizeですが、あれはPlayerとBig Enemyの接触位置がどこであろうと一定の吹っ飛びにしようと思ってのことでした。Big Enemyはその名の通りPlayerよりもけっこう大きいボールなんだろうと考えまして、そうなるとPlayerがBig Enemyのほぼ真下で押しつぶされた場合と、Big Enemyの周辺部でかすめるように弾かれた場合とで吹っ飛び量に差が出てしまうのではないか...と思って付け加えたものです。ですがあれは今回の問題の主題ではありませんので、Normalizeなしで問題ないようでしたらそれでかまわないと思います。

    キャンセル

  • 2021/02/01 20:10

    わかりました!そういうことだったのですね。たくさんのことを教えていただきありがとうございました。

    キャンセル

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

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

関連した質問

同じタグがついた質問を見る