imog

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

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や効果音を追加+若干バランス調整しました。 お蔵入りにするのはアレなので公開します。 ちなみにゲームパッド対応です。あと一人だと遊べません。かならずお友達を呼んでね。

Chef実践入門を読んでいる 4

球磨焼酎美味しい

今日の進捗 4.2 まで

  • Berkshelfを使って外部クックブックを管理しよう
  • roleとenvironmentsを使い分けて、見通し良い感じに書こう
  • 複数のノードにまとめて適用する処理はchef-soloにはないので外部のツールを使おう、capとか
  • Serverspec更新したよ

roleとenvironmentってなんだろなーと思ってたところが全部書いてた。役割と環境なるほど。 attributeが結構どこからでも定義できるので、うっかりよくわからんとこで上書きしちゃってたーみたいな事故が怖いなと思った。気をつけよう。

4章に入ってパッケージとかノード名とか変わったのでその辺を修正

Serverspec修正 by adarapata · Pull Request #2 · adarapata/chef-practice · GitHub

ちなみに、写経してvm二台立ち上げて片方にchef適用したら以下のエラーが出た。 FATAL: Cannot load configuration from /home/docker/chef-solo/dna.json

調べたらOpenSSHのControll Masterという機能を使って接続するところに問題があったらしく、すでに解決法まで出てた

cross-black777.hatenablog.com

  • ControllMasterをOnにするとsshセッションのコネクションを使いまわすので速度が速い
  • 同名ホストでポートだけ分けてたのでソケットファイル名が重複して通信に失敗した
  • よって、dna.jsonが作られていない

今回は書いてる通り --ssh-control-master no を渡して無効にしたら解決した。

このブログなかったら詰んでた気がするので気をつけよう。

Chef実践入門を読んでいる 3

三連休は全力で遊んだ。

今日の進捗 3.6 まで

  • 各種リソースの名前と定義方法が書いてたよ
  • Attributeはノードに紐づく属性情報を持つし、定義していくといいよ
  • data_bagはノードに紐付かない、グローバルなデータを持ちたい時に定義していくといいよ
  • cookbookのディレクトリ構造が書いてあったよ

リソース内で参照したディレクトリが存在しなかったら普通に落ちるので、directoryリソースでディレクトリを確保するのは結構あるとのこと。

templateは template/default下を見に行く。cookbook_file は files/default下を見に行く

templateは動的なコンテンツの際に使って、filesは静的なコンテンツを入れるという分け方が望ましい。 Script(bash)リソースは自由度めちゃくちゃ高い。が、冪等性から何まで自分で調べないといけないので手間はかかる。 どうしても細かいことをしたい時に。

createsコマンド大事。not_if, only_ifでもよし。ただ冪等であるかだけで考えるとcreatesの方が伝わりやすそう。

attributeは同じキーの値がノードオブジェクトにある場合はそちらが優先される。

databagはノード感で共有したいデータなどを格納してる。cap用のデプロイユーザとか定義しておいたり。

attributeはあくまでノードに関する情報で、data_bagにはそれ以外の情報を入れるべきとのこと。

リソースの種類は結構な数あったので、その都度調べていくのが一番良さそうだった。

そういえば、前回removeアクションって何に使うんだろうと書いてたけど、色々見てたらapacheを入れた後にwelcome.confをremoveしていてなるほどなとなった。

今日も読んだだけなのでリポジトリに変更なし。

Chef実践入門を読んでいる 2

今日の進捗 2.8~3.2まで

  • gitで管理しようぜという話があったよ
  • リポジトリの各ディレクトリの説明が書いてあったよ
  • vagrant環境以外にchef流す方法を知ったよ
  • chefの考え方を知ったよ
  • td-agentのレシピを読んだよ

Berkshelfというcookbook用bundler的な役割のツールがあり、その設定ファイルがBerksfile。 少し前にLibrarian-Chefをインストールした記憶があるけど、最近はこっちが主流っぽい。

プロビジョニングは、裏で ssh で繋いでから sudo chef-solo を叩いているとのこと。

chefは冪等性という話。 以前rubyのバージョンアップ時に前のバージョンどうなるんだろうと怖かったので、過去のrubyのバージョンを残したままPR出したの思い出した。 書いた状態に収束されるということは、それ以外に関しては何も担保しないので残り続けるのだった・・

td−agentのレシピはこれだった。本の内容とは若干の変更あり

chef-td-agent/default.rb at master · treasure-data/chef-td-agent · GitHub

結構内部で普通にrubyのコード書いてる。

packageのremoveアクションの使いどころってどういう時だろうとふと思った。 元々使ってたけどもう不要になったものをremoveで明示的に消して、いずれコードごと消すみたいな運用かな。

ohaiというツールはシステムの情報を取り出していい感じにjsonにしてくれる便利なやつだった https://github.com/chef/ohai

試しに ohai | head したらBroken pipeしたので扱いには気をつけようと思う。

本とソース読んだだけなので、特にコミットは無し。

おまけ:弊社イケメンエンジニアからjqという便利コマンドを教えていただきました。

$ cat nodes/webdb.json | jq ".run_list"
[
  "recipe[dstat]",
  "recipe[apache]",
  "recipe[mysql]"
]

便利だ・・・。 https://stedolan.github.io/jq/

Chef実践入門を読んでいる 1

先週買ったChef実践入門のログを残すことにした。 www.amazon.co.jp

Chefは業務で使われているのでレシピを読んだり軽い修正のPRはしたことあるが、1から自分で書いたことはないのでやってみようと思った。サーバサイドに疎いし。

書いたものは下記のリポジトリにpushしていく github.com

所感を事細かに書いていった方が身につきそうだけど、きつくて続かなそうだから印象に残ったとこだけ書いていく

今日の進捗 2.7まで

  • centos6.7(本は6.5だが最新)をインストールしたよ
  • chef soloをvagrant上に入れてゲストOS上でレシピ書いていったよ
  • ホストOSにknife solo入れて手元で構築するようにしたよ
  • dstat、mysqlapacheのレシピを書いたよ
  • ついでにServerspecも入れて上記の三つがインストールされているかのテストも書いたよ PR

特につまづくことはなかった。 actionで出てきた enable start がそれぞれ sbin下の chkconfig service の実行に相当すると知ったが、そもそもsbin下のコマンドを全然知らなかった。知見を得た。

Serverspecで今回はdstat、mysql、httpで三つテストファイル区切ったんだけど、どのくらいの粒度が良いのだろうかと気になった。ツールごとに区切っていくとかなり増えそう。

UnityWebRequestについてちょっと調べてみた

この記事はUnity 2 Advent Calendar 2015のエントリです。

UnityWebRequest

UnityWebRequestとはWWWに変わる新しいHTTP通信用のクラスである。

docs.unity3d.com

名前空間Experimentalが含まれている通り、まだ実験的な機能なので今後も仕様が変更されていく可能性がある事をご了承いただきたい。

それでも、WWWと比較して十分使いやすくなってはいるので普通に活用して良さそう。

使い方

例えば、以下のコードはローカルホストにアクセスして、レスポンスをログに吐く

using UnityEngine;
using UnityEngine.Experimental.Networking;

public class HTTPTest : MonoBehaviour {

    // Use this for initialization
    void Start () {
        StartCoroutine(HttpRequest());
    }

    IEnumerator HttpRequest()
    {
        var request = UnityWebRequest.Get("http://localhost:4567");
        yield return request.Send();
        Debug.Log(request.responseCode.ToString() + ":" + request.downloadHandler.text);
    }
}

コルーチンで、通信終わるまで待つという点は今までと同じ。 WWWクラスの場合コンストラクタ呼び出したタイミングで通信しに行くが、UnityWebRequestの場合はSendを呼び出したタイミングで通信する。 なので通信する前にSetRequestHeaderで諸々のヘッダ設定ができるようになった。

WWWと比較した時の違いを幾つか挙げるとこんな感じ

  • RESTに対応
  • データとHTTPのハンドリング部分が分離されている
  • DownloadHandlerScriptでロギングや加工が便利

RESTに対応

多分これが一番大きな変更点。

ついにPUT、DELETEメソッドが実装された・・・。

PUTは引数にstringとbyte[]しか渡せない。 POSTはDictionaryが使用できるのでキーバリューペアでサクッといけるがPUTは自分で整形する必要がある。ちなみに文字列をバイト配列に変換する場合下記で行ける。

System.Text.Encoding.UTF8.GetBytes("foo=get")

引数を空文字列にした場合、以下のエラーが出る。

ArgumentException: Cannot create a data handler without payload data

因みにPATCHはないので、必要があればインスタンスmethodプロパティに直打ちしよう

request.method = "PATCH";

また、現状POST以外のメソッドはContent-typeが空になっている模様。 なので、そのままパラメータ付きでPUTしてもサーバ側によってはうまく受け取ってくれない可能性が高いので以下の感じでヘッダに組み込むのがいい

request.SetRequestHeader("Content-type", "application/x-www-form-urlencoded");

ここは修正されていくと思う、多分。

データとHTTPのハンドリング部分が分離されている

UploadHandlerはリクエストを飛ばす際のパラメータデータ、DownloadHandlerはレスポンスのデータを保持する小さなクラスになっている。 その二つをUnityWebRequestが所有している。 UnityWebRequestはURLやヘッダ情報、リダイレクト回数の設定などHTTP通信に関する処理を扱う。 WWWと違い、データそのものと通信がしっかり分離しており扱いやすい。 UploadHandlerとDownloadHandlerは外部から流し込むことができるからだ。 UploadHandlerを外から設定する場合はUploadHandlerRawクラスを使う。

var request = UnityWebRequest.Get("http://localhost:4567");
var upload = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes("foo=get"));
request.uploadHandler = upload;

コンストラクタの引数がバイト配列なので、変換が必要。 上記で書いたが、PUTはbody部分を空文字列にするとエラーになるので「適当な文字列を入れて初期化」->「uploadHandlerに流し込む」 という流れになった。もっといいやり方ありそう。

DownloadHandlerクラスそのものは流し込むことはできるがあまり意味はない。 むしろ、後述するDownloadHandlerScriptの時に役に立つ。

DownloadHandlerScriptでロギングや加工が便利

DownloadHandlerはダウンロード時の幾つかのタイミングでコールバックされる

  • ReceiveData データを読み取った際に呼ばれるコールバック
  • CompleteContent 読み取り完了した時に呼ばれるコールバック
  • ReceiveContentLengthヘッダーからデータの長さを受け取った時に呼ばれるコールバック

データを読み取った各所でメソッドを呼ぶようになっており、Unityではそれをユーザ側でカスタマイズできるようにDownloadHandlerScriptというクラスを提供しています。

サンプルコードから抜粋

using UnityEngine;
using System.Collections;
using UnityEngine.Experimental.Networking;

public class MyDownloadHandler : DownloadHandlerScript  {
    // Standard scripted download handler - will allocate memory on each ReceiveData callback
    public MyDownloadHandler(): base() {
    }

    // Pre-allocated scripted download handler
    // Will reuse the supplied byte array to deliver data.
    // Eliminates memory allocation.
    public MyDownloadHandler(byte[] buffer): base(buffer) {
    }

    // Required by DownloadHandler base class. Called when you address the 'bytes' property.
    protected override byte[] GetData() { return null; }

    // Called once per frame when data has been received from the network.
    protected override bool ReceiveData(byte[] data, int dataLength) {
        if(data == null || data.Length < 1) {
            Debug.Log("LoggingDownloadHandler :: ReceiveData - received a null/empty buffer");
            return false;
        }

        Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveData - received {0} bytes", dataLength));
        return true;
    }

    // Called when all data has been received from the server and delivered via ReceiveData
    protected override void CompleteContent() {
        Debug.Log("LoggingDownloadHandler :: CompleteContent - DOWNLOAD COMPLETE!");
    }

    // Called when a Content-Length header is received from the server.
    protected override void ReceiveContentLength(int contentLength) {
        Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveContentLength - length {0}", contentLength));
    }
}

上記のコードは、コールバックの各所でログに吐くような処理をしています。 あとは、これをUnityWebRequestのDownloadHandlerに流し込むだけです。

var request = UnityWebRequest.Get("http://localhost:4567");
var download = new MyDownloadHandler();
request.downloadHandler = download;

これで、データ受信時にログに吐いたり、ゲーム用にレスポンスを加工したりなど痒いところに手が届くようになる。

まとめ

現状、content_typeの問題など、まだまだこちらでカバーしなければならない問題はありますが、WWWクラスと比較するとかなり改善されており、期待が持てるAPIっぽい

明日は @yaegakiさんのmruby on Unityです