LoginSignup
0
0

More than 1 year has passed since last update.

スーパーボールを重ねて落とす「すっとびボール」を物理エンジン(Unity)で再現する

Last updated at Posted at 2021-11-27

概要

Unityは物理エンジンであり、
これを用いて物理実験をシミュレーションすることが可能である。
今回はUnityを用いて「すっとびボール」をシミュレーションする。

環境

Unity 2019.3.7f1

仕様

上の段のボール(直径1m)を高さ10mから落とす。
下の段のボール(直径1.5m)が床に着地したとき、上の段のボールが高く跳ねる。

実装

まず、床を実装する。
ヒエラルキーウィンドウで座標が(0,0,0)でScaleが(1,1,1)の
Planeを作る。

次に、スーパーボールとなる球を実装する。
ヒエラルキーウィンドウにScaleが(1,1,1)で座標が(0,10,0)のSphereをBall1とし、
Scaleが(1.5,1.5,1.5)で座標が(0,8.75,0)のSphereをBall2として作る。

すなわち、上の段のボールがBall1、下の段のボールがBall2となる。

(Ball1とBall2のマテリアルを変更しておくと見やすい。私はBall1を赤、Ball2を青とした。)

Ball1とBall2にRigidbodyをアタッチし、
Ball1の方のRigidbodyのMassを1に、Ball2の方のRigidbodyのMassを3にする。

ボール用のPhysic Materialを作る。
プロジェクトウィンドウにPhysic Materialを作り、
Bouncinessを0.9にする。
これをBall1とBall2のSphere ColliderのMaterialに入れる。

ここまでやれば、シーンウィンドウが以下の画像のようになっているはずである。

suttobi1.PNG

ここからスクリプトを書いていく。

スクリプトをこのように書く。

GalileanCannon.cs
public class GalileanCannon : MonoBehaviour
{
    public Rigidbody ball1;
    public Rigidbody ball2;
    private float v0;
    private float v1;
    private float v2;
    private float e1 = 0.9f;
    private float e0 = 0.9f;
    private bool flag = true;
    private bool isGrounded = false;

    void Start()
    {
        ball1.isKinematic = true;
        ball2.isKinematic = true;

        //速度を求める
        v0 = Mathf.Sqrt(2 * 9.81f * (ball2.transform.position.y - (ball2.transform.localScale.y / 2.0f)));
        v1 = ((e0 * ball2.mass - (e0 * e1 + e1 + 1) * ball1.mass) * v0) / (ball2.mass + ball1.mass);
        v2 = (((e0 * e1 + e0 + e1) * ball2.mass - ball1.mass) * v0) / (ball2.mass + ball1.mass);
    }

    void FixedUpdate()
    {
        //スペースキーを押したら開始
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ball1.isKinematic = false;
            ball2.isKinematic = false;
        }

        //ball2が地面についたらball1が跳ねる
        if(isGrounded == true && flag == true)
        {
            ball2.velocity = new Vector3(0, v1, 0);
            ball1.velocity = new Vector3(0, v2, 0);
            isGrounded = false;
            flag = false;
        }
    }
    void OnCollisionEnter(Collision collision)
    {
        //ball2が地面と接触したかを一回だけ判定する
        if (collision.gameObject.name == "Ball2" && flag == true)
        {
            isGrounded = true;
        }
    }
}

変数を見てみよう。

GalileanCannon.cs
    public Rigidbody ball1;
    public Rigidbody ball2;
    private float v0;
    private float v1;
    private float v2;
    private float e1 = 0.9f;
    private float e0 = 0.9f;
    private bool flag = true;
    private bool isGrounded = false;

ball1は上の段のボール、ball2は下の段のボールである。

v0はball2が地面と接触する直前の速度、
v1はball2が地面と衝突したあとのball2の速度、
v2はball2が地面と衝突したあとのball1の速度である。

e1、e0は反発係数である。

isGroundedは地面に接触したかどうかを判定するものであり、
flagは地面と衝突した最初の1回だけシミュレーションを行うための判定である。

スクリプトの動作は、スクリプトに書いてあるので割愛する。

このスクリプトをPlaneに加え、
スクリプトのball1とball2にそれぞれBall1とBall2を入れる。

これで完成である。

あとは再生ボタンを押してスペースキーを押せばシミュレーションが始まる。
カメラの位置を見やすい位置に変更するのを忘れずに。

まとめ

オブジェクトを作り、計算式をスクリプトに書く。
計算式さえ理解すれば、スクリプトはさほど難しくはない。
今回はこのようなスクリプトになったが、もっと良いスクリプトがありそうなので、
自分でも考えてみてください。

参考文献

塚本浩司:「すっとびボールの研究史 物理教育 49巻6号」,2001年12月
P.537-541

Wikipedia
https://ja.wikipedia.org/wiki/%E3%81%99%E3%81%A3%E3%81%A8%E3%81%B3%E3%83%9C%E3%83%BC%E3%83%AB
(Wikipediaの途中式が異なっているので、お気をつけて)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0