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

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

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

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

Q&A

解決済

2回答

3747閲覧

Unity2Dでキネマティック-キネマティックの衝突反射処理を自前で用意したい

Nicholas

総合スコア0

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

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

0グッド

0クリップ

投稿2020/09/01 05:03

前提・実現したいこと

Unity2Dでブロック崩しに変化を加えたゲームを作っています。
当初はボール-ブロックの反射を動的-キネマティックで2D物理演算に任せていましたが、
稀に意図しない方向で反射してしまったり、速さが変化してしまったり、細かい動きを自分で調節したくなったため、
ボールを動的ではなくキネマティックで動作させることにして、
ボール-ブロック(キネマティック-キネマティック)の反射を2D物理演算に頼らず自前のスクリプトで動作させたいです。

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

反射処理のスクリプト自体はVector2 Reflect(Vector2 inDirection, Vector2 inNormal)を使ったり、
あるいは自分で内積などを計算して用意することができるのですが、
どちらの場合も衝突面の法線ベクトルinNormalが必要になります。
このときの法線ベクトルを取得する方法が分かりません。

該当のソースコード

ソースコード

試したこと

[Unity] 衝突判定における、IsTriggerとRigidbodyとIsKinematicのパターンを実験
上記サイトによるとキネマティック-キネマティックの場合はisTriggerのOnTriggerEnter2Dを呼び出すことはできるものの、OnCollisionEnter2Dを呼び出すことはできないと考えられます。実際試してみたところできないようでした。
OnTriggerEnter2Dでは(以前のバージョンでは取得できたこともあるらしいのですが)接触点を取得することができません。
それゆえ法線ベクトルを取得できませんでした。

また、3Dの衝突に関しては__[プロジェクト設定]>[物理]>Contact Pairs Mode__でキネマティック-キネマティックのCollisionを可能にできるようですが、
2D物理についてこれに相当する設定項目は見つけられませんでした。

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

Unityバージョン2020.1.3f1

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

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

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

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

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

guest

回答2

0

自己解決して解決方法とコードを書いたのですが502 Server Errorで消失してしまいました。

Getting a Collision2D from an OnTriggerEnter2Dのトピックを参考に、
OnTriggerEnter2D内でRayCastして相手の面の法線ベクトルを取得してVector2.Reflectを用いることで実装できました。
そのままだと自分自身のColliderを取得してしまうのでLayerMaskを使い、
transform.positionからRigidbody2D.velocity方向へ自分自身の大きさ程度の長さのRayを放射することで上手くいきました。

投稿2020/09/02 06:55

Nicholas

総合スコア0

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

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

0

自己解決

Getting a Collision2D from an OnTriggerEnter2D
上記のトピックを参考に、Raycastを使った方法で解決しました。

お互いをキネマティックにしてColliderをTriggerにする。

C#

1OnTriggerEnter2D(Collider2D collision) 2{ 3 // できればここではなくあらかじめキャッシュしておく 4 Rigidbody2D rigidbody = GetComponent<Rigidbody2D>(); 5 CircleCollider2D cicleCollider = GetComponent<CircleCollider2D>(); 6 7 // 自分自身(Ballレイヤー)にだけ反応させない 8 int layerNo = LayerMask.NameToLayer("Ball"); 9 int layerMask = ~(1 << layerNo); 10 11 // RayCastして衝突相手の垂線ベクトルを取得 12 RaycastHit2D hit = Physics2D.Raycast(transform.position, rigidbody.velocity, circleCollider.radius, layerMask); 13 Vector2 normal = hit.normal; 14 15 // 反射処理 16 Vector2 reflectVector = Vector2.Reflect(rigidbody.velocity, normal); 17 rigidbody.velocity = reflectVector; 18}

自身のRigidbody2DとCollider2Dを取得しておく。
layerMaskしないとRayCastで自分自身のCollider2Dを取得してしまっていたのでマスクする。
自身の位置(transform.position)から進行方向(rigidbody.velocity)へ半径長さ(circleCollider.radius)のRayを放射する。
衝突した面の法線ベクトル(normal)を取得する。
自身の速度と法線ベクトルを使って反射後のベクトルを取得して設定する。

BallやCircle Colliderでなくても、放射するRayの長さを上手く設定すれば問題ないと思います。


追記(2020.09.02 21:50)
正面衝突であればこれで問題ないものの、角度がついた状態でぶつかることを考えると、
進行方向上へ放射したRayのぶつかった先と実際の衝突位置は異なる問題がありました。
これの根本的解決方法は今のところ思いつきませんが、少なくとも半径長さのRayでは角度がついた衝突でRayの衝突先を見失う可能性があるため、これより多少長さを伸ばす必要性はあります。


追記(2020.09.04 19:00)
ボールの大きさが無視できるならRayCastで問題ないはずですが、実際は大きさを持っているのでRayCastの代わりにCircleCastを使うとよさそうです。
Circleの半径をボールの半径と同じにしてCastすれば理論上は問題ないように思われます。
しかし実際に動かしてみると、処理速度の関係なのか、たまに壁をすり抜けてしまうことがありました。
その不具合を抜きにしても、結局やっていることは(デメリットを含めて)Dynamicでやっていることと同様なので、全てキネマティックだと全てTriggerにできるというメリット以外は素直にDynamicで動かすことに諦めた方がいいかもしれません。

投稿2020/09/02 06:47

編集2020/09/04 10:01
Nicholas

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問