こんな方針でどうでしょう。なお、この方法でやる場合、ご質問者さんの現在のコードballPos_x += vx * Math.Cos(deg * (Math.PI / 180));
、ballPos_y += vy * Math.Sin(deg * (Math.PI / 180)) + g * t;
のような「毎回初速度と経過時間から現在の速度を求め、ボールの位置に加算する」方式はちょっと相性が悪いかと思います(跳ね返り後のボールの動きをご想像いただくとお分かりかと思いますが、跳ね返った後は軌道が変わるので、発射時の式のままではおかしな動きになってしまうはずです...あえてやるとしたら、「跳ね返り地点から跳ね返り方向へ跳ね返り速度でボールを再発射する」イメージで実装することになるでしょうか)。それよりも、
- ボールの位置のほかに、「ボールの現在速度」を保持する変数を追加する(仮に
ballVel_x
、ballVel_y
とする)
- ボール発射時に現在速度に初速度を代入する...
ballVel_x = vx * Math.Cos(deg * (Math.PI / 180));
、ballVel_y = vy * Math.Sin(deg * (Math.PI / 180));
Update
内では「現在速度に加速度を加算」してから「現在位置に現在速度を加算」する...ballVel_y += g;
、ballPos_x += ballVel_x;
、ballPos_y += ballVel_y;
といった方式の方が跳ね返りを実装しやすいように思います。
当たり判定ですが、まず、ボールの中心が下図1~9のどのエリアにいるか特定し、エリアに応じて判定方法を分けることにします。どのエリアにいるかはボール座標とピクチャーボックスの4辺の位置L、T、R、Bを比較すればいいでしょう。
そして、ボールのエリアに応じてピクチャーボックスとの衝突判定を行い、併せてボールを跳ね返らせるのに使う、衝突面の法線ベクトルを求めます。各エリアごとに見てみると...
- 衝突する可能性があるのはa点。ボール中心とa点の距離を求め、ボール半径以下なら衝突とする。法線は(ボール中心座標 - a点座標) / ボール中心とa点の距離。
- 衝突する可能性があるのは辺ab。T - ボール座標Yがボール半径以下なら衝突とする。法線は(0、-1)。
- 衝突する可能性があるのはb点。ボール中心とb点の距離を求め、ボール半径以下なら衝突とする。法線は(ボール中心座標 - b点座標) / ボール中心とb点の距離。
- 衝突する可能性があるのは辺bd。ボール座標X - Rがボール半径以下なら衝突とする。法線は(1、0)。
- 衝突する可能性があるのはd点。ボール中心とd点の距離を求め、ボール半径以下なら衝突とする。法線は(ボール中心座標 - d点座標) / ボール中心とd点の距離。
- 衝突する可能性があるのは辺cd。ボール座標Y - Bがボール半径以下なら衝突とする。法線は(0、1)。
- 衝突する可能性があるのはc点。ボール中心とc点の距離を求め、ボール半径以下なら衝突とする。法線は(ボール中心座標 - c点座標) / ボール中心とc点の距離。
- 衝突する可能性があるのは辺ac。L - ボール座標Xがボール半径以下なら衝突とする。法線は(-1、0)。
- ボールが速すぎると、ボールの縁で衝突判定されることなくこのエリアに来てしまうと予想される。このケースに対処する方法の一例としては、このエリアに来た場合はボールを1フレーム前の位置に戻し、短い間隔で少しずつ移動させてみて当たり判定を行うという手が考えられる。
ここまででボールがピクチャーボックスのどの辺、あるいはどの角に当たったか、そして衝突面の法線が分かるので、後は...
- 辺abに当たったケースでは「ボールがカゴに入った」と判定する
- それ以外では跳ね返り処理を行う...C# - 当たり判定 反射するようにするには(103369)|teratailでozwkさんのおっしゃった方法が有効でしょう。「ボールの現在速度ベクトルと法線ベクトルの内積を求める」→「現在速度ベクトル - (2 * 内積 * 法線ベクトル)を新たな現在速度ベクトルとする」でいけるはずです。
こんな感じでいかがでしょうか...?どの方向から当たったかの場合分けがややこしいかもしれませんが、紙に図を描いたりして動きをイメージしてみるのもいいんじゃないかと思います。