Unityでローレンツアトラクタを3Dで描いてみた

Unityでローレンツアトラクタを3Dで描いてみた。
基本的にはpygletで描画したときと同じであるが、C#Scriptでカメラをアトラクタの中心に向けて回転させるようにした。

f:id:willwealth:20200123132414p:plain
Lorenz Attractor 3D by Unity
アトラクタ―生成のためにGameRootというEmpty Objectを作成し、
以下のC#ScriptのAssetである泥臭いコードのAttractor.csをGameRootに紐づける(Attractor.csをGameRootにドラッグ&ドロップ)。
Escで終了させるコードはネットから拝借。Unity:ゲームを終了させる - Web開発など
(日本語環境に情報があると助かります!ありがとうございます(-_-))

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

public class Attractor : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        createAttractor(48000);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.Escape)) Quit();
        if(Input.GetKeyDown(KeyCode.S))
        {
             ScreenCapture.CaptureScreenshot("image.png");
             Debug.Log("screenshot");
        }
    }

    void Quit() {
        #if UNITY_EDITOR
            UnityEditor.EditorApplication.isPlaying = false;
        #elif UNITY_STANDALONE
            UnityEngine.Application.Quit();
        #endif
    }

    void createSphere(float x, float y, float z){
        float size = 0.5f;
        var obj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
        obj.GetComponent<Renderer>().material.color = Color.red;
        obj.transform.Translate(x,y,z);
        obj.transform.localScale = new Vector3(size,size,size);
    }

    void createAttractor(int iteration){
        float param_s = 10.0f;
        float param_r = 50.0f;
        float param_b = 2.6667f;
        float delta_t = 0.001f;

        float start_x = 5.0f;
        float start_y = 8.0f;
        float start_z = 10.0f;
        int interval = 10;

        float x;
        float y;
        float z;

        float xx;
        float yy;
        float zz;

        x = start_x;
        y = start_y;
        z = start_z;

        for(var i=0;i < iteration; i++){
            xx = x + param_s * ( -x + y ) * delta_t;
            yy = y + ( -x * z + param_r * x - y ) * delta_t;
            zz = z + ( x * y - param_b * z ) * delta_t;

            if ( i % interval == 0 ) {
                createSphere(x,y,z);
            }
            x = xx;
            y = yy;
            z = zz;
        }
    }
}

Main CameraのPositionのZを-15に変更。
カメラをアトラクタの周りに回転させるために以下のスクリプトcameraCtrl.csをMain Cameaに紐づける。
RotateAroundという関数を使用すると、カメラをある方向に向けて回転させることができるということネットで学ぶことができました。【Unity入門】キー入力でプレイヤーを中心にカメラを回転させる方法! | もぎブログ
ありがとうございます。(^^♪

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

public class cameraCtrl : MonoBehaviour
{
    private float rotateSpeed = 10.0f;
    private Vector3 pos;
    private float angle;

    // Start is called before the first frame update
    void Start()
    {
         pos = new Vector3(0.195f,0,45.42f);     
         angle = rotateSpeed * Time.deltaTime;
    }

    // Update is called once per frame
    void Update()
    {
        transform.RotateAround(pos, Vector3.up, angle);
    }
}

上のコードには反映させていませんが、画像の保存には、Qiitaコミュニティの@Nekomasuさんの記事
【Unity】Unityでスクリーンショットを撮る時に必要なTipsまとめ - Qiita
を参考に、というかパクらせていただきました。助かりました。

追記)
画像保存のためにAttractor.csのupdate()に以下のコードを追加した。

if(Input.GetKeyDown(KeyCode.S))
{
   ScreenCapture.CaptureScreenshot("image.png");
   Debug.Log("screenshot");
}

この箇所以外は私には理解できそうになく、幸いこの箇所を追加したら"s"キーを押したら画像が保存されました。
Unityはゆっくりと最後尾くらいを歩いていくとしよう。

2020.8.31 追記
読み返して「あれ?どうやったんだっけ?」という状況だったので補足説明を追記しておこう。
objectはscriptで作成するのだが、まずは空のgame objectを作成する。

f:id:willwealth:20200831134926j:plainf:id:willwealth:20200831134328j:plain
空のGame Objectを作成

すると以下のようにGameObjectが追加される。

f:id:willwealth:20200831134738j:plain
追加されたGameObject

次に以下の2つのc#scriptを作成する。

  • Attractor.cs(GameObjectに紐づけ)
  • CameraCtrl.cs (Main Cameraに紐づけ)
f:id:willwealth:20200831134328j:plain
C#Scriptの作成

作成されるとAssetsにファイルが作成される。これらのファイルの中身を書き換え、ドラッグ&ドロップで各々objectに紐づける。
ちょっとした気分でクラス名の頭を小文字から大文字にしたら紐づけでエラーになったが、どうもクラス名とファイル名は同じでなければならないようだ。常識なのかもしれないが知らなかった。
今回試しにCameraCtrl.csを以下のように変更し、ひとコマごとにイメージを保存するようにしてみた。

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

public class CameraCtrl : MonoBehaviour
{
    //private float rotateSpeed = 10.0f;
    private float rotateSpeed = 1.0f;

    private Vector3 pos;
    private float angle;

    private string fname = "";
    private int num = 0; 

    // Start is called before the first frame update
    void Start()
    {
         pos = new Vector3(0.195f,0,45.42f);     
   
         angle = rotateSpeed * Time.deltaTime;
    }

    // Update is called once per frame
    void Update()
    {
        transform.RotateAround(pos, Vector3.up, angle);
        fname = "Picture\\img" + num.ToString("D6") + ".jpg";
        ScreenCapture.CaptureScreenshot(fname);
        num++;
    }
}

イメージは保存できたが、ffmpegでmp4に変換しようとしたらエラーになってしまった。
jpg形式ではなくpng形式で保存したら変換はできたが、今度は再生時にエラーになってしまった。
なぜだろう?自己の力量を鑑み、追求せず諦めることにした。

以上