2020年振り返り

2020年ふりかえり

2020年の活動記録です。今年も一年お疲れさまでした。

勉強会登壇

今年はコロナの影響もあり勉強会の回数が減ってしまい、ちゃんとした登壇回数は2回でした。

勉強会主催

勉強会の運営としてはVRM勉強会を2回開催しました。

Microsoft MVP

再受賞できました。ただグローバルサミットはコロナの影響でオンライン開催となり、シアトルに行くことはありませんでした。

本の出版

ずっと昔から執筆していた本をついに出版することができました!

f:id:torisoup:20201231210325j:plain

また、ユニティさんに頼んで出版記念生放送を開催していただきました!

VRChat

VRChatで割と頻繁に遊んでいた年でもあります。 メインのアバターは「マロンちゃん」を改変したものを使ってます。

またワールドをいくつか作成しました。VRChat初心者でも楽しんでもらえるようなワールドになっていると思います。

来年の抱負
  • 貯金する
  • 人生

「Grapnel Gun Playground」というVRChatのワールドを作った話

(追記)配布しました

torisoup.booth.pm

はじめに

めちゃくちゃ久しぶりの投稿になります。 最近はVRChatでよく遊んでいるのですが、そこにワールドを1つアップロードしたのでそれの解説をします。

どのワールドか

「Grapnel Gun Playground」というワールドです。 f:id:torisoup:20200508205601p:plain

グラップネルガン(フックショット)を使って飛び回って遊ぼうというワールドです。

完成までの作業時間としては30時間弱くらいです。

元ネタ

ワイヤーアクション、グラップネルガン、フックショット、立体機動装 置、人によって呼び方がバラバラです。 自分は「グラップネルガン」と呼んでいますが、これは元ネタとしてバットマンを使っているからです。

自分は「バットマンアーカムシリーズ」が好きなので、そのグラップネルガンの挙動に似せて作っています。 特に、「バットマンアーカム・シティ」以降の作品においては「グラップネル・ブースト」というシステムが追加されています。 (グラップネルガンで移動中に加速して、そのまま建物を飛び越して飛び出すことができる)


Batman Arkham City: How to use Grapnel Boost (PC tutorial)

このシステムが好きなので、これも再現しようと取り込んでみました。

こだわり

次の点にかなりこだわって作ってあります。

  • 操作性
  • 汎用性

「操作性」は、とにかく気持ちよく操作したいという欲求から来ています。 巷にグラップネルガンを使ったVRコンテンツはたくさんあるのですが、酔いやすかったり、思ったとおりに動けなかったりして結構なストレスが溜まります。 この点はかなり調整して作ったため、ぜひ触ってみて欲しいです。

「汎用性」は、グラップネルガンをワールドに依存させないということです。 後からグラップネルガンのアセット一式(スクリプト込み)で配布できるようにしたかったからです。 実際、グラップネルガンは疎結合に作ってあるのでそのままワールドに配置すればどんな場所でも動くようになっています。

仕組み

次のものを使って作られています。

  • VRCSDK3
  • Udon
  • UdonSharp

VRChatのワールド作成の仕組み

VRChatでは、ユーザが作成したコンテンツ(アバターやワールド)はすべてAsset Bundleの形式でアップロードされることになります。 つまりAsset Bundleで使えるものがVRChat上で使えるものということになります。

Udon

Udonとは、VRChatが開発している、VRChat用のプログラミング言語です。 ノードベースでビジュアルスクリプティングすることができ、作成したノードをコンパイルしてUdon VM上で実行するという仕組みになっています。

Asset Bundleは仕組み上、C#スクリプトを含めることができません。 そのためUnity標準のスクリプト機構を使わずに、独自のプログラム実行環境をVRChat側で用意したということになります。

UdonSharp

UdonSharpは、このUdonから生成されるILC#から生成してしまうというヤバイツールです。 深く考えず、普段どおりのUnity C#を書けば、ほぼそのままUdon上でスクリプトが動きます。 SerializeFieldも使えます。ヤバイ。

つまりどういうことか

Unityで動くC#の上で動くVRChatの上で動くUdonの上で動くUdonSharpを使って自由にスクリプトが書けるようになったということです。

素晴らしく便利なんですが、恐ろしくハードルは高いです。

これらのことをしっかり把握した上で書かないといけません。 特にUdonでは一部のUnity APIが使えないので、そうなったときに代替策を考えられるだけの引き出しの多さが必須になってきます。

普段からUnityでC#書いている人なら問題ないでしょうが、UdonSharpをきっかけにC#を勉強し始めるのは茨の道でしょう。

グラップネルガンのこだわり解説

閑話休題。 グラップネルガンを作る上でどのような点を工夫したかを紹介します。

1.狙いやすさ

「狙いやすさ」とは、「狙った場所にちゃんとアンカーを撃てるか」です。 特に遠くの対象を狙うとなったときに、ちゃんと対象に銃口を向けないと狙えないのは結構なストレスです。 ということで簡易的な「エイムアシスト」的な挙動を追加しました。

といっても、やってることはすごく単純でRaycastではなくSphereCastを使っているだけです。 SphereCastは、「球体の当たり判定」を飛ばして対象にぶつかったかをチェックする機能です。

SphereCastでかなり太めの判定を飛ばせば、対象が小さくても「だいたい狙えば当たる」という挙動を作ることができます。 つまり実質的なエイムアシスト。

また、小さいものを狙いやすくなるだけではなく、建物の縁(エッジ)部分も狙いやすくなります。 建物の上に登るためには縁を狙わないといけないので、このエイムアシストがかなり便利に使えます。

2.登りやすさ

グラップネルガンで移動ですが、そのまま作ると「まっすぐアンカーに向かって直進して終わり」ということになります。 そうすると「建物の上に飛び上がって登る」ということができず、アンカーに到達した時点で落下してしまいます。

さすがにこれは面白くないので、「アンカーに到達すると前に少し飛び上がる」という挙動を入れています。

// グラップネルガンで移動中の処理の一部
if (targetDirection.magnitude < 1.0f) // 対象の1m以内に近づいたら
{
    // 進行方向に上向きの補正を加えた上で、プレイヤーの速度に上書き
    _player.SetVelocity((startDirection.normalized + Vector3.up).normalized * 5.0f);
    return;
}

この動作は「バットマンアーカム・シティ」の「グラップネルブースト」という仕組みをそのまま模倣しただけです。 (もともと「グライダー(後述)」とセットで遊ぶためにグラップネルガンを作っていたのでこの仕組を入れた)

3.酔いにくさ

個人差はありますが、VR酔いは「視界が突然回転する」「自分の意図した動きと違う動きをする」という場合に起きやすいです。

このグラップネルガンは基本的には「等速直線運動」となるように作ってあるのでこの酔いにくくなったいます。 ただ愚直に等速直線運動だと味気が無いので、アンカーを撃った最初の数秒間は加速フェーズにとして徐々に加速するようにしてます。

4.操作のしやすさ

「できるだけ直感的に操作したい」という欲求から、こだわって次のような挙動に調整しました。

  • トリガー引きっぱなしで自動的にアンカーを発射する

    • 「狙いをつけてからトリガー引く」ではなく、「トリガーを引いたまま対象に銃口を雑に向ければ勝手に発射してくれる」という動きにしたかったため
    • 建物の縁を狙う時は、先にトリガーを引きっぱなしにしてから銃口を合わせると簡単に狙えます
  • 移動中に再度トリガーを引くと加速する

    • 慣れてきたら高速移動したいよね
  • 銃口を向けた方向にベクトル変更できる

    • アンカーに向かって直線的に飛ぶだけだとつまらないので、銃の向きで若干の方向調整ができるようにしました
    • 最終的にかならずアンカーに収束するような軌跡となります
  • 移動中に銃口を反対に向けると移動キャンセル

    • 間違えた場所にアンカーを撃った時に、キャンセルする仕組みが欲しかったのでいれました
    • ワイヤーを切るイメージです

こういう細かい調整が、小気味よく使えるグラップネルガンになったのかなと。

グライダーの解説

「Grapnel Gun Playground」には実は「グライダー」という別のギミックが置いてあります。

空を滑空するためのギミックであり、持っている間は滑空することができます。 挙動としては次のように動きます。

  • 落下すると加速する
  • 上昇すると減速する
  • 水平移動では速さは変化しない
  • 落下の開始地点よりは上に登れない(滑空なので)

仕組み

グライダーの仕組みですが、実はかなりガバガバな計算で作られています。

この動きを「揚力」の式を使って実現しようとするとめちゃくちゃ大変です。 なので今回は「力学的エネルギー保存の法則」だけを使って実装しました。

\displaystyle{
mg\Delta h = \frac{1}{2}mv^2
}

(落下した分の位置エネルギー = 運動エネルギー)

この式を変形して、速度の項について解くとこうなります。

\displaystyle{
v = \sqrt{2g\Delta h}
}

つまり開始地点より⊿h[m]落下したときの速度がこの式より導出可能になります。 この式をそのまま用いてプレイヤーの速度を上書きすることで、グライダーによる滑空っぽい挙動を実現しています。

using UnityEngine;

// すごく単純なグライダーのサンプル実装
public class Glider : MonoBehaviour
{
    private Rigidbody rigidBody;
    private float glideStartedY;

    void Start()
    {
        rigidBody = GetComponent<Rigidbody>();
        glideStartedY = transform.position.y;
    }

    private void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            transform.rotation =
                Quaternion.AngleAxis(60.0f * Time.deltaTime, transform.right)
                * transform.rotation;
        }
        else if (Input.GetKey(KeyCode.S))
        {
            transform.rotation =
                Quaternion.AngleAxis(-60.0f * Time.deltaTime, transform.right)
                * transform.rotation;
        }
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        var deltaY = glideStartedY - transform.position.y;

        // 力学的エネルギー保存則より、落下分を移動速度に変換
        rigidBody.velocity =
            transform.forward.normalized
            * Mathf.Sqrt(2 * Physics.gravity.magnitude * deltaY);
    }
}

これくらい単純なスクリプトでグライダーっぽい挙動は作れてしまいます。

グラップネルガンと協調する

  • グラップネルガンで移動中はグライダーを無効化する
  • グラップネルガンの移動がキャンセルされたら落下判定を行う

こういう処理を追加して、グライダーとグラップネルガンを協調して利用できるようにしています。

「プレイヤー」をグラップネルガンのターゲットとする

「Grapnel Gun Playground」では、他のプレイヤーに対してアンカーを撃ってグラップネルガンで移動することができます。 単純そうに見えて、ちょっと面倒くさいことをしています。

アンカーの追従

撃ち込んだアンカーを移動する対象にくっつけて追従させる方法です。

一番簡単なのは、アンカーとなるGameObjectを用意してそれを追尾対象のTransformの子に入れてしまう方法です。 実装としては一番簡単ですが、対象がDestroyされた時にアンカーも一緒にDestroyされてしまうという欠点があります。 このような挙動は予期せぬバグを生み出しかねないので、別方法を使います。

それは、愚直に「ベクトル演算で相手に追従する」というやり方です。 やり方としてはそんな難しくなく、アンカーと相手との相対位置を維持するようなスクリプトを書くだけです。

次のスクリプトはその例です。

using UnityEngine;

public class AttachSample : MonoBehaviour
{
    /// <summary>
    /// 追従する対象
    /// </summary>
    private Transform _target;

    /// <summary>
    /// 相対位置
    /// </summary>
    private Vector3 _deltaPosition;

    /// <summary>
    /// 相対角度
    /// </summary>
    private Quaternion _deltaRotation;

    private void Update()
    {
        if (_target != null)
        {
            // 相対ベクトルにぶつかったときの角度の逆を反映してから、
            // 現在の角度を反映させる
            transform.position = _target.position + _target.rotation * _deltaRotation * _deltaPosition;
        }
    }

    /// <summary>
    /// 衝突したらくっつく
    /// </summary>
    /// <param name="other"></param>
    private void OnCollisionEnter(Collision other)
    {
        _target = other.transform;

        // 衝突したワールド座標
        var cp = other.contacts[0];

        // 対象の中心部から衝突地点への相対ベクトル
        _deltaPosition = cp.point - _target.position;

        // 対象の現在角度の「逆」
        _deltaRotation = Quaternion.Inverse(_target.rotation);
    }
}

PlayerのTransform取れないじゃん問題

これはUdonの仕様なのですが、プレイヤーのGameObjectおよびTransformは取得ができません。 Physics.SphereCast等でプレイヤーとの衝突自体は取得できるのですが、RaycastHit.transformnullが返ってきてしまいます。 そのため相手のプレイヤーを直接追従させることはできず、代替策を考える必要があります。

代替策:プレイヤーを追従するColliderを別に用意する

かなり脳筋な解決策なのですが、「プレイヤーを追従するColliderを作ってそっちのTransformに追従させる」という手法をとりました。

using UdonSharp;
using VRC.SDKBase;

namespace GrapnelGunPlayground
{
    // Playerを追従するコライダーのマネージャ
    public class PlayerColliderManager : UdonSharpBehaviour
    {
        // プレイヤーを追従するコライダーたち
        private PlayerFollowCollider[] _colliders;
        private int _current = 0;


        public void Register(PlayerFollowCollider c)
        {
            // 遅延初期化で32個分用意
            if (_colliders == null) _colliders = new PlayerFollowCollider[32];
            _colliders[_current++] = c;
        }

        public override void OnPlayerJoined(VRCPlayerApi player)
        {
            if (_colliders == null) _colliders = new PlayerFollowCollider[32];

            if (player.isLocal) return;

            // 他のプレイヤーがワールドに参加したら
            // 未使用のPlayerFollowColliderを割り当てる
            foreach (var c in _colliders)
            {
                if (c == null) continue;
                if (c.IsUsing()) continue;
                c.Set(player);
                break;
            }
        }
    }
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;

namespace GrapnelGunPlayground
{
    // プレイヤーを追従するスクリプト
    public class PlayerFollowCollider : UdonSharpBehaviour
    {
        private VRCPlayerApi _vrcPlayerApi;
        private Transform _anchor;
        [SerializeField] private Collider _collider;
        [SerializeField] private PlayerColliderManager _manager;
        private int myId;


        public bool IsUsing()
        {
            return _vrcPlayerApi != null;
        }

        private void Start()
        {
            // 初期化時にManagerに自身を登録する
            _manager.Register(this);
            transform.position = -Vector3.up * 100;
            _collider.enabled = false;
        }

        public void Set(VRCPlayerApi player)
        {
            _vrcPlayerApi = player;
            myId = player.playerId;
        }

        private void Update()
        {
            if (_vrcPlayerApi != null && Networking.LocalPlayer != null)
            {
                var myPosition = _vrcPlayerApi.GetPosition();
                transform.position = myPosition;

                var d = Vector3.Distance(Networking.LocalPlayer.GetPosition(), myPosition);
                if (d < 3.0f)
                {
                    _collider.enabled = false;
                }
                else
                {
                    _collider.enabled = true;
                }
            }
        }

        public override void OnPlayerLeft(VRCPlayerApi player)
        {
            if (_vrcPlayerApi == null) return;
            if (player.playerId == myId)
            {
                _vrcPlayerApi = null;
                transform.position = Vector3.up * -100;
                _collider.enabled = false;
                myId = -1;
            }
        }
    }
}

UdonSharpは「List<T>が使えない」「配列の初期化は1回しかできない」「GetComponentに制限がある」などの制約があります。 これらの制約を回避するためにあらかじめ各プレイヤー分のオブジェクトを作成しておいて、起動時に登録するという手法をとっています。

f:id:torisoup:20200508204656p:plain
あらかじめシーンにおかれたCollider
(32個分用意されたColliderたち。脳筋実装。)

また、これらColliderLayerはプレイヤーと干渉させないために、VRChatで定義されている空いている適当なLayerを使うことにしました。 今回はとりあえずStereoRightというLayerを使いましたが、特に問題もなく動作しました。

f:id:torisoup:20200508204708p:plain
Playerと不干渉な未使用っぽいレイヤーを使う
f:id:torisoup:20200508204706p:plain
Collider側のレイヤをStereoRightに
f:id:torisoup:20200508204659p:plain
グラップネルガン側のLayerMask

最後に

「Grapnel Gun Playground」は細かい操作感にこだわって作ったので、ぜひ一度遊んでみてほしいです。

2019年振り返り

2019年の活動記録です。今年も一年お疲れさまでした。

勉強会 登壇

今年の登壇回数は5回でした。ちょっと少ないかな?

Unityで作ったゲームをDLカードで配布してみた話 #roppongiunity

UniTask入門 – Unity Learning Materials

MagicOnion入門

MagicOnion ~C#でゲームサーバを開発しよう~

Doozy UI 使おうぜ! #unity_lt

勉強会 主催・運営

勉強会の主催と運営です。 主にMagicOnion勉強会と、VRM勉強会を主催していました。

【Unity / .NET Core】 MagicOnion勉強会 - connpass

.NET Conf in Tokyo 2019 - connpass

第1回 VRM勉強会 - connpass

第2回 VRM勉強会 - connpass

第3回 VRM勉強会 - connpass

meetup.unity3d.jp

meetup.unity3d.jp

Microsoft MVP

再受賞できました。3月にまたシアトルに行ってきます。

旅行

今年はいろんな場所に行きました。

伊勢神宮 ・シアトル ・有馬温泉 ・嵐山温泉 ・伊東 ・熱海 ・箱根

映画

毎週のように映画を見ていました。

スパイダーマン スパイダーバース ・名探偵ピカチュウガールズ&パンツァー 最終章 第2話 ・ザ・ファブルONE PIECE STAMPEDE ・おっさんずラブ ?LOVE or DEAD? ・HELLO WORLD(2回) ・JOKER ・JOHN WICK: CHAPTER 3 - PARABELLUM ・ジェミニマン

うちの子

カシスちゃんに続いて「フィズ」ちゃんを公開しました。

グッズ

うちのこグッズをいろいろ作りました。

一部は販売してます booth.pm

2020年抱負

VRMのアプリを作ってリリースする ・ゲームを作って売る ・書籍を出版までもってく ・人生

Microsoft MVP 再受賞しました

Microsoft MVP for Developer Technologiesを再受賞しました!
今回で去年に引き続いて2度目の受賞になります。

Micorosoft MVPがどういうものかというと、Microsoftの関連技術についてコミュニティに大きく貢献した人を讃えようという制度です。
アワードカテゴリの中に「Unity」が含まれており、自分はそれ関係での受賞となります。

マイクロソフト MVP アワード プログラムとは - マイクロソフト MVP

先輩MVPの方々のレベルがすごく、正直ぜんぜん敵わないなぁと日々痛感しております。
全く追いつける気がしないのですが、それでも粛々とUnity向けのコミュニティ活動を続けて行きたいなぁと思っております。

継続は力なり。ということで今期も1年よろしくお願いします。

MVP Global Summit 2019 にいってきた

はじめに

2019/3/17-21に、マイクロソフト本社(アメリカ、ワシントン州レドモンド)で開催されていたMicrosoft MVP Global Summitに参加してきました。 今回はそれについての感想を書こうと思います。

MVP Global Summitとは

MVP Global Summitとは、毎年マイクロソフト社が主催する、MS MVP向けのカンファレンスです。 全世界からMS MVPの人たちが集結し、マイクロソフト社員の方の講演を聞いたり、討論したりできるかなり貴重なイベントです。

なのですが、講演タイトルを含め内容はほぼすべてNDAのため共有不可となっています。 そのためどういう講演があったとかはほとんど書けないです。

イベント参加費

Global SummitはMS MVPであれば参加することができ、参加費は無料です。 また、メインセッションの開催期間の前後のホテル代および食事はすべてマイクロソフト持ちです。 ホテルから会場への無料送迎もあるため、開催期間中はほとんど無料で過ごすことができます。 (ただし飛行機代や空港からの往復代金は自費で、だいたい20万円前後は必要)

ただしホテルは二人一組でルームシェアしたときのみ無料であり、一人部屋を希望した場合は半額自己負担となります。

食事

食事ですが、基本的にすべて大味です。 野菜の概念は無いため、芋か、なんかすごい濃い味付けの肉か、肉か、肉です。 特にホテルの朝食が4日間まったく同じだったため、最終日には完全に飽きてました。

ホテルの朝食

f:id:torisoup:20190323212908p:plain

f:id:torisoup:20190323213026p:plain

卵、ベーコン、ハム、マフィン、あとカットフルーツのみ。野菜がマジでない。

会場での食事

会場ではランチにケータリングが出ました。多少バリエーションはあり、ビーガン向けのメニューもあるため野菜が食える! なのだけど、大味なのは変わりなく、くどいので結構すぐ飽きます。

f:id:torisoup:20190323213410p:plain

f:id:torisoup:20190323213245p:plain

おやつ

カンファレンス会場では15時くらいになるとおやつが出てました。 シナモンクッキーが本当に美味しかった。

f:id:torisoup:20190323213559p:plain

f:id:torisoup:20190323213720p:plain

飲み物

会場には冷蔵庫があり、その中にいくつかの種類の炭酸飲料や乳製品が入っています。これらはすべてテイクフリーでした。 他にもコーヒーやフレーバーウォータなどがあり、この辺も飲み放題。

f:id:torisoup:20190323213844p:plain

スタッフがいて、注文するとその場で豆を引いてコーヒーを入れてくれる。 ただし結構並ぶ。

f:id:torisoup:20190323214010p:plain フレーバーウォータ。キュウリ風味がしれっとある。一番人気がなかった。

ホテル

自分たちが止まったホテルはCourtyard Marriott Bellevue Downtownでした。 Bellevueの中心街から2ブロックほど離れており、中心街から歩いて10分くらいの場所です。

4階建ての横に広い感じのホテルでした。窓からの景色は皆無。

f:id:torisoup:20190323214420p:plain

f:id:torisoup:20190323214458p:plain

f:id:torisoup:20190323214520p:plain (窓からの形式。裏路地。)

1点だけ不満があるとすれば、エアコンが異常に埃っぽいところです。 エアコンを付けると、鼻水と涙が止まらなくなり、室内で花粉症みたいな状態になりました。 (シアトルに花粉は無い)

マイクロソフト本社

とにかく広く、敷地内を無料のタクシーやバスが走っていました。 高層ビルなどはなく、3-4階建て程度の建物が点々とある感じでした。 筑波大学を想像してもらえるとわかりやすいかもしれない。

建物やオフィスの様子もNDAなので、そのへんの写真は撮っていません。

f:id:torisoup:20190323214738p:plain

f:id:torisoup:20190323214807p:plain

f:id:torisoup:20190323220405p:plain あと、会場に世界地図があったのですが、日本列島がありませんでした。 (さらにいうとイギリスとかスリランカもない)

f:id:torisoup:20190323220445p:plain ちなみにこの地図ですが、後に有志によって勝手に修正されてました。

カンパニーストア

敷地内にストアがあり、マイクロソフトグッズやSurface,XBOX本体やアクセサリーなどが販売されています。

f:id:torisoup:20190323215541p:plain

f:id:torisoup:20190323215618p:plain

訪れたタイミングではSurface製品がセールをしており、モノによっては最大$400引きくらいの価格で売られていました。 特にSurface Pro 6がTypeCover込でこのお値段!?みたいな感じ。 日本で買うより圧倒的にお得でした。

f:id:torisoup:20190323215749p:plain

値段に釣られて自分もSurface GO買っちゃった。 技適も付いてるので日本で使えます。

アフターパーティ

最終日はホテルを貸し切ってのパーティでした。本当にパーティ。

f:id:torisoup:20190323220129p:plain

f:id:torisoup:20190323220250p:plain

f:id:torisoup:20190323220149p:plain DJがいてダンスフロアがあったり、ボドゲスペースがあったり、フォトブースがあったりとかなり面白かったです。

感想

MS MVPという壁を超えてきている人たちばかりということもあり、参加者がみな非常に落ち着いた雰囲気のイベントでした。 今回は日本のMVPの方々とツアーで参加したのですが、ノリが完全に「大人の修学旅行」でした。

雰囲気もよく、カンファレンスの内容もかなり興味深いものばかりだったため、お金に余裕があれば次回も参加したいと思います。 そのためには英語を勉強しなくては…。

あとはノージャンルな写真たち

f:id:torisoup:20190323221047p:plain BestBuyという電気屋に入ったらいきなり展示されていたOculus製品。

f:id:torisoup:20190323221147p:plain Switch製品たち。プロコンが日本で買うより半額くらいで買える。

f:id:torisoup:20190323220550p:plain ニンテンドーSwitch対応のUSBタイプのGC型コントローラ。 日本ではそもそも売っていないはず。買った。

f:id:torisoup:20190323220919p:plain パイク・プレイス・マーケットの近くの写真。 スターバックスコーヒー1号店があった(すごい行列だったので買い物はしなかった)。

f:id:torisoup:20190323220719p:plain パイク・プレイス・マーケットの近くの公園。眼の前に見ているのは湖ではなく、海。

f:id:torisoup:20190323221355p:plain 街中で見かけたVRChat。 BellevueSquareのマイクロソフトストアに展示されていた。

f:id:torisoup:20190323221537p:plain 夕日。これくらい明るいのに19時前という。

2018年振り返り

はじめに

どうも、とりすーぷです。 周りの人たちが2018年の振り返りブログを書いているのをみて、自分も書こうかなと思ったので書いてみました。 2019年にオーバランしていますがご容赦ください。

続きを読む

【UniRx】 ニフティクラウド mobile backend のログイン周りをObservable化してみる

niftyのmBaaSであるニフティクラウド mobile backend(NCMB)のログイン周りの処理をUniRxを使って処理できるようにしてみました。

続きを読む