Programming Serendipity

プログラミングを中心に種々雑多に書き留めます

Unity5 メモ4

  • モンストで敵に当たったときのキャラの跳ね返り処理
    f:id:q7z:20150716190035p:plain
    図で言うと、Aから進んでOの地点で接触した場合、Cの方向に進むのが正しい。
    現状進もうとしているベクトルOA'を回転させてベクトルOCにすればいいのだが、直接∠A'OCを求めることは難しいため、まずは∠A'OPの角度を求めて、ベクトルOA'を時計回りに2度回してベクトルOC'とし、そのベクトルを反転させてベクトルOCにすることを考える。 前提として、∠AOB = ∠BOC、∠A'OP=∠POC'。
    まず、2つのベクトルのなす角は以下の公式を使う。

ベクトルのなす角

この公式を2つのベクトルOA'とOPに適用すると、∠A'OPのcosθが取得できる。このcosθに逆余弦であるarccos(アークコサイン)をかけると、∠A'OPがラジアンで取得できる。ここまでできればあとは簡単で、取得できたラジアンを使ってOA'を時計回り方向に2回回し、最後に-1をかけてそのベクトルを反転させれば、欲しかったベクトルOCが取得できる。

これを手動で行う場合は以下のようになる。

まず、2つのベクトルOA'とOPがなす角∠A'OPを求めるために、上記リンクで解説されている公式を適用し、(OA'とOPの内積)/(OA'の長さ*OPの長さ)を求めたいが、簡略化のために両方とも長さを1とし、Oを原点として考える。すると分母は(1*1)=1となって計算が省かれ、A'.x * P.x + A'.y + P.yだけ計算すればよくなる。これがcosθの値なので、ここにアークコサインをかける(arccos(A'.x * P.x + A'.y + P.y))。 これが∠A'OPのラジアン角なので、それを2倍すると∠A'OC'のラジアン角となる。これを使って、ベクトルOA'を回転させ、そのあとにベクトルを反転させれば答えとなる。

これをUnityで行う場合、実はここまで考えなくてもいい。

Unityでは回転系の関数がQuaternionクラスなどにまとめられているので簡素に実装することができる。
Vector3 attackerForce;がキャラの移動ベクトル(上の図ではベクトルAOであり、ベクトルOA')として、OnTriggerEnter()内で以下のようにできる。

    // プレイヤーから敵へのベクトル、上の図ではOPに相当
    Vector3 vecToEnemy = coll.gameObject.transform.position - this.transform.position; 
    // to make sure  
    attackerForce.z = 0;  
    vecToEnemy.z = 0;  
    // この関数を呼ぶだけで角度が取得できる
    float deg = Vector2.Angle(attackerForce, vecToEnemy);  
    // しかし、↑で取得した角度は相対的な角度で、常にプラスの角度しか取得できない
    // ので、2つのベクトルの外積を求め、そのz成分が正だったらマイナスにする
    Vector3 cross = Vector3.Cross(attackerForce, vecToEnemy);   
    if (cross.z > 0)  
        deg *= -1;  
    // ここでdegが∠A'OPのラジアン角で、それを2倍している
    // そしてそれをattackerForce にかけ、最後に-1をかけてベクトルを反転させている
    attackerForce = Quaternion.AngleAxis(deg * 2, Vector3.back) * attackerForce * -1;  
    // Vector3.backというのは、new Vector3(0, 0, -1)と同じで、手前へのベクトルで、
    // これを軸に回転させることでXY平面の2Dとしての回転に利用できる
  • Vector2/3.Angleで負の値が欲しい場合
    ↑で使っているが、Angleメソッドは絶対値を返してしまうので、負の値が欲しい場合は外積で判断する必要がある。
    float ang = Vector2.Angle(fromVector2, toVector2);  
    Vector3 cross = Vector3.Cross(fromVector2, toVector2);   
    if (cross.z > 0)  
        ang *= -1;  
  • Unityは左手座標系
    UnityはDirectX同様奥ほどZが大きいので、XY平面を数学的に直感的に回転させるときは、
    Quaternion.AngleAxis()の第2引数のZ軸をVector3.backで指定する(正の角度で反時計回り)。
    Vector3.fowardだと回転が時計回りになる。

  • ヒットストップの実装
    キャラが敵に当たったとき、一瞬止まり、すぐ復帰するいわゆるヒットストップ処理をする場合、
    Time.timeScaleを0にしてInvoke()で一定時間後にtimeScaleを元に戻す関数を呼ぶ方法が思いつくが、
    Invoke()も内部的にtimeScaleで実装されているため、これだと永久に呼ばれなくなってしまう。
    Time.realtimeSinceStartupを使用して、ヒットしたときに

    Time.timeScale = 0;  
    lastHitTime = Time.realtimeSinceStartup;  
    isHitStopped = true;  

    /// とし、Update()関数内で以下のようにすると実現できる。

    if(isHitStopped && Time.realtimeSinceStartup > lastHitTime + HIT_STOP_TIME){  
        Time.timeScale = 1;  
        isHitStopped = false;  
    }  
  • 拡張メソッド
    C#内在の機能で、特定のクラスにメソッドを追加する。
    まず、任意の新規スクリプトpublic static classとして宣言する。
    追加したいメソッドをstatic関数として定義し、第1引数はthis 拡張対象のクラス 変数名 の形式で書く。
    たとえば、Vector2クラスにRotate()メソッドを追加したい場合以下のように書くことができる。
    public static class Extensions{  
        public static void Rotate(this Vector2 v, float degrees){  
            return Quaternion.Euler(0, 0, degrees) * v;  
        }  
    }  
    /// これで、  
    Vector2 vec = new Vector2(3, 5);  
    vec = vec.Rotate(45f);  
    /// のように使うことができる。  
    /// このスクリプトは何かのGameObjectにアタッチしなくてもビルドを通すだけで使える。  
  • スマホの縦画面のアスペクト比
    多くは9:16なので、GameウインドウでFreeAspectになっている部分をクリック、プラスボタンを押してTypeをAspectRatioにして
    9:16になるように変更してOKを押すと、縦画面のアスペクト比で固定される。

  • MonoDevelopのショートカット
    多くのVS互換のショートカットが利用できる
    C-F(search) C-H(replace) C-W(close tab) C-G(goto line)
    F2(rename) F7(build) F9(breakpoint) F10(step over) F11(step in) F12(goto definition)

  • スプライトを非表示にするには
    SpriteRendererに対して、
    spriteRenderer.enabled = false;

  • Vector3をQuaternionに変換
    Quaternion.Euler(new Vector3(0, 0, angle));

  • キャラクターを押している間だけキャラクターを振動させる
    キャラクター本体を動かすとpositionが変わって弾く方向まで変わってしまうため、
    空のオブジェクトを作ってそこにコライダーなどスプライト以外全てのコンポーネントを移し、
    スプライトだけをそのオブジェクトの子オブジェクトにし、子オブジェクトのpositionをいじって調整する。
    当たり判定・位置情報を親のものを使うことで描画だけをずらすことができる。