2014年7月25日金曜日

【Unity、UniRx】オブジェクトをカラータイマーみたいに点滅させる


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。その4。

やりたいこと

ReactiveExtensionsを使ってオブジェクトをカラータイマーみたいに点滅させる。

やったこと

(UniRxはインポート済み)
まずは、点滅させるためのSphereを追加(Sphereじゃなくてもいい)
んで点滅してるのがわかるようにPlaneを追加(Planeじゃなくてもいい)
んでResourcesフォルダ作って、その中に"self-illumin"ってMaterialを作っておく(ShaderをSelf-illuminにしてね)
それからC#Scriptを作成してコードを以下のように変更(今回もTestScriptにしました)
そしてTestScriptをSphereにドラッグ&ドロップしたら出来上がり
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

public class TestScript : ObservableMonoBehaviour {

  /// 点滅速度
  public float Speed = 5f;

  /// 点滅時間[sec]
  public float DueTime = 3f;

  ///  RXのサンプルがAwakeだったので、ここに書きました。 
  public override void Awake ()
  {
    //参考サイト:http://neue.cc/2010/07/28_269.html

    //最初につけてあるShader
    var defaultShader = this.gameObject.renderer.material.shader;
    //光らす用のShader(Resourcesフォルダの中に"self-illumin"ってMaterialを作っておく)
    var illuminShader = Resources.Load( "self-illumin" ).shader;

    //マウスダウンされると値が発行される
    OnMouseDownAsObservable()
    //光らす用のShaderに切り替え&ライト追加。追加したライトを後続に渡す。
    //ここの書き方はあんまりよろしくない気がする。。。
    .Select(_ => {
      this.gameObject.renderer.material.shader = illuminShader;
      return this.gameObject.AddComponent();
    })
    //Updateで明るさ値更新してOnGUIのタイミングで明るさ値とライトを値として発行。
    .SelectMany(light => 
      UpdateAsObservable()
      .Select(__ => Mathf.Sin(Time.time * this.Speed))
      //OnGUIのタイミングで値を発行したいのでこれする。
      .CombineLatest(OnGUIAsObservable(), (sin,g) => new {sin, light})
    )
    //時間が来たら値の監視をやめて、Shader元に戻す&ライト削除
    .TakeUntil(
      Observable.Timer(TimeSpan.FromSeconds(this.DueTime))
      //Shaderを切り替える処理がメインスレッドじゃないと出来なかったのでこれした。
      .ObserveOnMainThread()
      .Do(_ => {
        this.gameObject.renderer.material.shader = defaultShader;
        Destroy(light);
      })
    )
    //TakeUntilで監視が終了するので、Repeatで繰り返す
    .Repeat()
    //値が発行されたらライトの明るさ更新
    .Subscribe(o => {o.light.intensity = Mathf.Abs(o.sin)*8f;});

    // If you use ObservableMonoBehaviour, must call base method ← サンプルにこう書いてますので、そのまま。
    base.Awake ();
  }
}


こんな感じ。
Hosted by UnityRoom.com

点滅の仕方がちょっとイビツだけどまぁいいや。
センスがないのはほっといて。
一応やろうと思ってた動きにはなったのでOKってことで。


2014年7月23日水曜日

【Unity】ScriptableObjectってなんぞ?

ScriptableObjectって何??

先日TLでScriptableObjectっていうものを聞いて気になってたので調べてみた。
ツイート引用ごめんなさい。

まー結果から言うとよくわからなかったんだけど色々できそうな予感はする。予感だけ。

とりあえず今日知ったことだけまとめとこう。

参考サイト

利用法

ざっと見た感じ、下記の利用方法でメリットがありそう
  • 特定のGameObjectに属さず、共通で参照されるべきデータを保持する場合
    • 例えば、レベルレイアウト、お店の商品、敵ごとのステータス、などなど
  • リリース時にあらかじめデータの入っているDBとして使う場合
    • これもレベルレイアウトや、商品とか。読み取り専用として扱うもの全般。
  • シーンをまたいで保持したい内容
    • ゲームスコアとか
    • ゲーム中に書き換えることもできるけど毎起動時に初期化される模様(後述)。
    • でもこの利用法ならシングルトンなオブジェクトで十分とも思える。
  • UnityPro限定だけど、AssetBundleを利用する場合
    • レベルレイアウトなどをScriptableObjectを使って.asset化しておき、アセットバンドル機能でダウンロードしたりすれば変換なしで直接利用できるっぽい。
      ってことが冒頭のalwei(@aizen76)さんの呟きですね。
  • 外部データ(Excelとか)をインポートするときに利用
    • そのままレベルデータとして利用したり。これは便利そう。
  • エディタ拡張
    • やったことないし未知
    • 拡張するなら間違いなく使うことになりそう

あと思ったこと

  • ハイスコアや所持アイテムなど、ゲーム終了・再開時のための永続化についてはやっぱりPlayerPrefsを使うのが正解っぽい…?
    (UnityでDBって何使うの?って会話でScriptableObjectの話がでてきたのでRDBの代わりとしてデータ保存目的で使えることを期待してたので残念)
    XMLとかJSONにパースしてPlayerPrefsと組み合わせればできそうだけどなんだかなぁ・・・

とりあえずゲーム内で使ってみた

よくわからんけどどんな動きするか見てみた。


新規Project作成


Editorフォルダを作って椿さんのこのスクリプトを配置
https://gist.github.com/tsubaki/5149402


Resourcesフォルダを作り、下記スクリプトを作成

ClickCount.cs
using UnityEngine;
using System.Collections;

public class ClickCount : ScriptableObject {

    public int Value;

}

Cubeを1つ配置(必要ならDirectionLightも)

下記スクリプトを作成し、Cubeにつける

Cube.cs
using UnityEngine;
using System.Collections;

public class Cube : MonoBehaviour {

    private ClickCount clickCount;

 void Start () {
        this.clickCount = Resources.Load<ClickCount>("ClickCount");
 }

    void OnGUI()
    {
        GUI.Label(new Rect(10, 10, 200, 80), this.clickCount.Value.ToString());
    }

    void OnMouseDown()
    {
        ClickCount.Instantiate(this.clickCount);
        this.clickCount.Value++;
    }
}

Resouces/ClickCount.csを右クリックし、Create ScriptableObjectを押す


ClickCount.assetができてるはず


再生ボタンを押して実行

CubeをクリックするとClickCount.assetのValueがインクリメントされる
Inspectorビューを見てると変化してくのがよくわかる。

しかもこれは再生を止めても保存されてる。
実行中に書き換えたものが.assetに書き込まれて残ってるっぽい。

この動きならハイスコアの保存とかに使えるか?
とも思ったけどWebPlayerとかだと毎回初期化されるみたいね。

↓再生ボタンで実行中に17まで進めて、WebPlayer向けにリリースしたもの。

Hosted by UnityRoom.com

毎回初期化されるのでブラウザをリロードすると17に戻る。


うーん、まだしっくりこないな。
エディタ拡張するようになるとなくてはならない存在になりそうだけど。

今日はここまで。

【Unity、UniRx】マウス操作でオブジェクトをグリグリ回す


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。その3。

やりたいこと

ReactiveExtensionsを使ってマウス操作でオブジェクトをグリグリ回す 。
ホントは【Unity】タッチ操作でオブジェクトをグリグリ回すみたいにタッチでやりたかったんだけど、実機がないのでマウス操作にしました。コードはもちろん丸パクリ。
プロジェクトの構成は【Unity、UniRx】ダブルクリックを検知するのスクリプトを変えただけ。
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

public class TestScript : ObservableMonoBehaviour {

  /// 回転速度
  public float Speed = 0.01f;

  ///  RXのサンプルがAwakeだったので、ここに書きました。 
  public override void Awake ()
  {
    //参考サイト:http://neue.cc/2010/07/28_269.html

    //マウスドラッグを監視。マウス位置を値として発行してもらう
    var dragObservable = OnMouseDragAsObservable().Select(_ => Input.mousePosition);
    //マウス位置付きマウスドラッグ
    dragObservable
    //次の値が発行されるまで待って差分を値として発行
    .Zip(
      dragObservable.Skip(1)
      , (p1,p2) => new {x = p2.x-p1.x, y = p2.y-p1.y}
    )
    //値が発行されたら回転処理
    .Subscribe(deltaPosition => {
      //移動量に応じて角度計算
      float xAngle = deltaPosition.y * Speed * 10;
      float yAngle = -deltaPosition.x * Speed * 10;
      float zAngle = 0;

      //回転
      this.transform.Rotate(xAngle, yAngle, zAngle, Space.World);
    });

    // If you use ObservableMonoBehaviour, must call base method ← サンプルにこう書いてますので、そのまま。
    base.Awake ();
  }
}

うに部屋から埋め込みタグを取得できるようになったのでちょっと埋め込んでみる。
Hosted by UnityRoom.com

しまった。
これだと、ドラッグやめても最後のマウスの位置覚えてて動きがおかしい。
マウスアップで一旦終わらせて、それを繰り返すように修正
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

public class TestScript : ObservableMonoBehaviour {

  /// 回転速度
  public float Speed = 0.01f;

  ///  RXのサンプルがAwakeだったので、ここに書きました。 
  public override void Awake ()
  {
    //参考サイト:http://neue.cc/2010/07/28_269.html

    //マウスドラッグを監視。マウス位置を値として発行してもらう
    var dragObservable = OnMouseDragAsObservable().Select(_ => Input.mousePosition);
    //マウス位置付きマウスドラッグ
    dragObservable
    //次の値が発行されるまで待って差分を値として発行
    .Zip(
      dragObservable.Skip(1)
      , (p1,p2) => new {x = p2.x-p1.x, y = p2.y-p1.y}
    )
    //マウスアップで一旦監視終了
    .TakeUntil(OnMouseUpAsObservable())
    //このままだと1回しかぐりできないから、繰り返す
    .Repeat()
    //値が発行されたら回転処理
    .Subscribe(deltaPosition => {
      //移動量に応じて角度計算
      float xAngle = deltaPosition.y * Speed * 10;
      float yAngle = -deltaPosition.x * Speed * 10;
      float zAngle = 0;

      //回転
      this.transform.Rotate(xAngle, yAngle, zAngle, Space.World);
    });

    // If you use ObservableMonoBehaviour, must call base method ← サンプルにこう書いてますので、そのまま。
    base.Awake ();
  }
}

Hosted by UnityRoom.com

いい感じの動きになった。
うに部屋の使い方これであってんのかな?
動作サンプルは自動ツイートされると恥ずかしいな。新着ゲームにも出てくると恥ずかしいな。


2014年7月21日月曜日

【Unity、UniRx】画面を暗転させる


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。その2。

やりたいこと

ReactiveExtensionsを使って画面を暗転させる。
【Unity】シーン遷移時のフェードイン・フェードアウトを実装してみた コードを丸パクリしたけど、シーンは変わりません。
プロジェクトの構成は【Unity、UniRx】ダブルクリックを検知するのスクリプトを変えただけ。
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

public class TestScript : ObservableMonoBehaviour {

  /// フェード開始トリガー
  Subject trigger = new Subject();

  /// フェード中の透明度
  private float fadeAlpha = 0;

  ///  RXのサンプルがAwakeだったので、ここに書きました。 
  public override void Awake ()
  {
    //参考サイト:http://neue.cc/2010/07/28_269.html

    //ここで黒テクスチャ作る
    Texture2D blackTexture;
    blackTexture = new Texture2D (32, 32, TextureFormat.RGB24, false);
    blackTexture.ReadPixels (new Rect (0, 0, 32, 32), 0, 0, false);
    blackTexture.SetPixel (0, 0, Color.white);
    blackTexture.Apply ();

    //今回の場合、この変数は必要ない
    var clickFadeObservable = 
      //trigger.OnNextされると値が発行される
      trigger
      //OnGUIを監視対象に追加
      .SelectMany(_ => OnGUIAsObservable())
      //OnGUIのタイミング?でGUIの透明度を更新
      .Do(_ => {
        //透明度を更新して黒テクスチャを描画
        GUI.color = new Color (0, 0, 0, fadeAlpha);
        GUI.DrawTexture (new Rect (0, 0, Screen.width, Screen.height), blackTexture);
      })
      //フェードアウト・フェードインが終わるまでOnGUIを監視する
      .TakeUntil(
        //trigger.OnNextで発行された値(秒)でフェードアウトしてフェードインする
        trigger.SelectMany(sec => fadeOut(sec).ToObservable().SelectMany(_ => fadeIn(sec)))
      )
      //TakeUntilで監視が終了するので、Repeatで繰り返す
      .Repeat()
      //発行される値の監視を開始。特に何もしない。
      .Subscribe(_ => {});

    //マウスダウンされたらトリガーから値を発行させる。
    OnMouseDownAsObservable().Subscribe(_ => trigger.OnNext(1));

    // If you use ObservableMonoBehaviour, must call base method ← サンプルにこう書いてますので、そのまま。
    base.Awake ();
  }

  /// フェードアウト
  private IEnumerator fadeOut (float interval)
  {
    //だんだん暗く
    float time = 0;
    while (time <= interval) {
      this.fadeAlpha = Mathf.Lerp (0f, 1f, time / interval);     
      time += Time.deltaTime;
      yield return 0;
    }
  }

  /// フェードイン
  private IEnumerator fadeIn (float interval)
  {
    //だんだん明るく
    float time = 0;
    while (time <= interval) {
      this.fadeAlpha = Mathf.Lerp (1f, 0f, time / interval);
      time += Time.deltaTime;
      yield return 0;
    }
  }
}

やり方が合ってるか分からないけど、とりあえずやりたいことはできたっぽい。
コードの最後にXMLタグが付いちゃうのはなんなんだろ。

2014年7月20日日曜日

【Unity、UniRx】ダブルクリックを検知する


@Baiteen

ReactiveExtensionsがUnityでも使えるってのを今更知ったので、今更ながら使ってみた。

環境

・Unity 4.5.1f3
・UniRx 4.3 (AssetStoreから”UniRx”で検索してインポートしました)

やりたいこと

ReactiveExtensionsを使ってダブルクリックイベントを検知する。

やったこと

(UniRxはインポート済み)
まずは、ダブルクリックするためのCubeを追加(Cubeじゃなくてもいい)
それからC#Scriptを作成してコードを以下のように変更(今回はTestScriptにしました)
そしてTestScriptをCubeにドラッグ&ドロップしたら出来上がり
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UniRx;

public class TestScript : ObservableMonoBehaviour
{
  ///  クリック間隔 
  public class ClickInterval
  {
    public TimeSpan Interval { get; set; }
    public Vector3 Location { get; set; }
  }

  ///  ダブルクリックイベント引数 
  public class DoubleClickEventArgs : EventArgs
  {
  ///  クリック間隔リスト 
    public IList ClickIntervals { get; private set; }

    public DoubleClickEventArgs(IList clickIntervals){
      this.ClickIntervals = clickIntervals;
    }
  }

  ///  ダブルクリックイベントハンドラ 
  public event EventHandler DoubleClickEventHander;

  ///  ダブルクリックイベントを発生させます 
  private void OnDoubleClick (IList clickIntervals)
  {
    if (this.DoubleClickEventHander != null) {
      this.DoubleClickEventHander(this, new DoubleClickEventArgs(clickIntervals));
    }
  }

  ///  RXのサンプルがAwakeだったので、ここに書きました。 
  public override void Awake ()
  {
    //参考サイト:http://neue.cc/2010/07/28_269.html

    //今回の場合、この変数は必要ない
    var mouseDoubleClickObservable = 
      //マウスダウンされると値が発行される
      OnMouseDownAsObservable()
        //発行された値と前回の時間との差分を包んだオブジェクトを返す
        .TimeInterval()
        //マウスダウンされた位置もほしいので、自作のクラスにして後続に渡す
        .Select(i => new ClickInterval(){ Interval = i.Interval, Location = Input.mousePosition })
        //ダブルクリックなので2回マウスダウンされるまで貯めこむ
        //トリプルクリックを検知したければここを3にすればいけるんじゃないかな
        .Buffer(2)
        //”1回目のマウスダウン”の”前回の時間との差分”が1秒以上であること
        //これをしないと3クリックで2回ダブルクリックイベントが発生してしまう(1,2と2,3)
        .Where(l => l.First().Interval.TotalSeconds > 1)
        //”1回目のマウスダウン”以外のマウスダウンの”前回の時間との差分”が全て1秒未満であること
        //せっかちの人は0.5とかにしてあげればいいかな
        .Where(l => l.Skip(1).All(i=>i.Interval.TotalSeconds < 1))
        //発行される値の監視を開始。値が発行されたらダブルクリックイベントを発行させる
        .Subscribe(l => this.OnDoubleClick(l));

    this.DoubleClickEventHander += (sender, e) => Debug.Log("double click");

    // If you use ObservableMonoBehaviour, must call base method ← サンプルにこう書いてますので、そのまま。
    base.Awake ();
  }

}

実行してCubeをダブルクリックするとログが吐かれることを確認できる。はず。
こんな感じ。
って、あれ?うに部屋から埋め込みタグをコピー出来なくてサンプル貼れない。。。
まぁいいや。

UniRxの記事が少なすぎて焦った。


Related Posts Plugin for WordPress, Blogger...