見出し画像

【Unity】ブロック崩しの「あの挙動」を再現した話

最初にゲーム作るならブロック崩しから
なんて言われるくらいには簡単に作れちゃうブロック崩し。
それっぽい動きするだけのもんなら、RigidBodyとコライダーつけて少しコード書くだけでできちゃうのでガチで簡単。

ですが、市販されてるブロック崩しを見てみると単純な物理演算だけで玉を跳ね返してるわけではないようです。
今回は、ブロック崩しの「あの挙動」を上手いこと再現してみたという話です。

そもそも「あの挙動」って何よ?

参考ゲーム:ふつうのブロックくずし

タイトルなし

パドルのどこに当たったかによって玉の軌道を決める動きのことです。
具体的には以下の通りです。

・パドルの左側に当たれば左上
・パドルの右側に当たれば右上
・真ん中に当たれば真上へ上がる

画像1

(この図は逆にわかりにくいかも…)

再現方法

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

public class BallController : MonoBehaviour
{

   [SerializeField]
   private float speed = 5f;

   Rigidbody2D rigid;

   private void Awake()
   {
       rigid = GetComponent<Rigidbody2D>();
   }

   void Start()
   {
       speed = Firstspeed;
       Vector2 force = new Vector2(1, -1) * speed / Mathf.Sqrt(2);

       rigid.AddForce(force, ForceMode2D.Impulse);
   }
   
   private void OnCollisionEnter2D(Collision2D collision)
   {
       //パドル(プレイヤー)に当たったら、当たった位置によってvelocityを変更
       if(collision.gameObject.tag == "Player")
       {
           float force_x;
           float force_y;

           //x軸の差によってベクトルの決定を行う
           float dis_x = transform.position.x - collision.gameObject.transform.position.x;
           //コサインの値がdis_xになる角度を求める
           float radian = Mathf.Acos(dis_x);
           force_x = speed * dis_x;
           force_y = speed * Mathf.Sin(radian);
           if (float.IsNaN(force_y))
           {
               force_y = 0;
           }

           Vector2 force = new Vector2(force_x, force_y);

           rigid.velocity = force;
       }
   }
}
※2Dでの制作のため、Vector2やRigidBody2Dなどを使用しています。
3Dの場合でもほぼ同じです。

その1.パドルと衝突したらvelocityを書き換える通常RigidBodyで移動する場合は、AddForceを使います。
しかし今回は完全に物理法則を無視した動きになるため、RigidBodyの速度を直接書き換えます。

Vector2 force = new Vector2(force_x, force_y);

rigid.velocity = force;

その2.パドルと玉の位置関係からベクトルを求める
velocityはVector3型(このコードではVector2)なので、三角関数を用いてvelocityへ代入するベクトルを求めます。

まず、衝突時のパドルと玉のx座標の差(玉のx座標-パドルのx座標、以降dis_x)からMathf.Acosを用いて角度を求めます。

//x軸の差によってベクトルの決定を行う
float dis_x = transform.position.x - collision.gameObject.transform.position.x;

//コサインの値がdis_xになる角度を求める
float radian = Mathf.Acos(dis_x);​
※今回はdis_xの最大値が0.9だったためそのまま計算に用いましたが、場合によってdis_xを最大値(より少し大きい値)で割る必要があります。

画像2

次に、ベクトルの各成分をそれぞれ求めます。
x成分はMathf.Cos(上で求めたパドルと玉のx座標の差でもいい)、
y成分はMathf.Sinを使いましょう。

force_x = speed * dis_x;
force_y = speed * Mathf.Sin(radian);

if (float.IsNaN(force_y))
{
     force_y = 0;
}
y成分の値が非数字(NaN)になりエラーを吐くことがあるので、if (float.IsNaN())で判別してから0を代入するようにしましょう。

実際の動き

タイトルなし

(変な画像使ってることはスルーで)

それらしい動きにはなってるかと思います。
ブロック崩し作ったけど動きがいまいちだな〜、と思った時の参考程度になれば幸いです。

今回お見せしたブロック崩しは、ミニゲーム集の1作品としてこちらのサイトで公開させていただいています。
是非遊んでみてください。


いいなと思ったら応援しよう!