はじめてのUnity – インタラクティブサイネージができるまで

Unityをインストールはしていたけど、実際に使ったことがない状態から試行錯誤して、やりたい事が実現し、
ハマったところをメモしていたので、ここに残しておきます。
何かの参考になれば幸いです!

はじめ

そもそもサイネージなどのハードウェアと密接なお仕事を経験したことがなかったので、
終盤までかなり苦しめられました。

やろうとしていたことが、

  1. 巨大なタッチパネルを2枚繋げたデュアルディスプレイで、且つフルスクリーンでマルチタッチをしたい!
  2. 複数人で楽しめてゲーム性を持たせたい!

というものでした。

そこで、まずはUnity自体を触ってみるということで以下の本をとりあえず1日掛けてやってみました。

Unity4入門   最新開発環境による簡単3Dゲーム製作
浅野 祐一 荒川 巧也 森 信虎
ソフトバンククリエイティブ
売り上げランキング: 1,550

この手の新しいことをやるときに、難しいところから入るとテンションが上がらないので、まずは入門書。
こちらの本は入門書なんですが、初心者がハマるところをよく練りこまれていて、この本以外は結局読まずに最後まできてしまいました。

あとは、ググったり、Unityのdocsを中心に見ていきました。そしてStackOverFlow様!
Unity – Unity Manual

これ以外で解決できないものは、会社のインタラクティブチームにヒアリングして教えてもらってという感じです。
(ほんとこのチームがなかったら出来てなかった、、、)

今回使ったもの

実行機のOS
Windows8
Unity
4.2
GestureWorks
マルチタッチライブラリ
Uni2D
Unity用2Dライブラリ

この後の、Unity4.3で2Dを作る仕組みが入ったようです。

GestureWorksはマルチタッチを検出してくれるライブラリで、dllをUnityから読み込んで使います。
ここはTouchScriptでも代用できるようですが、
今回はトライという意味も含めてGestureWorksで遊んでみました。

Unity編

物理エンジン

ゲームのベースとなる部分は物理演算をメインに使いたいと思っていたので、Unityは最高でした。
とくにコードレスでモノを落下させたり地面でバウンスさせたりが楽でした。

物理演算を有効にするには、GameObjectにRigidbodyを付与します。
するとどこまでもどこまでも落ち続けるオブジェクトが完成します。
Unity – Unity Manual

さらに当たり判定が必要な場合は、何かしらのColliderを付与します。

ColliderはBox ColliderやMesh Colliderなど様々ありますが、最終的にパフォーマンスを考慮して、
Box Collider、Sphere Collider、Capsule Colliderの3種類のみを使いました。

Mesh Colliderをはじめのうち使っていたのですが、画面内にオブジェクトが100個や200個などに増えた場合、
FPSが極端に落ちてしまい、今回のゲームでは使えなそうでした。
確かに、オブジェクトの形状と同じようにColliderを作り、当たり判定を計算するのはむちゃくちゃ重そうですね。

物理演算をいじる場合、物理特性マテリアルをいじるといい感じになりました。
これはPhysics Materialを作成し、GameObjectのColliderにアタッチします。
Unity – Unity Manual
デフォルトだと全然バウンスしてくれないので、Bouncinessを1とかにすると楽しいです。

あと、落下スピードが速かったので、もう少しゆるい世界観にするために、物理特性マネージャを使ってGravityのYを少し大きくしました。
デフォルトは-9.81なので、-8とか。
Unity – Unity Manual

オブジェクトを吹っ飛ばす

rigidbodyに力を加えることで吹っ飛ばすことが可能です。
以下の2種類を使ってみました。

AddForceで力を加える

流星とかを作るときに便利でした。

gameObject.rigidbody.AddForce (new Vector3 (power, power, 0), ForceMode.Impulse);

AddExplosionForceで周囲に爆発の力を加える

周囲を吹き飛ばす爆弾とかに便利でした。

Rigidbody body = gameObject.collider.attachedRigidbody;
body.AddExplosionForce (50.0f, transform.position, 10.0f, 3, ForceMode.Impulse);

最後の引数のForceModeは以下のとおり。

Force その質量を使用して、rigidbodyへの継続的な力を追加します。
Acceleration その質量を無視して、rigidbodyへの継続的な加速を追加します。
Impulse その質量を使用して、rigidbodyに瞬時に速度変化を追加します。
VelocityChange その質量を無視して、rigidbodyに瞬時に速度変化を追加します。

via: RigidBodyにAddForce()を行った場合のForceModeによる動作の違い – 強火で進め

モノにあたったときに何かしたい

これはもうむちゃくちゃ使ったイベントです。
ぶつかったときに何か起こるというのがメインのコンテンツなので、ここでコントロールしました。

isTriggerでない場合、つまり動いているオブジェクトの場合のイベントハンドラ

void OnCollisionEnter (Collision col) {}

isTriggerの場合、つまり動いていないオブジェクトの場合のイベントハンドラ

void OnTriggerEnter (Collider col) {}

当たり判定を無効化したい

あるオブジェクトとあるオブジェクトを通過するようにしたい場合、以下のように書くとできます。
ただ、本当にキレイに通過するというよりは、一瞬だけぶつかったような感じになってから通過する気がします。
これは状況によるかもしれないので、要検証。

Physics.IgnoreCollision (gameObject.collider, col.gameObject.collider);

参考リンク

Pushing rigidbody with FPS Controller – Unity Answers

レンダリング

初めハマったのがこのレンダリングの順番です。
UnityではZ軸を奥のほうに配置したからといって、手前のオブジェクト→後ろのオブジェクトとレンダリングされるとは限らないようです。
なので、奥にいるんだけど、手前としてレンダリングされてしまう。

これを回避するために、レンダリングの順番はかなり細かく設定しました。

using UnityEngine;
using System.Collections;

[AddComponentMenu("Custom/Scripts/Render/RenderQueue")]
public class RenderQueue : MonoBehaviour
{
  public int queue = 1;

  // Use this for initialization
  void Start ()
  {
    if (!gameObject.renderer) {
      return;
    }

    gameObject.renderer.material.renderQueue += queue;
  }

  // Update is called once per frame
  void Update ()
  {

  }
}

このプログラムをGameObjectにアタッチして、数値をコントロールしてレンダリングの順番を調整しています。

レンダリングする順番を調整する

子どもたち全部を同じRenderQueueにする場合は、

レンダリングする順番を調整する(子供たちも)

とか使ってます。

CharacterControllerを使ってみた

Unity – Unity Manual

CharacterController(以下CC)のvelocity.magnitudeで運動をしているかが取れたり、isGroundedで地面に着地したのかどうかが取れたり、
結構便利なんですが、CCを使いこなすのはなかなか大変でした。
あと、CC独自の仕様なのか他のオブジェクトとの違いが結構あり、ある程度割り切って使ってみました。

ひじょうにベーシックなCCのUpdateはこんな感じかと思います。
これにいろいろ肉付けしていくと、動きの感じがキャラクターっぽくなります。

void Update ()
{
  // 地面に着いている場合
  if (CC.isGrounded) {
    moveDirection = new Vector3 (1, 0, 0);
    moveDirection = transform.TransformDirection (moveDirection);
    moveDirection *= walkSpeed;
    moveDirection = moveDirection  * 2.0f;

    moveDirection.y = 0.0f;
  } else {
    moveDirection.y -= gravity * Time.smoothDeltaTime;
  }

  // どこの面にぶつかったかの判定
  CollisionFlags flag = CC.Move (moveDirection * Time.smoothDeltaTime);
  if (flag == CollisionFlags.None) {
  } else if ((flag & CollisionFlags.Above) == CollisionFlags.Above) {
  } else if ((flag & CollisionFlags.Sides) == CollisionFlags.Sides) {
  } else if ((flag & CollisionFlags.Below) == CollisionFlags.Below) {
  }

  // 右に移動している
  if (moveDirection.x > 0.0f) {
    transform.localScale = origLocalScale;
  }

  // 左に移動している
  if (moveDirection.x < 0.0f) {
    // テクスチャを反転
    transform.localScale = new Vector3 (-origLocalScale.x, origLocalScale.y, origLocalScale.z);
  }
}

CC同士が衝突するときにZ軸が動いてしまう

今回は2Dベースなので、基本的にZ軸には移動しません。
ただ、CCはSphere Colliderなので、キャラクター同士がぶつかった場合は、ずるっとZ軸側に移動してしまうことがあります。

これに関してはググると、ParentにBox ColliderなGameObjectを用意して、キャラクターがぶつかっても、Z軸方向にずれないようにする、
みたいな記事もあったんですが、ややこしく、変化に対応できなさそうなのでやめました。

結果、

transform.position = new Vector3 (transform.position.x, transform.position.y, 初期Z位置);

をUpdateメソッドの中に書くことにしました。
つまり、Z軸を強制的にある位置に固定する。
これでZ軸はズレなくなり、特にパフォーマンスもFPSも落ちていなかったです。

AddForceしても吹っ飛ばない

RigidbodyFPSWalker – Unify Community Wikiにあるような自作キャラクターを作り、
AddForceするとなんですかね。
要検証。

CC同士の衝突をを検知できない

こちらの記事が参考になりそうです。
これもほんとハマりました。

CharacterController 同士は、何も設定しなくても互いにぶつかり、相手の身体を滑ったりも出来る便利なものですが、その代わり現在の仕様上、ぶつかった相手が CharacterController だった場合、衝突情報を取得することができないようです。
 処理上はちゃんと衝突してても、OnControllerColliderHit には、CharacterController タイプのオブジェクト情報だけ送られてきません(その他のコライダは当然取得出来ます)。

 回避策としては、子オブジェクトに CharacterController 付属のコライダと同じくらいの大きさの CapsuleCollider をアタッチすることが挙げられます。
 この子オブジェクトに、CharacterController を搭載した親オブジェクトと同じ tag や layer を設定しておけば、大抵の用は済みます。
 必要であれば、衝突した子オブジェクトの parent を参照すればよいでしょう。

via: 紫雪PのUnity備忘録 CharacterController 同士の衝突が検知できないんだが?

それでもやはり自前で実装しないといけないんですね。

CCは使えそうですが、少し特殊なので、使うシーンを見極める必要があると感じました。
今回は特に大きな役割をしないキャラクターの実装時に使ってみました。

動画を再生する

ざっくりですが、以下のような感じです。
MovieTextureには動画、AudioClipには動画の音を設定。

public MovieTexture movieTexture;
public AudioClip audioClip;

private AudioSource audioSource;

// Use this for initialization
void Start () {
  // movie
  gameObject.renderer.material.mainTexture = movieTexture;
  movieTexture.Play();

  // sound
  audioSource = gameObject.GetComponent<AudioSource>();
  audioSource.clip = audioClip;
  audioSource.Play();
}

動画をループさせたい場合は、

movieTexture.loop = true;

をつける。

実際にはもっとコード量が多いのですが、この方法はなかなかうまくいきませんでした。
動画に音声もある場合、mov + mp3のようにUnity側で作られるのですが、複数回再生していると変な音が入ることがあったり、
動画が始まる瞬間、真っ黒だったりと違和感がありました。
(mov + mp3だとロードが重いからなんですかね)

なので、動画には音なしにして、movとmp3を完全に分けてUnityで管理していたのですが、今度は映像のループと音のループにずれが生じました。
どうも、このあたりはまだ不安定のようで、自前で映像が(きっと)終わったタイミングなどで音の再再生をInvokeなどで呼び出すとかがよさそうです。
これまた自前InvokeRepeating!

WindowsでMovie Texturesにmp4をアタッチしてもなんかmissingになってしまう

Unity – Unity Manual
を読みながら、QuickTime入れたりしたんですが、Macでmp4をMovie TexturesにアタッチしてWindowsでUnityを起動すると動画ファイルが外れた状態になってしまいました。
なので、Windows上でビルドができなかったんです。

movにしたら外れなかった!
よって、mov採用!

音を再生する

こんなコードを書いて使っています。
if (audioClip && audioSource) { なんてif文があるのは、再現率はかなり低いのですが、ヌルポが発生してしまうケースがあったための回避です。

using UnityEngine;
using System.Collections;

public class EventObjectSound : MonoBehaviour
{
  public AudioClip audioClip;
  private AudioSource audioSource;

  public void PlaySound ()
  {
    audioSource.Stop ();

    if (audioClip &amp;&amp; audioSource) {
      audioSource.Play ();
    }
  }

  public void PlaySoundOneShot ()
  {
    audioSource.Stop ();

    if (audioClip &amp;&amp; audioSource) {
      audioSource.PlayOneShot (audioClip);
    }
  }

  public void StopSound ()
  {
    audioSource.Stop ();
  }

  // Use this for initialization
  void Start ()
  {
    audioSource = gameObject.GetComponent<AudioSource> ();
    audioSource.clip = audioClip;

  }

  // Update is called once per frame
  void Update ()
  {

  }
}

Unityで音を再生する

カメラを動かす

カメラにスクリプトを追加し、Updateの中で以下のようなコードを実行

  float x = Mathf.Sin(Time.time) * 0.02f;
  float y = Mathf.Cos(Time.time) * 0.02f;
  gameObject.transform.position -= new Vector3 (x, y, 0.0f);

カメラもGameObjectなので、他のGameObjectと同じ要領ですね。

GameObjectを透過させる、そしてExtensionMethodsで既存のオブジェクトを拡張する

Unityで透過の処理を探していたら、意外と面倒でしたが以下の方法で可能でした。
material.alpha = 0.1
とかしたいのにー

ExtensionMethodsでMaterialを拡張し、SetAlphaメソッドを追加しています。
こうゆうのすんごく便利!だけど、あとあと分からなくならないように程々にねっ!

using UnityEngine;
using System.Collections;

public static class ExtensionMethods
{
  public static void SetAlpha (this Material material, float value)
  {
    Color color = material.color;
    color.a = value;
    material.color = color;
  }
}

呼び出し方は以下。

gameObject.renderer.material.SetAlpha (value);

Debug.Logをリリース時(exe実行時)に無効にしたい、でも既存のソースはそのままがいい!

ここに解決策が書かれていました。
すばらしい!
Unity3D – Debug.Logを開発環境以外で無効化 – Qiita [キータ]

どうしてもDebug.Logは多用してしまい、本来ならラッパーを作って条件によってLogを出す出さないをすればいいんですが、
なんとなく後回しになったりして、気がついたら結構Debug.Logを埋め込んでしまったという状況はあります。

方法は、

やり方としては、自分で同名の「Debug」クラスを定義する。
UnityのDebugクラスは、UnityEngineというnamespaceにあるので、UnityEngine.Debugとして定義されている。なので、ネームスペースを使わずに同名クラスを定義した場合は、そちらが優先される・・・というC#の仕様を利用する。

via: Unity3D – Debug.Logを開発環境以外で無効化 – Qiita [キータ]

のようで、なかなか面白いですねー

ぼくの場合、Uni2DがDebug.Log以外にも使っていたので、少し足してあげる必要がありました。

#if !UNITY_EDITOR
#define DEBUG_LOG_OVERWRAP
#endif

using UnityEngine;

#if DEBUG_LOG_OVERWRAP
public static class Debug
{
    static public void Break(){
        if( IsEnable() )    UnityEngine.Debug.Break();
    }

    static public void Log( object message ){
        if( IsEnable() ){
            UnityEngine.Debug.Log( message );
        }
    }
    static public void Log( object message, Object context ){
        if( IsEnable() ) {
            UnityEngine.Debug.Log( message, context );
        }
    }

    static public void LogWarning( object message){
        if( IsEnable() ) {
            UnityEngine.Debug.LogWarning( message );
        }
    }

  static public void LogError( object message){
        if( IsEnable() ) {
            UnityEngine.Debug.LogError( message );
        }
    }

  static public void LogError( object message, Object context ){
        if( IsEnable() ) {
            UnityEngine.Debug.LogError( message, context );
        }
    }

  static bool IsEnable(){ return UnityEngine.Debug.isDebugBuild; }
}
#endif

UnityのDebug.Logをラップする via http://qiita.com/rodostw/items/39183e62ed2a1f52f690

マウスポインタを無効にする

タッチが前提の場合マウスカーソルは邪魔です。
なので、以下で非表示に。

Screen.showCursor = false;
Screen.lockCursor = true;

Unity3D の Web Player でマウスカーソルをロックする | Lonely Mobiler

ただし、これをしてしまうと、Developmentでビルドしエラーコンソールが出た場合、タップしても反応しません。
つまり、何行目でエラーが出ているかを見ることができませんでした。

なので、Developmentなときはカーソルを表示するようにして回避しました。

Hierarchyやリソースからオブジェクトを探す

Unity3D – 【Unity】スクリプトからオブジェクトやコンポーネント操作まとめ – Qiita [キータ]

・Hierarchyから探す

GameObject.Find("MyObject");
GameObject.FindWithTag("MyObjectTag");

・Projectのリソースから探す

Resource.Load("hoge");

・子どもたちを探す

Transform[] allChildren = parentObject.GetComponentsInChildren<Transform> ();
foreach (Transform child in allChildren) {
  // ゴニョゴニョ
}

これでだいたい事足りる。

タイマーを使って遅延したり、リピートしたりしたい!

StartCoroutine、WaitForSeconds、そしてyield | ひささん日記の記事でコルーチンを取り上げたんですが、
ぼくは、コルーチンはあまり使わず、Invokeをかなり使いました。

setTimeoutのように使えるので、直感的で使いやすかったです。
さらに、InvokeRepeatingで何度も実行する場合は、これが使えます。
止めたければ、CancelInvokeを実行すればOK。

// InvokeRepeating("関数名",初回呼出までの遅延秒数,次回呼出までの遅延秒数)
InvokeRepeating("Logging", 10, 10);
void Logging ()
{
  Debug.Log("1");
  Invoke("Logging2", 2);
}

void Logging2 ()
{
  Debug.Log("2");
}

オブジェクトが数秒間だけ生存するスクリプトを書く

この方法、Materialの削除が行われていないので、結局使いませんでした。
Materialのリーク怖い!

//ゲームオブジェクトを5秒後にDestroy
Destroy(gameObject, 5);

GameObjectをコードから破棄する

Monobehaviourを継承していないクラス内で破棄する場合

GameObject.DestroyObject (material);
GameObject.DestroyObject (gameObject);

Monobehaviourを継承しているクラス内で破棄する場合

Destroy (material);
Destroy (gameObject);

上記コードで注目したいのがmaterialを破棄していることです。
これをしないとmaterial数はどんどん上昇しメモリリークを引き起こす。
GameObjectを破棄したからといって、materialは自動で破棄されてはくれない
これはすごくハマった。

子オブジェクトを持っているGameObjectの場合は子もちゃんと破棄すること。
もちろんmaterialもねっ!

private void DestroyObject ()
{
  foreach ( Transform t in transform ){
    Destroy( t.gameObject.renderer.material );
    Destroy( t.gameObject );
  }
  Destroy(gameObject.renderer.material);
  Destroy(gameObject);
}

ScreenCaptureする

JPEG、PNGで保存する方法を以下のリンクにまとめておきました。

Unity/ScreenCapture at master · hisasann/Unity

また、Application.CaptureScreenshotを使ってキャプチャするとUIが一瞬ロックされてしまいました。
画面の状態を一定時間でScreenCaptureし、Facebookに上げるなどの場合にこのロックがかなりネックになるので、以下のコードで対応できるかもしれません。

さらに、デュアルディスプレイでUnityをEditorモードで実行していると、片面しかキャプチャできなかったので、
次の項目で説明しているpopupwindow引数を付けてexeを実行しScreenCaptureするのがよいでしょう。

Unityのプログラムをサブディスプレイをまたいで表示する方法

exeの上でShift押しながらクリックし、WindowedモードでいったんUnityを起動する。
一度Unityを閉じる。
その後popupwindowの引数を付けて、コマンドラインから起動するとフルスクリーンになる。
どうもこれはWindows限定のパラメータのようですが、なので、MacMiniとかでフルスクリーンにするのどうするんだろう、、、

hoge.exe -popupwindow -screen-width 3840 -screen-height 1080

フルHDを2枚使った場合の引数の付け方。

Unity – Unity Manual

条件付きコンパイル

環境ごとに処理を変える場合

こんな感じでかなり細かく指定できるようです。

UNITY_EDITOR Define for calling Unity Editor scripts from your game code.
UNITY_STANDALONE_OSX Platform define for compiling/executing code specifically for Mac OS (This includes Universal, PPC and Intel architectures).
UNITY_DASHBOARD_WIDGET Platform define when creating code for Mac OS dashboard widgets.
UNITY_STANDALONE_WIN Use this when you want to compile/execute code for Windows stand alone applications.
UNITY_STANDALONE_LINUX Use this when you want to compile/execute code for Linux stand alone applications.
UNITY_STANDALONE Use this to compile/execute code for any standalone platform (Mac, Windows or Linux).
UNITY_WEBPLAYER Platform define for web player content (this includes Windows and Mac Web player executables).
UNITY_WII Platform define for compiling/executing code for the Wii console.
UNITY_IPHONE Platform define for compiling/executing code for the iPhone platform.
UNITY_ANDROID Platform define for the Android platform.
UNITY_PS3 Platform define for running PlayStation 3 code.
UNITY_XBOX360 Platform define for executing Xbox 360 code.
UNITY_NACL Platform define when compiling code for Google native client (this will be set additionally to UNITY_WEBPLAYER).
UNITY_FLASH Platform define when compiling code for Adobe Flash.
UNITY_BLACKBERRY Platform define for a Blackberry10 device.
UNITY_WP8 Platform define for Windows Phone 8.
UNITY_METRO Platform define for Windows Store Apps (additionally NETFX_CORE is defined when compiling C# files against .NET Core).
UNITY_WINRT Equivalent to UNITY_WP8 | UNITY_METRO

via: Unity – Platform Dependent Compilation

[via]
Unity – Platform Dependent Compilation

使い方はこんな感じ。

#if UNITY_STANDALONE_OSX
  Debug.Log("Stand Alone OSX");
#endif

#if UNITY_STANDALONE_WIN
  Debug.Log("Stand Alone Windows");
#endif

OSごとに処理を変える場合

void Start () {
  if (Application.platform == RuntimePlatform.WindowsEditor || Application.platform == RuntimePlatform.WindowsPlayer) {
    Debug.Log("WindowsPlayer");
  } else if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) {
    Debug.Log("OSXPlayer");
  }
}

gitでpushしてpullするといろいろおかしくなる場合

Project Settings → Editor
Version Control ModeをMeta Files
Asset Serialization ModeをForce Text

にすると幸せになれます。

(Unityは画像や動画などファイルサイズが大きいものを使うので、gitにpushしまくっているとcloneが激重になるんですが、ここはしょうがないのかな。
やはり、
Unity – Asset Server Guide
このあたりを使って管理するべきなのかな。)

解像度を指定する

Project Settings → Edit
Project Settings → Player

から

Default Is Native Resolutionのチェックを外すとWidthとHeightが出てくるので入力するすると、GameビューのStandaloneから選べるようになる。

Textureがどうもジャギるなーという場合

TextureのAniso Levelを最大にしてみた。
(そこまでパフォーマンスは落ちないらしい)

TrailRendererはtimeで指定してもGameObjectが破棄されない場合がある

どうもUnityのバグっぽくて、TrailRenderer自体は画面上から消えるのに、GameObject数が下がらない現象がありました。
なので、ポーリングして、すでに消えているはずのGameObjectがいる場合、破棄するように自前でコードを書きました。

それでも、TrailRendererは見た目もよくかっこよく演出できるので使いたいところですねー

タッチしたところにあるオブジェクトを判別して特定のスクリプトを呼び出すパターン

UnityではC#のInterfaceが使えるようなんですが、Editor上で作ることができなくて、
かつ、いろんな記事を読んでいるとうまくいかないケースがあるかもみたいなことが書かれていたので、
使うのをやめました。

そこで、タッチした場所に存在しているオブジェクトをRayを使って特定し、且つ、そのオブジェクトのメソッドを呼び出し、
個々の動作をさせたいということを考えて以下のようになりました。

つまり、if文をあまり使いたくなかったので、SendMessageを使ってそれっぽく実現してみました。

Main.cs

Rayを使って、対象のオブジェクトを取得するコード。

RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay (new Vector3 (pEvent.Position.X, screenHeight - pEvent.Position.Y, 0));

// Ray
if (Physics.Raycast (ray, out hit, Mathf.Infinity)) {
  // hit object
  GameObject obj = hit.transform.gameObject;

  new TouchObject (hit, obj, x, y);
}

TouchObject.cs

targetにRayで取得したオブジェクトが入ってくる。

TouchObjectProvider provider = target.GetComponent&lt;TouchObjectProvider&gt; ();
if (provider != null) {
  provider.EventFire ();
}

TouchObjectProvider.cs

SendMessageを使って対象のオブジェクトが持つ、特定のメソッドをリフレクション呼び出し。

using UnityEngine;
using System.Collections;

[AddComponentMenu("Custom/Scripts/TouchObject/TouchObjectProvider")]
public class TouchObjectProvider : MonoBehaviour
{
  public void EventFire ()
  {
    SendMessage ("SendEventFire");
  }

  // Use this for initialization
  void Start ()
  {

  }

  // Update is called once per frame
  void Update ()
  {

  }
}

TouchObjectEvent.cs

このクラスにあるSendEventFireメソッドが重要。
このメソッドがSendMessageによってFireされる。

なので、クラス名は対象オブジェクトを表す名前とかにしておけば、管理がしやすい。

using UnityEngine;
using System.Collections;

public class TouchObjectEvent : MonoBehaviour
{
  public void SendEventFire ()
  {
    // ここにイベントの処理
  }

  // Use this for initialization
  void Start ()
  {
  }

  // Update is called once per frame
  void Update ()
  {
  }
}

MonoDevelop

キーボードショートカット

よく使うのは以下でした。

Rename

Command + R

定義場所にジャンプ

Command + Y

以下のリンク先にショートカットがまとまっています。

Monodevelop keyboard shortcuts | Kureus

文字化けを直す

/Applications/Unity/MonoDevelop.appを右クリックし、パッケージの内容を表示する。

「Contents/Frameworks/Mono.framework/Versions/Current/etc/gtk-2.0/gtkrc」ファイルをvimなどで開き、

font = “Lucida Grande 14″

の一行下に、以下を追加する。

font_name =”Hiragino Kaku Gothic Pro 10″

あとはPreferenceから画像のように設定すれば、再起動後に文字化けは直ってる。

ただし、コメント入力は相変わらず日本語が打てない。
(他のエディターや、ペーストで日本語を貼り付けることができるが、どうも特定の日本語の場合にビルドに失敗するというのを
他の記事で読んだ記憶があるので、まだあまり日本語コメントはしないほうがいいのかな)

Windows側の設定

Windows8のタッチ時の視覚効果を無効にする

コントロールパネル→ハードウェアとサウンド→ペンとタッチ
タッチタブで、スクリーンをタッチしたときに視覚的フィードバックを表示するのチェックを外す。

まとめ

Unityは触れば触るほどよく出来ていて、さくっと実験してみたり気軽に遊べるのが本当に楽しい。

Unitychanのデータ早く欲しい!
UNITY-CHAN!

Unity4入門   最新開発環境による簡単3Dゲーム製作
浅野 祐一 荒川 巧也 森 信虎
ソフトバンククリエイティブ
売り上げランキング: 4,144

StartCoroutine、WaitForSeconds、そしてyield

MonoBehaviourの機能として提供されているStartCoroutineをちょっとだけ触ったので忘れないようにメモメモ。

logの1は即座に表示され、
logの2は3秒待ったあとに表示され、
logのisFlgは5秒待ったあとに表示される。

ここで、logの2とlogのisFlgのwaitTimeがもし両方とも3秒の場合は、一気に表示される。
つまりその前のコルーチンのwaitをその下に書いたコルーチンは引きづられない。

サンプルコード

using UnityEngine;
using System;
using System.Collections;

public class Coroutine : MonoBehaviour {
  // Use this for initialization
  void Start () {
    Debug.Log(1);

    StartCoroutine(wait());

    StartCoroutine(waitForCallback(5f, (bool isFlg) => {
      Debug.Log(isFlg);
    }));
  }

  // Update is called once per frame
  void Update () {
  }

  IEnumerator wait() {
    yield return new WaitForSeconds(3f);
    Debug.Log(2);
  }

  IEnumerator waitForCallback(float waitTime, Action<bool> callback) {
    yield return new WaitForSeconds(waitTime);
    callback(true);
  }
}

なかなか面白いね。
これよく見るサンプルではStartメソッドの中に書かれているけど、Updateメソッドの中で書く場合は、
フラグとかもたして、間引く?必要があるのかな。
じゃないと、フレームごとにUpdateメソッドが呼ばれて、キューにコルーチンが溜まりまくってしまいそう。
そもそもそんなタイミングで使わないのかな。

こうゆうの作っておけば便利なのかな

using UnityEngine;
using System;
using System.Collections;

public class Callback {
  public static IEnumerator waitForCallback(float waitTime, Action callback) {
    yield return new WaitForSeconds(waitTime);
    callback();
  }
}

gist

Callback.cs

参考リンク

Unity4入門   最新開発環境による簡単3Dゲーム製作
浅野 祐一 荒川 巧也 森 信虎
ソフトバンククリエイティブ
売り上げランキング: 4,554