読者です 読者をやめる 読者になる 読者になる

おもちゃラボ

Unityで遊びを作っていきます

【Unity】ビルボードで常にカメラの方に向く木を作る

Unity

ビルボードを使って、常にカメラの方を向く木のオブジェクトの作り方を紹介したいと思います。

最近ではビルボードを使う機会も減り、パーティクル以外ではあまり使われなくなりました。ただ、スマートフォン用など、ROM容量が厳しい環境では、依然として使われているようです。

ビルボードの歴史

そもそもどうしてビルボードというものがあるのか、というところから説明したいと思います。今でこそ記憶媒体は豊富になりましたが、ひと昔前までは今では想像できないぐらい記憶媒体の容量が少なかったのです。したがって、ゲームの容量(サイズ)を出来る限り減らすことを常に意識する必要がありました。

ドラクエのROMサイズが64KBだったというのは有名な話ですね。一体どうやって作ったのか、想像を絶するものがあります。

マリオの雲と草も同じテクスチャを使いまわしていたり、マリオとルイージは色違いだったりと、色々と容量を減らすための涙の努力が見え隠れします。

matome.naver.jp

そんな中で、リッチでハイポリな木の3Dモデルなんか使えるはずもありません。かといって、そのまま木を平面に貼り付けただけでは、見る方向によってものすごく不自然な見た目になってしまいます。

f:id:nn_hokuson:20170324203420p:plain

そのために考えられた技術がビルボードです。ビルボードの実体は平面に貼ったテクスチャですが、常にカメラの方向を向く特性を持っています。そのため、あたかも2Dオブジェクトを3Dオブジェクトのように見せることができるのです。

ビルボードの作り方

ではどのようにしてこのビルボードを作るのかを紹介します。Unityを使わずにビルボードを作る場合はカメラの位置とオブジェクトの位置から変換行列を自力で計算しなくてはいけません。

UnityではLookAtという超便利なメソッドが用意されているため、非常に簡単にビルボードを作ることができます。次のプログラムをビルボードにしたいオブジェクトにアタッチするだけです。

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

public class BillBoard : MonoBehaviour {

	void Update () {
		Vector3 p = Camera.main.transform.position;
		p.y = transform.position.y;
		transform.LookAt (p);
	}
}

ただし、3Dモデル(Planeなど)にテクスチャを貼り付ける場合、平面を90度回転してカメラに向けた状態で使うことが多いと思います。
初期状態で回転した状態だとビルボードが正しく動かないため、回転していない空のオブジェクトを用意し、モデルをその子要素にして下さい。

f:id:nn_hokuson:20170324204623p:plain:w200

この場合、ビルボードのスクリプトは親のオブジェクトにアタッチします。

f:id:nn_hokuson:20170324211147p:plain

実行結果

ビルボードで作った木の間を車が走行するデモがこちらです。見た目に全然不自然ではありません。言われなければビルボードを使っていることは分からないのではないでしょうか。

f:id:nn_hokuson:20170324205001g:plain

ですが実際は、カメラの移動に合わせて結構壮絶な木の大回転が行われています(笑)バックヤードの動画がこちらです。
なんと、木がいっせいに動いている・・・ジブリの世界?ですね。

f:id:nn_hokuson:20170324205817g:plain

おまけ

ちなみに、スーパーマリオ64のボム兵は球体の部分(体)の部分だけ、ビルボードで作られているようです。昔は容量がなかった分、面白技術が色々と生まれたのですね。

【Unity】Animation Curveを使ってインスペクタからレベルデザインをする

Unity

Animation Curveとはベジェ曲線を使って物体の動きや挙動などをGUIで設定するための仕組みです。
Unityではスクリプトで定義したAnimation Curveをインスペクタに表示することができます。そこで、このアニメーションカーブを使ってレベルデザインをしちゃおう!というのがこの記事の内容です。

インスペクタからAnimation Curveを設定する

ここでは、簡単なシューティングゲームを考えてみます。敵が弾を打つ間隔をアニメーションカーブで制御してみましょう。

f:id:nn_hokuson:20170323200918p:plain:w400

早速ですが、敵クラスのプログラムは次のようになります。

public class EnemyController : MonoBehaviour 
{
    public GameObject bulletPrefab;
    public AnimationCurve waitTime;

    IEnumerator Generate()
    {
        while (true)
        {
            Instantiate (bulletPrefab);
            float wait = this.waitTime.Evaluate (Time.time);
            yield return new WaitForSeconds (wait);
        }
    }
            
    void Start () {
        StartCoroutine (Generate ());
    }
}

スクリプト中にpublic変数としてAnimation Curveを宣言しています。
Animation Curveクラスはグラフのようなもので、縦軸と横軸の定義は自由に決めてOKです。今回は縦軸に弾を打つ間隔、横軸にゲーム時間を表すグラフを作成します。

Animation Curve変数はpublic変数として宣言しているので、インスペクタを見るとアニメーションカーブの欄が表示されています。
この欄をダブルクリックしてAnimation Windowを開き、次のような曲線を作成しました。

f:id:nn_hokuson:20170323200251p:plain:w400

最初は弾を打つ間隔を大きく取り、時間が立つに連れて弾を打つ間隔を短くしています。最後はご褒美で少しだけ簡単にしています。今回は分かりやすいように、横軸は5秒で設定・・・(笑)

レベルデザインの方法については「Unity5の教科書」にも書いたので合わせて参考にしてください!

Unity5の教科書 (Entertainment&IDEA)

Unity5の教科書 (Entertainment&IDEA)

また、設定した時間外のグラフをどのように扱うのかは、カーブ終端の歯車ボタンで設定できます。
設定できる種類は次の3種類です。

  • Loop
  • PingPong
  • Clamp

[Loop]アニメーションカーブを1から繰り返します。
f:id:nn_hokuson:20170323200323p:plain:w300
[PingPong] グラフ終端で線対称になるようにグラフが生成されます
f:id:nn_hokuson:20170323200330p:plain:w300
[Clamp] グラフ終端の値が使われます。
f:id:nn_hokuson:20170323200336p:plain:w300

Animation Curveから値を取り出す

スクリプトに戻って、現在時刻から弾の時間間隔を取り出す部分を見ていきましょう。アニメーションカーブのX軸の値からY軸の値を得るためにはEvaluateメソッドを使います。

Evaluateメソッドの引数にX軸の値を渡すことで、Y軸の値が取得できます。ここではX軸の値としてゲーム開始からの時間を渡して、返り値として次の弾を発射するまでの待ち時間を取得しています。

f:id:nn_hokuson:20170323200407p:plain:w400

取り出した待ち時間ぶん、waitForSecondsメソッドを使って弾の生成を停止しています。

これでアニメーションカーブを使ったレベル設定ができました。
実行結果は次のようになりました。レベルデザインどおりに時間とともに弾を打つ間隔が変わっていますね〜^^

f:id:nn_hokuson:20170323200414g:plain

【Arduino】抵抗1本で作る静電容量式のタッチセンサ

Arduino

静電容量式タッチセンサは、主にスマートフォンなどの液晶タッチセンサとして使われいます。

この静電容量式タッチセンサですが、スマートフォンのように2次元ではなく、1次元(タッチしたか、していないか)を判別するだけであれば、簡単に作ることができます

f:id:nn_hokuson:20170322195708p:plain

作成する回路

今回作成する静電容量式のタッチセンサは次のようになります。Arduinoの8番ポートから9番ポートへ1MΩの抵抗がつながっているだけです。

f:id:nn_hokuson:20170322193051p:plain:w350

タッチセンサになるのは9番ポートに刺さっている部分です。タッチしにくいようでしたら、みのむしクリップなどで延長してください。

回路の原理

「こんな簡単な回路で、タッチ判別できるのかいな?」と思うかもしれませんが、ちゃんとできます( ー`дー´)キリッ

原理は簡単で、8番ポートからはパルスを出して、9番ポートが立ち上がる(HIGHになる)までの時間を計測します。
タッチしていない状態では単に1MΩの抵抗を介して9番ポートへつながっているだけなので、一瞬で立ち上がります。

f:id:nn_hokuson:20170322184553p:plain:w250

一方、タッチしている場合は人間がキャパシタとなるため、抵抗とキャパシタでRC回路を作ることになります。

f:id:nn_hokuson:20170322193204p:plain:w250

CR回路はローパスフィルタとしても使われる回路で、パルスの立ち上がりが鈍ります
鈍り方はCとRの時定数で決まるのですが・・・考え出すと大変なので大きな値の抵抗を使えば使うほど、立ち上がりも鈍ると考えて問題ありません。

f:id:nn_hokuson:20170322184545p:plain:w250

この時間の差を使うことでタッチされているか、されていないかを検出します。

タッチ検出プログラム

タッチ検出プログラムは次のようになります。実際に入力してArduinoに転送してみてください。

int prevt = 0;
int t = 0;
int threshold = 5;

void setup()
{
  pinMode(8,OUTPUT);
  pinMode(9,INPUT);
  pinMode(13,OUTPUT);
}

void loop()
{ 
  t = 0;

  // パルスの立ち上げ
  digitalWrite(8, HIGH);

  // 立ち上がりまでの時間計測
  while (digitalRead(9)!=HIGH) t++;

  // 放電するまで待つ
  digitalWrite(8, LOW);  
  delay(1);

  // ローパスフィルタ
  //t = 0.8 * prevt + 0.2 * t;
  //prevt = t;

  // LED点灯
  if( t > threshold ){
    digitalWrite(13, HIGH);
  } else {
    digitalWrite(13, LOW);
  }  
}

このプログラムでは、センサをタッチしたら13番ポートに接続されているLEDが点灯するようにしてみました。Arduinoを使ってLEDを光らせる方法はこちらの記事で紹介しています。

nn-hokuson.hatenablog.com

センサから得られるデータにはばらつきがあるため、LEDが常時点灯しない場合には、ローパスフィルタで平滑化する部分のコメントを外してみてください。

最後にArduinoを使った静電容量式のタッチパネルの動画を載せておきます。
簡単に作れるので、Arduinoをお持ちの方は是非試してみてください!

www.youtube.com

参考サイト

こちらのサイトを参考にさせていただきました!
kousaku-kousaku.blogspot.jp