vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

【Unity】【C#】ボール(Sphere)を転がしてみる その1

はじめに

Unityを使って、画面上にボールを転がすアプリを作ってみることにします。

仕様

  • 画面上にはボール(Sphere)を一つ配置する
  • 画面上をクリックしたら、その場所にボールを転がす
  • 球が動く方向は上下(X, Y方向)のみとする

準備

iTween

今回どの方向にどの場所まで転がすか、という計算をしたあと、実際に転がす処理でiTweenを使うことにします。

なお、Windowsの場合一度ダウンロードしたAssetsは C:\Users\ユーザー名\AppData\Roaming\Unity\Asset Store-5.x に置かれているので、そこからインポートすることが可能です。

Material

ボールが転がっていることを確認するために、以下のような画像をTextureとして設定したMaterialをボールに設定します。

f:id:mslGt:20151027231449p:plain

円周

ボールが移動するときに、どれだけ回転するかを調べるためまず円周を求めます。

円周は 直径×円周率(π) で求まります。 これをコードに直します。

MainController.cs

using UnityEngine;

namespace Ctrl{
    public class MainController : MonoBehaviour{
    
        public GameObject gmoBall;
        private float fltCircumference;
    
        private void Start(){
            fltCircumference = gmoBall.transform.localScale.x * System.Math.PI;
        }
    }
}
  • 今回ボールのScale値はX, Y, Zですべて同じであるため、使用するのはXでなくても問題ありません

角度

円周を使って、Scale値1移動するときに、ボールがどの程度回転するかの角度を取得します。
単純に360(度)を円周で割るだけですね。

MainController.cs

using UnityEngine;

namespace Ctrl{
    public class MainController : MonoBehaviour{
    
        public GameObject gmoBall;
        private float fltCircumference;
        private float fltAngleUnit;
    
        private void Start(){
            fltCircumference = gmoBall.transform.localScale.x * System.Math.PI;
            fltAngleUnit = 360 / fltCircumference;
        }
    }
}

オイラー角による回転

3Dオブジェクトを回転させるときに使われる方法はいくつかありますが、今回はUnityでも用意されているオイラー角(EulerAngle)と四元数(Quaternion)について調べてみます。

まずはオイラー角から。

X、Y、Zという3要素を持ち、3D空間のX、Y、Zと同じ感覚で角度を指定できるので比較的わかりやすいです。

iTweenを使って、以下のように回転させてみます。

   
        public GameObject gmoBall;
        private float fltRotatedAngle = 90;
        
        private void OnGUI()
        {
            if(GUI.Button(new Rect(10, 10, 200, 180), "Rotate"))
            {
                iTween.ValueTo(gameObject, iTween.Hash("from", gmoBall.transform.localEulerAngles.x, "to", fltRotatedAngle
                 , "time", 3.0f, "onupdate", "UpdateAngle", "easetype", iTween.EaseType.easeInQuad));
            }
        }
        private void UpdateAngle(float fltNewAngleX)
        {
            var vctNewAngle = gmoBall.transform.localEulerAngles;
            vctNewAngle.x = fltNewAngleX;
            gmoBall.transform.localEulerAngles = vctNewAngle;
        }

今回は仮にGUIでボタンを作成し、それが押されたらX方向に90度(fltRotatedAngleで指定)回転させる内容にしています。

動作させてみると、以下のように90度回転していることが確認できます。

f:id:mslGt:20151027231617j:plain

ここまでは何も問題なさそうです。

ところが、「fltRotatedAngle」を95に変更して、95度回転させようとすると、ボールが2、3回連続して回転してしまう上、Inspectorの値も不正となります。

どうやらオイラー角を使って回転させようとすると発生する、「ジンバルロック」という現象によるものが原因のようです。

もう一つの四元数を使ってみることにしました。

四元数(Quaternion)

四元数という名前の通り、X、Y、Z、Wの4つの値を持ちます。
この内訳は「X、Y、Z」の3次元の方向 + 「W」である、ということなのですが、詳しいことはよく理解ができませんでした。。。(これはおいおい勉強しようと思います)

また、Unity上でもこれに直接値を入力したり、変更したりすることは少ないようです。 (参考:Quaternion - Unity - Scripting API)

Quaternion.AngleAxisを使った回転

ではどうするか。

以下を参考に、「Quaternion.AngleAxis」を使うことにしました。

これは、回転させる度数(float)と回転の方向を表すVector3を引数として、Quaternionの値を返してくれます。

例えばZ軸方向に30度回転させるには、以下のようにします。

gmoBall.transform.localRotation = Quaternion.AngleAxis(30, Vector3.forward);

この回転の度数を95などに設定すれば、正しくボールが回転してくれます。

なお、「Vector3.forward」の中には、「0.0f, 0.0f, 1.0f」という値が入っています。
そして、それぞれの値は「1.0」を超えても問題ないようです(意味も無いようですが)。

さらに、それぞれの値の合計に制限も無いようで、「1.0f, 0.0f, 1.0f」と「0.5f, 0.0f, 0.5f」をそれぞれ指定した場合はどちらも斜め方向の回転となります。

以上から、ボールの位置とクリックされた場所を使ってどのぐらい回転させるかと回転の向きが取得できれば、狙い通りボールを回転させることができそうです。

長くなってきたので次回に続きます。

参考

書籍

円周計算

UnityのScaleの単位

iTween

Quaternion