MIS.W 公式ブログ

早稲田大学公認、情報系創作サークル「早稲田大学経営情報学会」(MIS.W)の公式ブログです!

Unityで宇宙を創成する【カウントダウンカレンダー2020冬5日目】

55代のT_SUM_Uです。最近の悩みは、朝に食べるつもりで茹でたおいたゆで卵を食べ忘れることです。

本題に入りましょう。Unityで宇宙を創成します。宇宙と言えば星、星と言えば万有引力です。Unityで万有引力を実装し、テクスチャとかSkyboxとかをいじって宇宙を作ってみましょう。

f:id:T_SUM_U:20201216191600p:plain
目的物

万有引力ってのは物体の間に働く引力の事でしたね。その力の大きさFは万有引力定数Gと2つの物体の質量mとM、そして2物体間の距離rで次のように表されるのでした。

F =G \frac{Mm}{r^{2}}

なのでUnityで次のようなスクリプトを書きます。(都合上、万有引力定数を6.67にしています。)

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

public class UniversalGravity : MonoBehaviour
{
    float G = 6.67f;
    public GameObject target;
    Rigidbody rb, trb;
    void Start()
    {
        rb = GetComponent<Rigidbody>();
        trb = target.GetComponent<Rigidbody>();
    }

    void FixedUpdate()
    {
        Vector3 rVec = target.transform.position - transform.position;
        float r = rVec.magnitude;
        float F = G * (rb.mass * trb.mass) / (r * r);

        rb.AddForce(F * rVec.normalized);
    }
}

そしたらUnityで2つの球体を設置し、それぞれに上記のスクリプトを付けます。Rigidbodyコンポーネントを付けてUseGravityのチェックを外すのも忘れずに。

f:id:T_SUM_U:20201216094124p:plain
2つの球体を設置した様子(右:Sphere、左:Sphere (1))

球体のInspectorのUniversal Gravity (Script)のTarget欄にお互いの球体を入れます。

f:id:T_SUM_U:20201216094726p:plain
Sphereの場合

この状態で再生してみると次のURLのようになります。

t-sum-u.github.io

なんかそれっぽく動いてますね。では球体を増やしてみましょう。物体にかかる万有引力の合力はそれぞれの万有引力の合計なので、スクリプトを次のように書き換えます。

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

public class UniversalGravity : MonoBehaviour
{
    float G = 6.67f;
    public GameObject[] AstroObjs;
    Rigidbody[] rbs;
    int count;
    void Start()
    {
        AstroObjs = GameObject.FindGameObjectsWithTag("AstroObj");
        count = AstroObjs.Length;
        Array.Resize(ref rbs, count);
        for (int i = 0; i < count; i++)
        {
            rbs[i] = AstroObjs[i].GetComponent<Rigidbody>();
            rbs[i].velocity = new Vector3(UnityEngine.Random.Range(-1.6f, 1.6f), UnityEngine.Random.Range(-1.6f, 1.6f), UnityEngine.Random.Range(-1.6f, 1.6f));
        }
    }

    void FixedUpdate()
    {
        for (int i = 0; i < count; i++)
        {
            Vector3 F = Vector3.zero;
            for (int j = 0; j < count; j++)
            {
                if (i == j)
                {
                    continue;
                }
                Vector3 rVec = AstroObjs[j].transform.position - AstroObjs[i].transform.position;
                float r = rVec.magnitude;
                F += rVec.normalized * G * (rbs[i].mass * rbs[j].mass) / (r * r);
            }
            rbs[i].AddForce(F);
        }
    }
}

ついでに初速度も与えてみました。球体が複数あるので、球体に付与されたタグから自動で球体を取得するようにします。 Unity側で球体を4つに増やし、それらの球体全てに"AstroObj"のタグを付与します。スクリプトは適当なゲームオブジェクトに付けます。

f:id:T_SUM_U:20201216110304p:plain
スクリプトを付けたゲームオブジェクト
f:id:T_SUM_U:20201216110345p:plain
球体を増やした

再生したら次のようになります。

t-sum-u.github.io

なかなかそれっぽくなってきましたね。じゃあこの球体たちを惑星として、恒星も追加してみましょう。 f:id:T_SUM_U:20201216112722p:plain

真ん中のでかいのが恒星で。それ以外は惑星です。惑星の質量は1、恒星の質量は100にしました。この惑星に初速度を与えて、恒星の周りを公転するようにします。面倒なので惑星と恒星の間だけの万有引力を考えることにしましょう。惑星の等速円運動の向心力と万有引力の大きさが等しくなるので、惑星の公転の速さをv、惑星の質量をm、恒星の質量をM、惑星と恒星との距離をrと置くと

m{\frac{v^{2}}r} =G \frac{Mm}{r^{2}}

v=\sqrt{\frac{GM}r}

となります。これが惑星に与える初速度となります。 実際にやってみると次のようになります。

t-sum-u.github.io

恒星系ができました。でも光の向きがおかしいですね。既存の光源を消して恒星を発光させましょう。同時にSkyboxも変え、更に天体にテクスチャを付けてみましょう。ついでに惑星も増やして、大きさとか質量とかいろいろ変えてみます。 出来たのがこちらです。

t-sum-u.github.io

かなり宇宙になったと思います。 この万有引力ですが、結構いろんなことに応用できます。例えばこんな風に


ColorLines

またこれはここで実装した万有引力を主な題材としたゲームです。

www.freem.ne.jp

力の方向を逆にすれば斥力になるので、それを利用して電子の動きとかも再現できそうですね。そろそろunity1weekが始まるので参考にしてみてください。

味が薄いので塩を入れたが今度は濃すぎたので水を入れた結果さらに薄まった味噌汁みたいな記事を最後まで読んでいただきありがとうございました。 明日は54代の藤汐さんです。

おまけ

f:id:T_SUM_U:20201216193513p:plain

f:id:T_SUM_U:20201216202552p:plain
魔貫光殺砲