imog

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

素振りをしている

8月入ったあたりから不健康がヒートアップしてきたので、一週間前くらいから木刀素振りを始めている。

木刀とは

こちらです

木刀 - Wikipedia

なぜ素振りなのか

ジョギング、筋トレ諸々やってみたけどどれもあまり長続きしなかった。

中学時代に剣道やっていたので木刀を持っており、素振りなら昔やってたしいけるかなと思って、始めてみた。

これが結構楽しい

内容は

  • 前後素振り50
  • 跳躍素振り30
  • 片手素振り20

入部したての時くらいのメニューしかこなせないことがわかったので自分に甘くしている

メリット

外でできる

腕立て腹筋背筋を外でやると若干変な目で見られかねない。

しかし自分の部屋には冷暖房器具が存在しないので基本的にサウナ室になっており、やる気が起きない。そこで素振りである。

素振りは室内だとある程度の高さが必要なので屋外で行うことに何の違和感もない。ごく自然に、周囲に「己を鍛えています」アピールができる。「玉竜旗がんばってね」と言われるようになれば概ね成功と言えるだろう。

集中力が上がる

運動をした後は、若干集中力が上がるらしい。 ※出典なし

少なくとも、突然こんな記事を書くくらいには集中力が増す。

デメリット

通報される

一軒家で自分の敷地内であれば木刀を持とうが素振りしようが問題はないが、マンションやアパートなどであれば時間帯には気をつけたほうがいい。

22時に木刀を持った男が家から出てくる姿を見られると事案になりかねない。

しかし、これは相手の素性がわからないから事案になるわけで、入居時に挨拶、普段すれ違う際の対応、ゴミ出しルールの徹底など、そのコミュニティに所属するための人付き合いをしっかりしておけば回避できるものと思われる。

知った顔であれば、「あら、こんな時間に木刀なんか持って闇討ちかしら?」と気さくに声をかけられるくらいで済むと思う。

結論

ご近所付き合いというのは、非常に大事なのである

ArborのStateBehaviourを継承してUniRXを少し使いやすくする

無料期間中にArbor: State Diagram Editorを入手しました。

caitsithware.com

毎回statemachineを自前で書いてて面倒だと思ってたのでこれはありがたい。 昨日から触り始めているけど、特別引っかかることもなく使いやすいです。

なお、Arborの使い方とかはテラシュールブログさんが細かく書いてるのでそちらを参照すると良さそうです。 tsubakit1.hateblo.jp

これで作るスクリプト内部でUniRXをごにょごにょして、State遷移してから変更あるまでHogeHoge〜みたいな処理を書きたかったので拡張してみました。

こういう拡張どこに置くか悩んだので、とりあえず CustomArbor 名前空間を作って ObservableStateBehaviour を作った。

using UnityEngine;
using System.Collections;
using Arbor;
using UniRx;
using UniRx.Triggers;
using System.Linq;

namespace CustomArbor
{
    public class ObservableStateBehaviour : StateBehaviour
    {
        private Subject<Unit> stateBeginStream = new Subject<Unit>();

        public IObservable<Unit> stateBeginAsObservable {
            get { return stateBeginStream.AsObservable(); }
        }

        private Subject<Unit> stateEndStream = new Subject<Unit>();

        public IObservable<Unit> stateEndAsObservable {
            get { return stateEndStream.AsObservable(); }
        }

        public IObservable<Unit> updateAsObservable {
            get { return this.UpdateAsObservable().
                            SkipUntil(stateBeginAsObservable).
                            TakeUntil(stateEndAsObservable).
                            Repeat();
                }
        }

        // Use this for enter state
        public override void OnStateBegin ()
        {
            stateBeginStream.OnNext(default(Unit));
        }
        // Use this for exit state
        public override void OnStateEnd ()
        {
            stateEndStream.OnNext(default(Unit));
        }
    }
}

やってることは、状態の開始時と終了時にそれぞれストリームを用意して、通知しているだけです。

また、StateBehaviourの通常のUpdateは OnStateBegin ~ OnStateEnd の間のみ走るので、代用としてupdateAsObservableを作ってます。

  • StateAは、0.5秒ごとに A! と出力する
  • StateBは、1秒ごとに B! と出力する
  • キーボードのAを押された場合 State X Down と出力して遷移する
public class StateA : ObservableStateBehaviour {

    public StateLink nextState;

    void Awake()
    {
        this.UpdateAsObservable().
            SkipUntil(stateBeginAsObservable).
            Sample(TimeSpan.FromSeconds(0.5F)).
            TakeUntil(stateEndAsObservable).
            Repeat().
            Subscribe(_ => {
                print("A!");
            });

        updateAsObservable.
            Where(_ => Input.GetKeyDown(KeyCode.A)).
            Subscribe(_ => {
                print("StateA Down");
                Transition(nextState);
            });
    }
}

public class StateB : ObservableStateBehaviour {

    public StateLink nextState;

    void Awake()
    {
        this.UpdateAsObservable().
            SkipUntil(stateBeginAsObservable).
            Sample(TimeSpan.FromSeconds(1F)).
            TakeUntil(stateEndAsObservable).
            Repeat().
            Subscribe(_ => {
                print("B!");
            });

        updateAsObservable.
            Where(_ => Input.GetKeyDown(KeyCode.A)).
            Subscribe(_ => {
                print("StateB Down");
                Transition(nextState);
            });
    }
}

こんな感じになります。

n秒間隔で流すという処理が挟まるので、updateAsObservable 使わず SkipUntil -> Sample -> TakeUntil と書いてるけど、もっと綺麗なやり方ありそう。

UniRXでマウスのドラッグを実装する

オブジェクトのドラッグであれば OnDragAsObservable で一発なんだけど、オブジェクトを対象としない場合に簡単なメソッドがなさそうだったので書いてみた。

public class DragObserver : ObservableMonoBehaviour
{
    private Subject<Vector2> onDragStream = new Subject<Vector2>();

    public IObservable<Vector2> onDragAsObservable {
        get { return onDragStream.AsObservable(); }
    }

    public override void Start ()
    {
        var mousePositionAsObservable = UpdateAsObservable().Select(_ => Input.mousePosition);

        mousePositionAsObservable.Zip(mousePositionAsObservable.Skip(1), (preview, current) => preview - current).
            DistinctUntilChanged().
            Where(_ => Input.GetMouseButton(0)).
            Subscribe(delta => onDragStream.OnNext(delta));
    }
}

使うときはこんな感じで。

DragObserver onDrag = FindObjectOfType<DragObserver>();
onDrag.onDragAsObservable.Subscribe(delta => print("差分:" + delta.ToString()));

一番欲しかったのはマウスの移動量で、前回の値をどうやって持ってくるかでずっと悩んでいたが下記のやり方でなんとかなった。

mousePositionAsObservable.Zip(mousePositionAsObservable.Skip(1), (preview, current) => preview - current)

Zip複数のストリームがそろったら流し込む、今回は以下の二つのストリームを待つようにしている。

  • mousePositionAsObservable(以下A)
  • mousePositionAsObservable.Skip(1) (以下B)

Aは毎フレームのマウス座標を流している。Bも同じだが、Skip(1)により最初のメッセージだけ無視している。

図で表すとこんな感じ

(毎フレーム処理するので本当は隙間なくメッセージ流れるけど見づらいので隙間を開けています)

---1-2-3-4-5-(Frame)

A -o-o-o-o-o-
    \ \ \ \
B ---o-o-o-o-
     | | | |
R ---o-o-o-o-

1フレーム前の座標Aと現在の座標Bで固めてそれぞれを preview, current として取り出して、マウスの移動量deltaを求めている。 Zip便利。

Where(_ => Input.GetMouseButton(0)). のところは、 SkilUntil(onMouse).TakeUntil(onMouseUp).Repeat() という感じで「マウスが押されている間購読して、マウスが放されたら購読をやめる」という感じに厳密にしたほうが良いかもしれない。

UniRX、スライドとかテキスト読むとすぐに納得できるけどいざ書くとき全く応用できてないので、 もっと積極的に組み込んで慣れるしかなさそう・・。

参考にしたページ

Learn Reactive Extensions

rxmarbles.com

※ 追記

zipじゃなくてBufferで良いのではというご指摘いただいたので試したらうまくいきました。 ありがとうございます。

public class DragObserver : ObservableMonoBehaviour
{
    private Subject<Vector2> onDragStream = new Subject<Vector2>();

    public IObservable<Vector2> onDragAsObservable {
        get { return onDragStream.AsObservable(); }
    }

    public override void Start ()
    {
        var mousePositionAsObservable = UpdateAsObservable().Select(_ => Input.mousePosition);

        mousePositionAsObservable.Buffer(2,1).
            Select(mousePosition => mousePosition.First() - mousePosition.Last()).
            DistinctUntilChanged().
            Where(_ => Input.GetMouseButton(0)).
            Subscribe(delta => onDragStream.OnNext(delta));
    }
}

Zipほげほげ部分を Buffer(2,1).Select に書き直したらうまくいった。

Bufferにskipも指定できるオーバーロードがあったみたい。 https://github.com/neuecc/UniRx/blob/master/Assets/UniRx/Scripts/Observable.Paging.cs#L215-L258

そもそもBufferを使った場合、貯めた分(Skipしたやつを除く)のメッセージのListが返ってくる。 今回は2回分、前フレームと現フレームがリストでくるのでそれをFirst()とLast()で差分取って解決した。

Buffer、塞き止める方は考えてたけどまとめて取れるのは意識できてなかった・・。

UnityのSkyboxを動的に弄る

会話の流れで、「Skyboxって動的にいじれるのかな」と思って調べたやつ。

Skyboxの情報を弄って動的に色を変えるやり方です。 http://i.gyazo.com/e690686477b66031ce47fa5bc66f605c.gif

マテリアルを準備しよう

なにはともあれSkyboxに使うマテリアルを新規作成。

今回は MySkybox という名前で新規マテリアルを作った。 使用するシェーダは、未設定時に使われている default-skybox と同じ Skybox/Procedual をつける

http://i.gyazo.com/d1f585228698f90571122b85f482eac1.png

window->lighting からLightingのメニューを開いて、 Environments Lighting の SkyboxにMySkyboxを指定する。

http://i.gyazo.com/4314db20dc0b7874fe2036ef84a651f9.png

下準備はOK

Skyboxの情報はどこにあるの?

RenderSettings.skybox でMaterialクラスのインスタンスが取得できます。シーンのライティング情報はこの辺に格納されている模様。

じつはこれを見つけるのに一番時間かかった。

どうやって色を変えるの?

RenderSettings.skybox.color の色を変える・・と思いきやそれだとエラーが出る。

Material doesn't have a color property '_Color'
UnityEngine.Material:get_color()

Skyboxは _Color プロパティーを使用していないのでこれは使えない。

というか、 Color プロパティて GetColor("_Color"); をやっていたのね・・。

シェーダが _Color を持っていないので、ちゃんと正しい名前を指定する必要がある。

Skybox/Procedualシェーダは以下のプロパティを用意している

  • _SunSize
  • _AtmosphereThickness
  • _SkyTint
  • _GroundColor
  • _Exposure

http://i.gyazo.com/b046698208406461a4af33fbcd8f2884.png

Unity上で表示される場合は右側の 「_」無しだけど、シェーダ側では左側の名前で持ってるので間違えないように注意。

この中で空の色を扱っているのは _SkyTint なので、これを取り出して値を弄ればよさそう。

実際にいじろう

以下は、毎フレーム空の色を -0.03して最終的に真っ暗にする処理です。

UniRXを使ってますが、あまりそこは気にしなくていいです。

   void Start ()
    {
        Material m = new Material(RenderSettings.skybox);
        RenderSettings.skybox = m;
        Observable.EveryUpdate().
            Subscribe(_ =>{
                var c = m.GetColor("_SkyTint");
                c.r -= 0.03F;
                c.g -= 0.03F;
                c.b -= 0.03F;
                m.SetColor("_SkyTint", c);
            });
    }

実行結果は記事トップのgifのようになります。

マテリアルのインスタンスからGetHoge、SetHogeでシェーダの任意の変数にアクセスできるので、 今回は _SkyTint のColor情報を取ってきて、黒くして、またセットを行っている。 ちなみにcolorが0~1の範囲超えてもエラー吐かなかったのでどこかで丸めてくれている模様。

一つ注意として、最初にインスタンスを新規で作ってSkyboxに設定している部分がある。

Material m = new Material(RenderSettings.skybox);
RenderSettings.skybox = m;

なぜわざわざ差し替えているのかというと、SkyboxはProjectフォルダのマテリアルを直接参照してるので、シェーダに値を入れたりすると、エディタ上でゲームを再生して終了しても 値が反映されたまま になってしまう。 一度直接やってしまったので、Projectフォルダの MySkybox の SkyTintの値が真っ黒のままになってしまっていて辛い思いをした。

なので、ゲーム開始時にその場で新しいマテリアルを生成してスワップすることで大元に変更を加えないようにしましょう。

GameObjectのInstantiate時と同じ考え方で大丈夫です。

Skyboxと銘打ったが、別にSkyboxに限らない話になってしまったことに気づいた。

UniRXでuGUIのテキストをアニメーションさせる

UniRXの勉強も兼ねて小ネタ的なやつ。

いわゆるノベルゲーのこういうやつ

これをuGUIのTextがついてるオブジェクトにペタっと貼ると動きます。

UniRXをインポートしていることが前提です

using UnityEngine;
using System.Collections;
using UniRx;


[RequireComponent(typeof(UnityEngine.UI.Text))]
public class TextAnimation : MonoBehaviour {
    [SerializeField]
    private float speed;

    void Start()
    {
        var current = GetComponent<UnityEngine.UI.Text>();
        string rawText = current.text;
        current.text = "";

        Observable.Interval(System.TimeSpan.FromSeconds(speed)).              // speed秒置きに
            Take(rawText.Length).                                             // 元の文字列の長さ分実行され
            Select(_ => 1).                                                   // 実行ごとに都度1を取り出し
            Scan((acc, currentLength) => acc + currentLength).                // 現在の実行回数を計算する
            SubscribeToText(current, length => rawText.Substring(0, length)); // rawから0~length文字取り出してcurrentに投げる
    }
}

実際使うときはボタンとかキーとか外部のアクションで文章を途中で止めたいとかありそう。 購読を一回止めて、再度実行というのがまだわかってないので次にでも書きます。

UnityFukuoka07をやってきた

およそ半年ぶり www.zusaar.com

31人の予定だったが、先日じょぎの後輩とかにお誘いしてみたら意外と参加者が多くて多分40人はいた気がする。Unity初心者がかなり多め(自己申告)

オフィス7Fの大きさ考えてMAX50人にしてたけど実際50人来てたらオールスタンディングだったかもしれない。

今回は、SPAJAMでやったことについて資料を上げて話した

www.slideshare.net

zxingがゼブラクロッシングという無駄にかっこいい略称と知って驚いた。

以下は他セッションの話

EventSystemsの話

www.slideshare.net

長峰さんのEventSystemsとはなんぞやと、活用方法のお話。 前回のrectTransformもだけど、便利だからなんとなーく使ってるくらいの認識の機能をしっかり保管できるくらいまとめ上げられてるの本当にすごいと思う。

自分はEventSystemsは知っていたが後半のUnityEventの方は把握できていなかった。エディタ上からやれるのは最高に便利なので活用していこうと思う。

今回聞きそびれたけどUnityEventのジェネリクスシリアライズした自前クラスとかいけるのかちょっと気になった。

Unityとストリートマップの連携の話

金子さんによるストリートマップAPIを使って、Unity上に実際の地図を表示していく話。 ストリートマップAPIの緯度経度から周辺の地図情報を取るのは面白そうだった。しかも結構早い。

後半に出てた標高APIを使って、周辺の標高情報を取得してTerrainを修正するのはオープンワールド系作るのに活用できそう。というか標高APIなんてあったのかという驚きが大きかった。

地理院地図|技術情報

日本の地形を動的に生成するオープンワールドなんかも面白いかもしれない。

標高APIもストリートマップAPIも通信に制限ないから無限に叩けるらしい。太っ腹!と最初思ったが単純にリスクを考えられていないのではという気持ちになって無駄に心配してしまった。

ユニティちゃんのゲーム作った話

金子さんの教え子の九大生の三人が、一ヶ月無いくらいの期間でUnity初心者からゲームを作ってきた話。並びに作ったもののお披露目会。

トロルを銃で倒すゲーム! => 銃弾が何故か真下に発生してしまい、その銃弾でプレイヤーが少し押し上げられる => 連射したら押し上げられて空に行く => そうだ、銃弾の軌跡で絵を描こう

上記の流れが秀逸すぎて腹筋が痛くなった。 というかそっちの方向性でゲーム作って欲しい。絶対面白い。

アンカンファレンス

Unity関連で気になることを付箋に書いてペタペタ貼ってもらい、それを元に雑談するタイム。 基本主催者4人が質問に答えるような感じだったが、後半は少しづつ主催者参加者含めた誰かが答えれるような感じになれた気がする。

記憶から抜け落ちてなければ、ピックアップしたキーワードは以下

  • 初心者はどうやって勉強したらいい?
  • 広告SDK何使ってる?
  • ゲーム以外でどんな事例がある?
  • アセットストアで販売てどうやるの?
  • UIの解像度がマシンごとにズレまくって困る。
  • Unityのライバル的エンジンは?
  • Unity+MMDてどう?
  • PVとか作りたいけどUnityって向いてる?
  • モーションはツールで作るべき?Unityで作るべき?
  • UnityからWEBページ観れる?
  • Unity+Bluetoothとかできる?

Unityで何ができるかというと割となんでもできると思っているんだけど、Unityの必要があるかどうかは別の話で、アニメーションとかPVとかは自分の慣れている方でやるのが早いなと思った。

Unityは何でもこなすスーパーマンではなく何でも大丈夫と言うイエスマンっぽい。

所感

自分はイベント立ち上げるのは好きなんだけど、継続的にやっていくのが致命的に苦手なので第七回の準備を進めてくれた岩本君にすごく助けられた。ありがとうございます。

また登壇してくださった長峰さん金子さん、九大生のお三方ありがとうございました。

毎度やはり参加人数が凄まじいのでUnityってすごいなと思った。

あと、明らかに初心者ってレベルじゃなさそうなスキルの人が結構いたので次回は登壇してもらえるようにアプローチかけたい所存。

SPAJAM福岡予選の所感

簡単に振り返って http://spajam.jp/2015/result/fukuoka.php

よかったところ

実装の速さが尋常じゃなかった

チームの人の実装速度がマジで速い。こうだろうというふわっとしたところからきちんと動くものが気づいたらできていた。寝てる間に妖精さんが頑張ってくれてたのかもしれない。

UIとかその辺もきちんと考えて作り上げてるから、実装後にデザイナーが入ってどうこうというのがあまりなかった気がする。普段の経験からできているのだろうなあという印象。 もちろんデザイナも仕事が早く、自分が灰色の質素な画面で開発しててmasterを取り込んだ瞬間いきなり背景とかボタンとか揃ってたこともあった。

取捨選択のスピードが程よい

自分の例でいくつかあげると、下記の三点を実装しようと考えていた。

  • 戦闘ログ保存、呼び出し機能
  • QRコード読み取り機能
  • Androidプッシュ通知機能

上記は全部自分がやったこと無いとこだったので、できるかどうかというところがよく見えていなかった。 なので30分程度調べて、目処が立たない場合は諦めるという感じで削っていった。

戦闘ログ保存は、いわゆる放置ゲーにおいてゲームを落として再起動したりしてもゲームが進んでおり、それまでの工程をユーザが見ることのできる機能のことを言っている。

やり方からわからなかったのでチームの人に聞いてみて実際に実装したらあっさりできたので調子こいて実装した。 ちなみに、その際に拙作のライブラリであるNattoを利用し大幅な開発コストの削減に成功いたしました。

github.com

大幅な開発コストの削減に成功いたしました。

QRコード読み取りは、Zxingというライブラリがググったら出てきたのでそれのサンプルコードを元に実装した

zxingnet.codeplex.com

調査から実装まで4時間もかからなかったのでここは凄く楽だった気がする。

一方AndroidのPush通知はどうもネイティブから書く必要があったらしく、今回の時間で作りきれるかというのと、そこに時間をかける価値があるかというのを話した結果お蔵入り。結果30分程度でやめましょうとなった。

そんなこんなを繰り返したので、実装したのに結局使わなかった・・とかめっちゃ時間かけたけど結局実装できなかった・・とかは回避できたと思う。

新しいことにチャレンジできた

上記のQRコード読み取りだったり。ハッカソンは時間が短いから自分の持ってる知識とかで回していくことが多いんだけど、今回は知らないところに足突っ込んで且つ終わらせれたのがよかった。

というか、そもそもゲーム内容決めるときにできるできないという話をしなかった気がする。 それでも上手く回せたのは取捨選択ができていたからかもしれない。

反省点

開発ルールがふわっとしていた

今回git使ってたんだけど、そもそも全員がgitに慣れているという状態ではなかった。

その中で、まあブランチ切っていい感じにやってプルリク出してね〜くらいのノリでやってしまっていたので開発終盤に大衝突(ビッグバン・コンフリクト)が起きた。急ぎでやっていて各々masterに直pushなどが重なった結果である。その他の小衝突(マイクロ・コンフリクト)が発生でタイムロスなどもあったので、これがなかったらもっと作り込めたかもね〜という話を祝賀会でやっていた。

とにかく時間がないので使い慣れたツールで動くのは重要だし、なんならバージョン管理しなくても全然良いのでここは反省点だなと思った。次回はgitに慣れておくか、別手段で行くかで。できれば前者。そしてルールを決めておこう。

開発中に他チームとの交流ができなかった

忙しすぎて他のチームのやってることが見えないのは勿体無かった。ギリギリネタかぶりは無かったけど。

あと学生さんのチームとかで初めてRailsでサーバ立てたとかでメチャクチャ苦労してたみたいな話を交流会で聞いたので、茶々入れに行けばよかったかなと思った。

少し寝坊

ごめんなさい

ちなみに今回短大の同じ弘中研究室出身の後輩たちが数人別チームで出ていたんだけど、自分も含め全員優秀賞以上はゲットしていたので、弘中研究室ハンパないなと思った。八耐ガチ勢マジパない。

他のチームだと KICKHOST チームの作ったアプリが個人的に凄くよかった。 「食事のときに使えるアプリ」というテーマに対して、食事中にスマホ触るんじゃねえよという方針から「食事中の電話を防ぐアプリ」を生み出していた。

Twillo API等を使って、電話の拒否や拒否メッセージの録音をして、あとはボタンひとつであらゆる電話をシャットアウトするアプリ。デザインもプレゼンも完璧で正直これが最優秀だと思ってた。

本選ではもっと効率良い開発体制にして、温泉に入る時間を増やしたい。あと最優秀賞も欲しい。

あと、今回名刺を持っていくの忘れて社会人としてダメな人になっていたので本選では名刺を持って行こうと思う