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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

2695閲覧

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

nakamu

総合スコア82

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2018/06/09 06:08

編集2018/06/11 15:39

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

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

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

physicalMaterialです
イメージ説明

追加
イメージ説明

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

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

C#

1using UnityEngine; 2 3[RequireComponent(typeof(Rigidbody))] 4public class Mallet : MonoBehaviour 5{ 6 [Range(0.0f, 50.0f)] public float MaxSpeed = 10.0f; 7 public Vector2 MovableRangeX = new Vector2(float.NegativeInfinity, float.PositiveInfinity); 8 public Vector2 MovableRangeZ = new Vector2(float.NegativeInfinity, float.PositiveInfinity); 9 private Vector3 destination; 10 11 private new Rigidbody rigidbody; 12 private void Start() 13 { 14 this.rigidbody = this.GetComponent<Rigidbody>(); 15 this.rigidbody.isKinematic = true; 16 this.destination = this.rigidbody.position; 17 } 18 19 private void Update() 20 { 21 // パドルの移動先をマウススクリーン座標をXZ平面上に投影した位置に設定 22 // この辺はご質問者さんの設計に応じて変えてみてください 23 this.SetDestination(Input.mousePosition); 24 } 25 26 private void SetDestination(Vector3 screenPoint) 27 { 28 const float dyThreshold = 0.001f; 29 var ray = Camera.main.ScreenPointToRay(screenPoint); 30 var oy = ray.origin.y; 31 var dy = ray.direction.y; 32 if (-dy > dyThreshold) 33 { 34 this.destination = ray.origin - ((oy / dy) * ray.direction); 35 36 // パドルがフィールド外に出られないよう位置を制限 37 this.destination.x = Mathf.Clamp(this.destination.x, this.MovableRangeX.x, this.MovableRangeX.y); 38 this.destination.z = Mathf.Clamp(this.destination.z, this.MovableRangeZ.x, this.MovableRangeZ.y); 39 } 40 } 41 42 43 private void FixedUpdate() 44 { 45 // パドルの移動量が過大にならないように制限 46 var deltaPsition = this.destination - this.rigidbody.position; 47 var clampedDeltaPosition = Vector3.ClampMagnitude(deltaPsition, this.MaxSpeed * Time.fixedDeltaTime); 48 var newPosition = clampedDeltaPosition + this.rigidbody.position; 49 50 // パドルはキネマティックにしておき、MovePositionで位置を変化させる 51 this.rigidbody.MovePosition(newPosition); 52 } 53}

プレビュー

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

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

めり込み

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

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

C#

1using UnityEngine; 2 3[RequireComponent(typeof(Rigidbody))] 4public class Mallet : MonoBehaviour 5{ 6 public Vector2 MovableRangeX = new Vector2(float.NegativeInfinity, float.PositiveInfinity); 7 public Vector2 MovableRangeZ = new Vector2(float.NegativeInfinity, float.PositiveInfinity); 8 [Range(0.0f, 10000.0f)] public float SpringStiffness = 1000.0f; 9 [Range(0.0f, 10000.0f)] public float VelocityDamper = 100.0f; 10 [Range(0.0f, 10000.0f)] public float DestinationDamper = 10.0f; 11 12 private Vector3 destination; 13 private new Rigidbody rigidbody; 14 15 private void Start() 16 { 17 this.rigidbody = this.GetComponent<Rigidbody>(); 18 this.rigidbody.isKinematic = false; // パドルを非キネマティックに変更 19 this.destination = this.rigidbody.position; 20 } 21 22 private void Update() 23 { 24 this.SetDestination(Input.mousePosition); 25 } 26 27 private void SetDestination(Vector3 screenPoint) 28 { 29 const float dyThreshold = 0.001f; 30 var ray = Camera.main.ScreenPointToRay(screenPoint); 31 var oy = ray.origin.y; 32 var dy = ray.direction.y; 33 if (-dy > dyThreshold) 34 { 35 this.destination = ray.origin - ((oy / dy) * ray.direction); 36 this.destination.x = Mathf.Clamp(this.destination.x, this.MovableRangeX.x, this.MovableRangeX.y); 37 this.destination.z = Mathf.Clamp(this.destination.z, this.MovableRangeZ.x, this.MovableRangeZ.y); 38 } 39 } 40 41 private void FixedUpdate() 42 { 43 // パドルを目標地点(マウス位置)に引きつける力をかける 44 this.rigidbody.AddForce(this.GetSpringForce()); 45 } 46 47 private Vector3 GetSpringForce() 48 { 49 var velocity = this.rigidbody.velocity; 50 var speed = velocity.magnitude; 51 var velocityDirection = speed < 1E-5f ? Vector3.zero : velocity / speed; 52 var relativePosition = this.destination - this.rigidbody.position; 53 var sqrDistance = relativePosition.sqrMagnitude; 54 55 // 目標地点とパドルの距離に比例する引っぱり力...とりあえず「バネ復元力」と呼ぶことにする 56 var springForce = relativePosition * this.SpringStiffness; 57 58 // パドルの速度に比例する抵抗力の大きさ...速度の過大な上昇を防ぐ 59 var velocityDamperForceMagnitude = this.VelocityDamper * speed; 60 61 // 目標地点とパドルの距離の2乗に逆比例する抵抗力の大きさ...目標地点に近づいたら急激なブレーキをかける 62 var destinationDamperForceMagnitude = this.DestinationDamper / sqrDistance; 63 64 // パドルの運動量の大きさ×秒間フレーム数 65 var momentumThreshold = (speed * this.rigidbody.mass) / Time.fixedDeltaTime; 66 67 // 抵抗力の大きさ...位置抵抗と速度抵抗の和 68 // ただしmomentumThresholdを越えないようにし、パドルの運動を完全停止させるのに 69 // 必要な以上の力が加わらないようにしたい(抵抗力によって運動方向が逆向きになってしまう状況を防ぐ) 70 // パドルは速度×質量の運動量を持っており、同量の力積を逆向きに加えれば完全停止する 71 // なので、1フレーム中に加わる力積がそれを越えないようにクランプしてやればいいはず 72 var damperForceMagnitude = Mathf.Min( 73 velocityDamperForceMagnitude + destinationDamperForceMagnitude, 74 momentumThreshold); 75 76 // 最終的な力は目標地点方向へのバネ復元力と速度方向と逆方向への抵抗力の和とする 77 var force = springForce - (damperForceMagnitude * velocityDirection); 78 return force; 79 } 80}

プレビュー

投稿2018/06/10 00:38

編集2018/06/11 18:56
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nakamu

2018/06/10 13:20

Bongoさん、ありがとうございます! 恥ずかしながら移動方法など全く知らずpositionの方を使ってました。 弾けるようになりました!! ただ時々、弾かずパドルとパックがくっついてしまう場面があるのですが何か原因ご存知でしょうか? puckの方のコンポーネントの設定などが間違っているのでしょうか?
Bongo

2018/06/10 21: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といったように)変化はあるでしょうか?
nakamu

2018/06/11 15:41 編集

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

2018/06/12 17:09

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

0

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

投稿2018/06/11 09:23

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nakamu

2018/06/11 15:49

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問