imog

主にUnityとかの事を書いています

UnityFukuoka12をやりました、発表しました。

5月くらいに11をやったので半年ぶり。

atnd.org

僕の発表はこちら speakerdeck.com

主に、マルチシーンを使うと便利だぞという話と、Prefabでの管理をSceneにできないかという話。

以下、スライドの補足。

今回なんでマルチシーンをテーマにしたかというとしたかというと実際にやってみたら楽だったから今後はスタンダードになるだろうと思ったのと、それにしてはその辺の話をした記事が調べても出てこなかったから。

このスライドはその辺の未来にもちょっと言及していたので共感できた。

www.slideshare.net

個人的にはPrefabは再利用するための仕組みであって、オブジェクトを保存する仕組みではないと考えている。 なので、複数のシーンにおいたりInstantiateしないのであれば直接シーンファイルの中だけに存在していた方が不要な小さいミスが減らせるんじゃないかなと思ってる。 例えばPrefab化しつつシーンに配置していたとき、以下の問題が起こりうる

  • PrefabとHierarchy上で微妙にパラメータが違ってることに気づかずウォォォ
  • 逆に誰かが知らずにPrefab側の値を変えてScene内で影響出ててウォォォ

小さいエラーで、修正もすぐできるんだけど頻発する可能性はあるし発生しないに越したことはない。 どちらも結局HierarchyとPrefabの二重管理が引き起こす問題だと思う。 特定のシーンでしか使わないならシーンにだけ存在した方が気持ちが楽になりそう。

多分この気持ちはみんな持っていてあまりPrefabを量産しない気概はあると思うんだけど、そうなると1シーンをエンジニア・デザイナが触る状況が発生してしまい、そこでコンフリクトしないように「Prefabにしてシーンに持っていきますね」ということになってるんだと思う。 そこで、じゃあそのPrefabをシーンにしませんか?という話をしたのが今回の発表ですた。

ただ、これを実現しようとしたときやはりシーン間のオブジェクトの依存関係をどうするかという問題が出てくる。 Prefabだとドラッグ&ドロップで参照できたのにどうしてもスクリプトを書かなくてはならなかったり。 ここをいい感じにできる方法がないものか。

ちなみに、リビングデイブでのシーン間のオブジェクトの依存は以下

  • ゾンビがデイブを追いかける(ゾンビはRoom, デイブはHumanControlUI)
  • デイブのライフが0になるとゲームオーバー(デイブはHumanControlUI、ゲームオーバー処理はMainGame)

依存先がどちらもユニークだったので、今回は FindObjectOfType で取ってきている。 これが複数のオブジェクトのうちの一個とかだとFindで決め打ちだのなんだのちょっと面倒な形になりそう。

ただ、個々のオブジェクトでFindだーとか書きまくってると管理に漏れが発生しそうなので一元管理した方がいいのだろうかとか思ったりした。どうやるかは考えてない。

シーン間依存問題とか絶対起きてるだろうからみんなのベストプラクティスをくれ!

ArborでUniRxを使いやすくするArborRxを作った

ということでArborRxというアセットをgithubに公開しました。

github.com

Arborとは

ケットシーウェアさんがアセットストアで販売しているアセットです。

https://www.assetstore.unity3d.com/jp/#!/content/47081

GUIで状態遷移を簡単に実装できるのが特徴です。 基本機能が揃っていているのと、結構ガシガシ自分でコード書けるので自分は愛用しています。

UniRxとは

UnityでReactiveExtensionsを実現するアセットです。

github.com

説明するとものすごく長くなるので割愛します。

ArborRxとは

その名の通り、ArborとUniRxを連携させるアセットです。 具体的に連携とは何かというと、まだ1機能しかないです。

  • GUI上でストリームを購読して遷移させる

リポジトリにも貼ってるやつですがこんな感じです。

ObserveTransitionは、同じState内のスクリプトを全部読んで IObservable<Unit> を返すメソッドを列挙します。 エディタ上で指定したメソッドが返すIObservable<Unit> をObserveTransitionが購読して、発行されたら任意のStateに遷移する仕組みです。 それまでもStateBehaviour内部でRxで書いてはいたけど、遷移条件が増えるほどTransition(state)を書き加えていく必要があったので、外出しできないかな?とやってみたのが今回です。 リフレクションする必要があったので若干面倒かと思ったが意外とさっくりできた。

これによって何が嬉しいかというと、StateBehaviourが遷移条件とか一切気にしなくて済むことです。 遷移はすべてObserveTransitionに任せて本来の処理だけを書いていけるので少し見晴らしがよくなります。 とはいえ、ストリームを定義するのは処理側なので実際のところはまだ気にする必要はあるんですけど。

今後はObserveTransitionにオペレータを付けれるようにしたいところ。 あとはUnit以外の型も許容するとか。個人的には通知してもらえればよいのでUnitでいいと思っており、やるかは微妙。

みなさまのプルリクお待ちしております。

第43回 八時間耐久作品制作会(仮) に行ってきた

(仮)までが正式名称 atnd.org

今回もゆったりとした雰囲気だった。

自分は前回から機能のパッケージ化が面白くなってきたので、今回も小さい機能を作っていた。

できたのがこちら。ミッコと呼びます。 github.com

Mikko is

Mikkoは、プラットフォームを気にすることなくタッチ処理を実装できるパッケージです。

Unityでスマホ向け開発を行うときに、タッチの挙動を調べるために Input.GetTouch などでTouch構造体を取り出すのが一般的だと思う。

しかし、このタッチの状態は実機だと問題ないけどエディタ上で再生したときに取得できないため、エディタ上での場合と実機の場合を両方書く、もしくはUnityRemoteなどで実機のみで確認する、みたいな対応が多かった。

ちなみにそれを解決するアセットはあるっぽいし、UnityもClossPlatformInputと言うものを提供している。

Unity5版Standard Assetsを使って仮想ジョイスティックを実装 - Qiita

前者は有料だったりするのと、後者はちょっと大袈裟すぎる時もあったのでシンプルなものを作ってみた。

使い方

タッチ関連の処理をエディタとスマホ実機で動かす場合、ざっくりとこんな感じ。

using UnityEngine;
using System.Collections;

public class Hoge : MonoBehaviour {
    void Update () {
        if(Application.isEditor){
            if(Input.GetMouseButtonDown(0)) {
                // タッチした瞬間の処理
            }
            else if(Input.GetMouseButtonUp(0)) {
                // 放した瞬間の処理
            }                
        }
        else if(Input.touchCount > 0) {
            var t = Input.GetTouch(0);
            if(t.phase == TouchPhase.Began) {
                // タッチした瞬間の処理                
            }else if(t.phase == TouchPhase.Ended){
                // 放した瞬間の処理                
            }
        }
    }
}

Mikkoを使うとこんな感じ

using UnityEngine;
using System.Collections;
using GuP;

public class Hoge : MonoBehaviour {
    void Update () {
        ITouch t = Mikko.input.touch;
        if(t.info == TouchInfo.Began) {
            // タッチした瞬間の処理
        } else if(t.info == TouchInfo.Ended) {
            // 放した瞬間の処理 
        }
    }
}

エディタだろうがスマホだろうがお構いなしにできます。

内部的にはMikkoはシングルトンになっており、シーン上に一つだけDontDestroyOnLoadで生成されます。

現在取れるのは三つだけ

  • position: タッチした座標
  • deltaPosition: 前回との差分ベクトル
  • info: タッチの状態

必要になったらガシガシ追加していこうと思う。 UnityPackageはこちらに

https://github.com/adarapata/Mikko/releases/download/0.1.0/Mikko_0_1_0.unitypackage

八耐感想

Vive使ったガンシューティングが面白かった。 www.instagram.com

難易度は高めだったけど、もっとのめり込めるFPSみたいになってて満足度高かった。

だけど後輩が、HMD装着して周りが見えない僕の頭皮を積極的に撮影しようとしていたのでVRは危険だと強く感じた。

Unityでシンプルなポップアップを実装する

スマホゲーとかでよく出るやつを作ってみた。

f:id:adarapata328:20160725232256g:plain

github.com

最近だと横からニュッと出るやつとか下からシュッと出るやつとかあるけど、今回は昔からあるダイアログ形式のやつを実装してみた。

まずは、シンプルに任意の時間でオブジェクトを拡大縮小させるTweenScaleクラスを作る。

UniRxがインポートされている前提なのでいますぐアセットストアから落としてこよう。

TweenScale.cs

using UnityEngine;
using System.Collections;
using UniRx;
using UniRx.Triggers;
using UnityEngine.Events;
using System;

[Serializable]
public class TweenScale {
    public Vector3 from, to;
    public float duration;
    public AnimationCurve curve = new AnimationCurve(new Keyframe(0,0), new Keyframe(1,1));
    public UnityEvent onBegin, onEnd;
    private GameObject target;

    private Subject<Unit> scaleStartStream = new Subject<Unit>();
    public IObservable<Unit> scaleStartAsObservable { get { return scaleStartStream.AsObservable(); } }

    private Subject<Unit> scaleEndStream = new Subject<Unit>();
    public IObservable<Unit> scaleEndAsObservable { get { return scaleEndStream.AsObservable(); } }

    public void Setup(GameObject t)
    {
        target = t;
        scaleStartAsObservable.Subscribe(_ => onBegin.Invoke());
        scaleEndAsObservable.Subscribe(_ => onEnd.Invoke());
        scaleEndAsObservable.Subscribe(_ => target.transform.localScale = Vector3.Lerp(from, to, curve.Evaluate(1.0F)));
    }

    public void Play()
    {
        scaleStartStream.OnNext(Unit.Default);
        Observable.EveryFixedUpdate()
            .Take(System.TimeSpan.FromSeconds(duration))
            .Select(_ => Time.fixedDeltaTime)
            .Scan((acc, current) => acc + current)
            .Subscribe(time => {
                float t = time / duration;
                target.transform.localScale = Vector3.Lerp(from, to, curve.Evaluate(t));
            },
            _ => {},
            () => scaleEndStream.OnNext(Unit.Default)
            ).AddTo(target);
    }
}

結構ゴチャゴチャしてるけど、実際の拡大縮小処理はこのストリームにまとまっている

scaleStartStream.OnNext(Unit.Default);
Observable.EveryFixedUpdate()
    .Take(System.TimeSpan.FromSeconds(duration))
    .Select(_ => Time.fixedDeltaTime)
    .Scan((acc, current) => acc + current)
    .Subscribe(time => {
        float t = time / duration;
        target.transform.localScale = Vector3.Lerp(from, to, curve.Evaluate(t));
    },
    _ => {},
    () => scaleEndStream.OnNext(Unit.Default)
    ).AddTo(target);

呼ばれた時点で scaleEndStream.OnNext(Unit.Default) を呼んでスケーリング開始時のイベントを発行する。 画像だとその時点で暗転用のオブジェクトをActiveにしている。

線形補間でスムーズにスケーリングさせるので、経過時間(time) / スケーリングにかける時間(duration)で現在の位置tを出す。

tを出したら挙動はAnimationCurveが持ってるので curve.Evalute(t) で現在の値を返すようにする。

duration秒経過したらストリームが終了するのでその時点で scaleEndStream.OnNext(Unit.Default) を呼んで終了時のイベントを発行する。

で、開くときと閉じる時の挙動は別々で用意したいことが多い気がしたので、上記クラスを二つ持たせたPopupコンポーネントを定義する。

Popup.cs

using UnityEngine;
using System.Collections;
using UniRx;

public class Popup : MonoBehaviour {
    public enum State { Open, Close, UnUsed }

    public State state { get; private set; }
    public TweenScale open, close;
    void Start()
    {
        open.Setup(gameObject);
        open.scaleEndAsObservable.Subscribe(_ => state = State.Open);
        close.Setup(gameObject);
        close.scaleEndAsObservable.Subscribe(_ => state = State.Close);
    }

    public void Open()
    {
        open.Play();
    }
    public void Close()
    {
        close.Play();
    }

    public void Toggle() {
        switch(state)
        {
        case State.UnUsed:
        case State.Close:
            open.Play();
            break;
        case State.Open:
            close.Play();
            break;
        }
    }
}

このコンポーネントはOpenとCloseしかできないシンプルなコンポーネント。 Toggleは開いてたら閉じる、閉じてたら開くけどあんまり使わないかもしれない。

実際にポップアップ使うとなると、ただオブジェクトが飛び出てくるだけでなくそれに付随した様々な処理が入ってくると思う。サンプルみたいに他の部分をタッチできないように暗幕を置いたりするなど。

その辺を色々考慮し始めるとキリがないので、UnityEventsを使ってインスペクタから別のオブジェクトにメッセージ飛ばせるようにしている。

https://i.gyazo.com/f7b488f32f4cc744dafe6154da94fa57.png

こっちの方がデザイナが演出とかの組み込みがやりやすそう。

スクリプトで書きたいなら scaleStartAsObservablescaleEndAsObservable プロパティがあるのでそこから購読できる。

ちなみにUnityEventsを使ってはいるが、最終的には scaleStartStreamscaleEndStream の二本のストリームに集約しているので若干コードはすっきりした。

https://i.gyazo.com/dac543bd6dabb94eb69350503f247ec7.gif

入れ子みたいにしても気持ちいい。

UnityPackageにしたのでご自由にお使いください。 https://github.com/adarapata/SimplyPopup/releases/download/0.1.0/SimplyPopup_0_1.unitypackage

おまけ

生成したポップアップオブジェクトのscaleEndAsObservableを購読して数珠繋ぎに消す

Japan VR Hackathon2016に参加してきました in Fukuoka

ブログ書くまでがハッカソンらしい。 土日でやってきた。

Japan VR Hackathon(JVRH)とは

こちらに All Japan VR Hackathon

  • 二日間でVRを利用したアプリを作るぞ
  • みんなでチーム組んでやるぞ

というイベントです。 VRというテーマなので、会場である程度のデバイスが用意されている。 OculusとかViveなど、この手のデバイスはそれなりに高価なのでそれが好きに使えるのはとても嬉しい。 全国7カ所で同時開催されており、今回は地元の福岡会場にお邪魔してきた。

ちなみにVRでの開発はやったことないけどノリでなんとかなった

福岡会場の様子

どういう経緯かはよくわからないけど会場が個人の家になっていて、個人宅に13人くらいが押しかけてハッカソンが行われた。 東京が300人入る会場で開催している中、僕たちは居間と和室と作業部屋で、机も足りないから時にはダンボール箱を駆使し開発に専念していた。

夜にピザとビール買ってきて宴会したのも福岡会場だけだと思う。

作ったもの

  • チーム名: ソースコ
  • タイトル: VR HANASAKA
  • メンバー: プログラマ*2、 デザイナ*1、サウンド*1

発表資料 VRHanasaka.key - Google ドライブ

実行ファイル(Windows) vrhanasaka.zip - Google ドライブ Myo2台とHMDがないと遊べません!

動画は発表資料の中に埋め込まれています。緑の服着て腕をブンブン振ってるのが僕です。

テーマが「日本らしさ」だったのでそこから広げて最終的に「花咲か爺さんになって枯れ木に花を咲かせるゲーム」となった。

左手を広げると花を咲かせる粉を発射し、右手を広げると鬼を滅する豆を発射。 鬼が果敢に桜の木を自爆して壊そうとするので、豆で滅しながら粉で桜を咲かせたらクリア。

VR使ってるけど、鬼と桜と演出、だいたい2Dなのが他のゲームとは違うポイント。ペラッペラの板ポリをVRで見るのは割と面白い。

あと、流れてるBGM3曲は全て今回のハッカソンで作られたもの。すごい

使ったデバイ

www.oculus.com

もはや説明不要のHMD。ハコスコとかCardBoardを使ったことはあるけど、やはりOculusの没入感は一歩抜きん出てた。

  • Myo (179$)

www.myo.com

腕に装着するタイプのデバイス。筋電によるジェスチャーコントロールができる。

サンプルの時点でグー、パー、ウェーブイン、ウェーブアウトが検知できる。生データも取れるのでやろうと思えば何でもできるみたい。

今回はこちらを二つ借りて両腕を検知した。贅沢の極みである。

チームの話

ハッカソンなので、その場でサクッとチーム決め。 今回は全員ほぼ初対面というところでアイスブレイクから始まった。 デザイナ・サウンドがいてプログラマが二人、且つプログラマは片方がVR経験者で片方がUnity得意と分野が綺麗に分かれていたので綺麗な作業分断ができた。

自分はゲーム本体の部分を開発して、もう一人はMyoデバイスの動作検証と導入を担当した。 Oculusの検証時間もいるかなと思ってたが、導入して一切問題も何も起きなかったので不要だった。Oculus優秀。

また、メンバーも全員優秀で特に詰まるみたいな状況にならなかったので比較的やりやすかった。 発表資料もデザイナさんがサクサクっとやってくれたし非常に良いチームだったなと思う。

VR使った開発やってみて

だいたいみっつ。

没入感がすごい

そもそもこれがウリなんだから当たり前なんだろうけど。 開発初期でとりあえずと画面上に青鬼(ホラー系だから自分でググってね!)の画像を板ポリに貼りつけてたんだけど、普通に悲鳴を上げてしまうくらい恐怖を感じた。

画面とは違うなんか一線を超えている感じがすごくやばい。 特にホラー系はメチャクチャ面白くなりそう。僕はやりたくない。

ゲームデザインが違う

HMDはあんまり長く装着すると辛い。自分も酔う方なので3分くらいが限界だった。 そうなると、気持ち悪くならないために1プレイのゲーム時間を結構意識する必要が出てきた。 VR-HANASAKAは割と画面が動くゲームなので、1プレイが長くても1分くらいで終わるような時間にしている。 それ以上超えると自分が辛い。

なので、死ぬまで頑張るみたいな耐久系ゲームは本当に耐久になってしまうのでは感じた。

あと、UI周りがいつもと比べてかなり苦労することになった。

通常のゲームだと、プレイヤーのパラメータやスコアなどは画面四隅に表示しておくというやり方がスタンダードだが、これがVRで見ると全然しっくりこなかった。まず、四隅を見ない。真ん中にしか意識が向かない。目線だけ向ければ見れるんだけど非常に辛かった。

現実世界で、オブジェクトが常に視界の固定位置にいることがしっくりこない。その方向に首を動かしても位置関係が常に変わらないというのが違和感の正体かもしれない。特に背けることもできないのがきつい。

ということでVR-HANASAKAでは看板のようなオブジェクトにスコアを書いて、顔を上に向ければ見えるくらいの位置に常駐させておくという実装にした。 「今何点だろう」という確認で上を向くのはしっくりきた。

デバッグが辛い

動作確認の度にOculusを装着するのは目の負担が凄まじく、普段のハッカソンの2倍は疲れていた。

後半は装着せずに頭に乗っけたまま首を動かしてコントロールするスキルを身につけた。

所感

打ち上げで南部さんとちょろっと話したけど、「ハッカソンは何をするかより誰とやるか」という南部さんの話は僕も同意で、今まで会ったことない人たちとその場で何かを作り、作ったものを語るという行為が面白いんじゃないかと思ってる。

福岡のハッカソンに来る人たち、基本的に良い意味でなんかおかしいので考えとか発想が面白い人が多い印象がある。 まず、隣で開発してたチームから「VRで鼻を伸ばして、その鼻を切り落としてミサイルのように飛ばしてバトルする」ゲームを作っていてよくわからなかったし。

チーム内の話もそうだけど他チームの人に何で作ったのかとかを聞くのも楽しくて、それがハッカソンの醍醐味だと思う。 なので、今回のような個人宅で酒を飲みながら開発するタイプのハッカソンは、個人宅故に全員が近く、終始なごやかな空気だったので割と良いのではないかと思った。 またやってほしい。

打ち上げのイカが美味しかったです。 www.instagram.com

第41回 八時間耐久作品制作会(仮)でゲーム作った

行ってきました atnd.org

今回は本当に0からやってみようということで10時から開発を始めた。 何作ろうか悩んだ結果最終的にSplatoon陣取りゲームができたのだった。

ルール

  • 相手に自分の陣地を踏ませたら勝ち
  • 相手の陣地は上書きできる
  • 寿司を取ると、塗れる範囲が広がったり、塗る速度が上がったりする
  • 眼球や爆弾を取ると塗り方が変わる(直線だったり全方位型だったり)
  • 落ちたら負け

8時間でゲーム自体は出来上がったけど音が一切入ってなく寂しい状態だったので、BGMや効果音を追加+若干バランス調整しました。 お蔵入りにするのはアレなので公開します。 ちなみにゲームパッド対応です。あと一人だと遊べません。かならずお友達を呼んでね。