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

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

ただいまの
回答率

90.34%

  • Unity

    4397questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

【Unity】エアホッケーでうまく弾けない

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 653

nakamu

score 11

![イメージ説明](94094aea0b6113938e79a2718132552f.gif)
【問題】 ボールがちょっとしか動かないのとすり抜けが起きてしまってます。ホッケーのようにうまく弾くには何が原因でしょうか?
※ちなみに、ゴールと敵はまだ用意しておりません。

白のプレイヤー側です
イメージ説明

赤のボール側です
イメージ説明

physicalMaterialです
イメージ説明

追加
イメージ説明

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+2

パドルの移動方法はどのようにしていますでしょうか?Rigidbodyの位置をスクリプトから変更する場合、方法が不適切だとうまく力が伝達されないかもしれません。
一案としては、パドルのisKinematicをオンにして、MovePositionで動かす方法があるかと思います。
(参考:[Unity] positionとMovePositionの違いを比べた - 弱火でじっくり

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class Mallet : MonoBehaviour
{
    [Range(0.0f, 50.0f)] public float MaxSpeed = 10.0f;
    public Vector2 MovableRangeX = new Vector2(float.NegativeInfinity, float.PositiveInfinity);
    public Vector2 MovableRangeZ = new Vector2(float.NegativeInfinity, float.PositiveInfinity);
    private Vector3 destination;

    private new Rigidbody rigidbody;
    private void Start()
    {
        this.rigidbody = this.GetComponent<Rigidbody>();
        this.rigidbody.isKinematic = true;
        this.destination = this.rigidbody.position;
    }

    private void Update()
    {
        // パドルの移動先をマウススクリーン座標をXZ平面上に投影した位置に設定
        // この辺はご質問者さんの設計に応じて変えてみてください
        this.SetDestination(Input.mousePosition);
    }

    private void SetDestination(Vector3 screenPoint)
    {
        const float dyThreshold = 0.001f;
        var ray = Camera.main.ScreenPointToRay(screenPoint);
        var oy = ray.origin.y;
        var dy = ray.direction.y;
        if (-dy > dyThreshold)
        {
            this.destination = ray.origin - ((oy / dy) * ray.direction);

            // パドルがフィールド外に出られないよう位置を制限
            this.destination.x = Mathf.Clamp(this.destination.x, this.MovableRangeX.x, this.MovableRangeX.y);
            this.destination.z = Mathf.Clamp(this.destination.z, this.MovableRangeZ.x, this.MovableRangeZ.y);
        }
    }


    private void FixedUpdate()
    {
        // パドルの移動量が過大にならないように制限
        var deltaPsition = this.destination - this.rigidbody.position;
        var clampedDeltaPosition = Vector3.ClampMagnitude(deltaPsition, this.MaxSpeed * Time.fixedDeltaTime);
        var newPosition = clampedDeltaPosition + this.rigidbody.position;

        // パドルはキネマティックにしておき、MovePositionで位置を変化させる
        this.rigidbody.MovePosition(newPosition);
    }
}

プレビュー

 めり込み現象について追記

パックを静かにフィールド隅に押しつけてみたところ、確かにパドルがパック内にめり込んでいきますね。
(確認しやすくするため、パックを大きくしています)

めり込み

フィールド壁面はStaticなので物理的作用を受けず、パドルもキネマティックなため物理的作用を受けないということになるでしょう。動くことができるのはパックだけという状況で力の釣り合いを解決した結果、上図のようなめり込んだ状態に落ち着いたのだろうと思います。

パックを無理やり壁面に押しつけようとした場合、感覚的にはパドルが押し返されてめり込まないのが自然でしょうね。destinationをめり込まない位置に補正してやる手もあるかもしれませんが、別な案として、パドルの移動方法を変えてみるというのはどうでしょう。
パドルを非キネマティックな通常の剛体に戻し、移動はパドルにdestination座標へ向かう力をかけることにより行うことにしました(マウスポインタとパドルをダンパー付きの固いバネで繋ぐようなイメージで作ってみました...コード上で記述する代わりに、ジョイントをうまく使って同様の動作をさせることもできそうです)。
パドルが非キネマティックになったので、パックから受けた抗力によってパドルが移動できるようになりました。

using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class Mallet : MonoBehaviour
{
    public Vector2 MovableRangeX = new Vector2(float.NegativeInfinity, float.PositiveInfinity);
    public Vector2 MovableRangeZ = new Vector2(float.NegativeInfinity, float.PositiveInfinity);
    [Range(0.0f, 10000.0f)] public float SpringStiffness = 1000.0f;
    [Range(0.0f, 10000.0f)] public float VelocityDamper = 100.0f;
    [Range(0.0f, 10000.0f)] public float DestinationDamper = 10.0f;

    private Vector3 destination;
    private new Rigidbody rigidbody;

    private void Start()
    {
        this.rigidbody = this.GetComponent<Rigidbody>();
        this.rigidbody.isKinematic = false; // パドルを非キネマティックに変更
        this.destination = this.rigidbody.position;
    }

    private void Update()
    {
        this.SetDestination(Input.mousePosition);
    }

    private void SetDestination(Vector3 screenPoint)
    {
        const float dyThreshold = 0.001f;
        var ray = Camera.main.ScreenPointToRay(screenPoint);
        var oy = ray.origin.y;
        var dy = ray.direction.y;
        if (-dy > dyThreshold)
        {
            this.destination = ray.origin - ((oy / dy) * ray.direction);
            this.destination.x = Mathf.Clamp(this.destination.x, this.MovableRangeX.x, this.MovableRangeX.y);
            this.destination.z = Mathf.Clamp(this.destination.z, this.MovableRangeZ.x, this.MovableRangeZ.y);
        }
    }

    private void FixedUpdate()
    {
        // パドルを目標地点(マウス位置)に引きつける力をかける
        this.rigidbody.AddForce(this.GetSpringForce());
    }

    private Vector3 GetSpringForce()
    {
        var velocity = this.rigidbody.velocity;
        var speed = velocity.magnitude;
        var velocityDirection = speed < 1E-5f ? Vector3.zero : velocity / speed;
        var relativePosition = this.destination - this.rigidbody.position;
        var sqrDistance = relativePosition.sqrMagnitude;

        // 目標地点とパドルの距離に比例する引っぱり力...とりあえず「バネ復元力」と呼ぶことにする
        var springForce = relativePosition * this.SpringStiffness;

        // パドルの速度に比例する抵抗力の大きさ...速度の過大な上昇を防ぐ
        var velocityDamperForceMagnitude = this.VelocityDamper * speed;

        // 目標地点とパドルの距離の2乗に逆比例する抵抗力の大きさ...目標地点に近づいたら急激なブレーキをかける
        var destinationDamperForceMagnitude = this.DestinationDamper / sqrDistance;

        // パドルの運動量の大きさ×秒間フレーム数
        var momentumThreshold = (speed * this.rigidbody.mass) / Time.fixedDeltaTime;

        // 抵抗力の大きさ...位置抵抗と速度抵抗の和
        // ただしmomentumThresholdを越えないようにし、パドルの運動を完全停止させるのに
        // 必要な以上の力が加わらないようにしたい(抵抗力によって運動方向が逆向きになってしまう状況を防ぐ)
        // パドルは速度×質量の運動量を持っており、同量の力積を逆向きに加えれば完全停止する
        // なので、1フレーム中に加わる力積がそれを越えないようにクランプしてやればいいはず
        var damperForceMagnitude = Mathf.Min(
            velocityDamperForceMagnitude + destinationDamperForceMagnitude,
            momentumThreshold);

        // 最終的な力は目標地点方向へのバネ復元力と速度方向と逆方向への抵抗力の和とする
        var force = springForce - (damperForceMagnitude * velocityDirection);
        return force;
    }
}

プレビュー

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/10 22:20

    Bongoさん、ありがとうございます!
    恥ずかしながら移動方法など全く知らずpositionの方を使ってました。
    弾けるようになりました!!

    ただ時々、弾かずパドルとパックがくっついてしまう場面があるのですが何か原因ご存知でしょうか?
    puckの方のコンポーネントの設定などが間違っているのでしょうか?

    キャンセル

  • 2018/06/11 06:57

    すみません、いくらか試してみたもののくっつき現象は現れず、原因ははっきりしないです...

    ご参考までに、私の場合Fixed Timestepは0.0167(60FPS)、パドルとパックにはSphere Colliderを使い(Convex設定したMesh Colliderでもいいとは思いますが、今回のケースで必要になるのは水平方向の衝突判定のみであり、計算コストが低く正確な円を表現できるであろう球の方が適しているかと考えました)、Collision Detectionはパドルとパックが両方動くためContinuous Dynamicとしました。解答の図ではInterpolateがNoneになっていますが、これはInterpolateに変更しました。Interpolateの設定はおそらく物理的挙動には影響せず、FixedUpdateとUpdateの実行周期が異なる場合の差異を吸収してくれる機能なのだろうと思いますので、たぶんくっつき現象の解消にはつながらない気はしますが...

    常にくっつきが起こるのではなく、たまにしか起こらないのであれば、反発係数などの設定には決定的なミスはなくて、物理シミュレーションの精度が不足しているだけなのかもしれません。
    計算コストが多少上がっても許容できるなら、Fixed Timestepをもっと短くしてみると(たとえば120FPS...0.00835、200FPS...0.005といったように)変化はあるでしょうか?

    キャンセル

  • 2018/06/12 00:39 編集

    すいません。くっつくというよりすり抜けるでした!
    端っこに寄った時に起こります。
    新しい画像添付してます。お時間お掛けして申し訳ありません。

    キャンセル

  • 2018/06/13 02:09

    Bongoさんありがとうございます。
    連日ご回答頂きありがとうございました!
    メンターになってもらいたいぐらいです笑。
    助かりました!

    キャンセル

0

すり抜けは、collision detectionをcontinuousにすればいいです。これで当たり判定が継続的なものになります。また、physics materialで跳ね返りやすくするというのも一つの手です

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/12 00:49

    新しく添付したんですが、隅っこに行った場合にすり抜けが起きてしまいます。何か原因ありますでしょうか?

    キャンセル

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

  • Unity

    4397questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。