第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でシンプルなポップアップを実装する
スマホゲーとかでよく出るやつを作ってみた。
最近だと横からニュッと出るやつとか下からシュッと出るやつとかあるけど、今回は昔からあるダイアログ形式のやつを実装してみた。
まずは、シンプルに任意の時間でオブジェクトを拡大縮小させる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を使ってインスペクタから別のオブジェクトにメッセージ飛ばせるようにしている。
こっちの方がデザイナが演出とかの組み込みがやりやすそう。
スクリプトで書きたいなら scaleStartAsObservable
と scaleEndAsObservable
プロパティがあるのでそこから購読できる。
ちなみにUnityEventsを使ってはいるが、最終的には scaleStartStream
と scaleEndStream
の二本のストリームに集約しているので若干コードはすっきりした。
入れ子みたいにしても気持ちいい。
UnityPackageにしたのでご自由にお使いください。 https://github.com/adarapata/SimplyPopup/releases/download/0.1.0/SimplyPopup_0_1.unitypackage
おまけ
生成したポップアップオブジェクトのscaleEndAsObservableを購読して数珠繋ぎに消す
トロイ pic.twitter.com/TJEp3t9zid
— いも@EFB~相手は死ぬ~ (@adarapata) 2016年7月25日
Japan VR Hackathon2016に参加してきました in Fukuoka
ブログ書くまでがハッカソンらしい。 土日でやってきた。
Japan VR Hackathon(JVRH)とは
- 二日間でVRを利用したアプリを作るぞ
- みんなでチーム組んでやるぞ
というイベントです。 VRというテーマなので、会場である程度のデバイスが用意されている。 OculusとかViveなど、この手のデバイスはそれなりに高価なのでそれが好きに使えるのはとても嬉しい。 全国7カ所で同時開催されており、今回は地元の福岡会場にお邪魔してきた。
ちなみにVRでの開発はやったことないけどノリでなんとかなった
福岡会場の様子
福岡会場の開発のアットホームさ #JVRH2016 pic.twitter.com/Iv8Z1GHdzh
— FoR (@For_K_Est) 2016年6月18日
どういう経緯かはよくわからないけど会場が個人の家になっていて、個人宅に13人くらいが押しかけてハッカソンが行われた。 東京が300人入る会場で開催している中、僕たちは居間と和室と作業部屋で、机も足りないから時にはダンボール箱を駆使し開発に専念していた。
夜にピザとビール買ってきて宴会したのも福岡会場だけだと思う。
作ったもの
- チーム名: ソースコ
- タイトル: VR HANASAKA
- メンバー: プログラマ*2、 デザイナ*1、サウンド*1
発表資料 VRHanasaka.key - Google ドライブ
実行ファイル(Windows) vrhanasaka.zip - Google ドライブ Myo2台とHMDがないと遊べません!
動画は発表資料の中に埋め込まれています。緑の服着て腕をブンブン振ってるのが僕です。
テーマが「日本らしさ」だったのでそこから広げて最終的に「花咲か爺さんになって枯れ木に花を咲かせるゲーム」となった。
左手を広げると花を咲かせる粉を発射し、右手を広げると鬼を滅する豆を発射。 鬼が果敢に桜の木を自爆して壊そうとするので、豆で滅しながら粉で桜を咲かせたらクリア。
VR使ってるけど、鬼と桜と演出、だいたい2Dなのが他のゲームとは違うポイント。ペラッペラの板ポリをVRで見るのは割と面白い。
あと、流れてるBGM3曲は全て今回のハッカソンで作られたもの。すごい
使ったデバイス
- Oculus Rift DK2 (599$)
もはや説明不要のHMD。ハコスコとかCardBoardを使ったことはあるけど、やはりOculusの没入感は一歩抜きん出てた。
- Myo (179$)
腕に装着するタイプのデバイス。筋電によるジェスチャーコントロールができる。
サンプルの時点でグー、パー、ウェーブイン、ウェーブアウトが検知できる。生データも取れるのでやろうと思えば何でもできるみたい。
今回はこちらを二つ借りて両腕を検知した。贅沢の極みである。
チームの話
ハッカソンなので、その場でサクッとチーム決め。 今回は全員ほぼ初対面というところでアイスブレイクから始まった。 デザイナ・サウンドがいてプログラマが二人、且つプログラマは片方が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陣取りゲームができたのだった。
日曜日の八耐は、0から八時間でオークが陣取りする対戦ゲームを作ってました。せっかくなのでどっかに公開します pic.twitter.com/7el7rQCAws
— いも@EFB~相手は死ぬ~ (@adarapata) 2016年5月30日
ルール
- 相手に自分の陣地を踏ませたら勝ち
- 相手の陣地は上書きできる
- 寿司を取ると、塗れる範囲が広がったり、塗る速度が上がったりする
- 眼球や爆弾を取ると塗り方が変わる(直線だったり全方位型だったり)
- 落ちたら負け
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という機能を使って接続するところに問題があったらしく、すでに解決法まで出てた
- 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という便利コマンドを教えていただきました。
@adarapata jq噛ませて必要な絡むだけ取ると良い。
— P山 (@pyama86) 2016年3月16日
$ cat nodes/webdb.json | jq ".run_list" [ "recipe[dstat]", "recipe[apache]", "recipe[mysql]" ]
便利だ・・・。 https://stedolan.github.io/jq/