phaserを使ってクソゲー作った

この前、社内のLT交流会があったので、その発表用にちょっとしたゲームを作ってみた。

iPhoneとパソコンで連携するウェブゲーム

プレーヤーはiPhoneライトセーバーのようなものとして扱う。 iPhoneを左右に振ると、パソコン上のライトセーバーのようなものが同期して左右に揺れる。 パソコン上では次々と敵と思わしきものが上から落ちてくるので、ライトセーバーのようなものを接触させて、消滅させる。

そんなゲーム。面白さはあまりない。

動機

最初はforceというsalesforceのデータを扱えるCLIのツールについて紹介しようと思っていた。
GitHub - heroku/force: A command-line interface to force.com

でも途中でたまにはいつもと違うことをやってみようと思い、ゲームを作ってみることにした。

phaserとは

Phaser - A fast, fun and free open source HTML5 game framework

ゲームエンジンであってるかな。ブラウザでゲームを作るためのライブラリで、色々とできるっぽい。

  • ゲーム内のオブジェクト管理
  • 音声
  • 画像
  • 当たり判定
  • イベント管理

本格的なゲームを作った経験はないが、flashとかで簡単なものは作ったことがあって、思いの外苦労はしなかった。

それに、オブジェクトが当たった時の跳ね返りとか勝手にやってくれるので、かなりラクができた。

Typescriptの定義ファイルもあったので、コーディングもあまり苦労しなかった。

iPhoneとパソコンの同期

websocketを使った。サーバはGoで構築した。

  • iPhoneからの接続とパソコンからの接続を管理
  • iPhoneからのデータをパソコンに転送する

というやつを作った。

iPhoneでは、

  • 端末の角度を取得、計算して送信
  • 加速度を取得して、音量調整

を行なった。ライトセーバーっぽさを演出するために、強く振るとブーンの音が大きくなるようにした(がLTの時には聞こえなかったっぽい)。

パソコンでは、送信されてきた角度に合わせて、画面上のライトセーバーを傾けるようにした。

ゲームとしてのクオリティ

もともとすごいゲームを作ろうってわけでもなかったので、完成度の低さは気にしていないが、

素材が大事

ということが勉強になった。

発表の3週間くらい前からやり始めたが、websocketで通信し合うとか、ゲーム内の当たり判定とか、そういうのはライブラリなどのおかげでほとんど苦労はしなかった。

画像や音声の素材探しと調整のための加工が大変だった。正解が良く分からないし、無限に時間が吸われていく感じがした。

何でも作ると面白い

専門で仕事にしようとはあまり思わないが、自分でゲームを作るのも面白かった。

iPhoneとパソコンの画面が繋がった時にはやっぱり「おぉーー!すごいすごい!」ってなるし、こういう風に動くかな〜と思って実装したものが期待通りに動く楽しさや嬉しさがある。

良い経験になった。

goaのVSCode用のスニペット書いてる

goaで設計するとして、ResourceやMediaTypeとかを地道に書くのは正直めんどくさい。

なので、少しでもラクするためにコードスニペットを書いている。まだ書き始め。

vscode goa snippets · GitHub

ざっくりとしたテンプレート的な部分と、細かく調整が必要そうなParamとか、必要に応じて書き足して行こうと思ってる。

ある程度まとまったら、MarketPlaceに公開したい。

Angular入門 6日目

社内ツールをバックエンドをGo(goa)で、フロントエンドをAngularで作ることにした。

6日目。 今回も少しだけ作業した。

やったこと

通知周りを表示

bulmaの通知用のCSSがあるので、それを使った部分をComponentとして独立させてみた。

通知用と本文のComponent間でのやりとりが必要になったので、その辺を調べていたが、どうやらServiceを共有するのが良いらしい。

何でもかんでもServiceだなぁと思った。ModelをラップしただけのServiceとか…。 Fat Controler → Fat Model → Fat Service という時代の流れなのかな?

Angular入門 5日目

社内ツールをバックエンドをGo(goa)で、フロントエンドをAngularで作ることにした。

5日目。 ちょっと忙しくてあまり時間が取れなかった。

金曜日にやったこと

バリデーション

inputタグにpatternという属性を指定すると良いみたい。ngModelでバインディングしておく必要があるかもしれない。

<input type="text" id="code"
    name="code" [(ngModel)]="project.code"
    #code="ngModel" pattern="PJ-[0-9]{6}">

こんな感じで正規表現が使える。

バリデーションのメッセージ

invalidな時にエラーメッセージを出すには以下のようにする。

<div [hidden]="code.valid">
    <span class="help is-danger">
        codeはPJ-で始まる6桁の数字です
    </span>
</div>

hiddenはCSSdisplay: none を付けるようなので、他のCSSのスタイルと競合しないように注意する必要がある。

もしかしたら ngIf を使った方が良いかもしれない。

フォームの見た目

inputタグなどを赤くしたいような時には、規定のCSSクラスを定義する。

例えはinvalidな時には、 ng-invalid というクラスが付与されるので、form.cssなどを作って置いてそこに必要なスタイルを書いてHTMLから参照する。

.ng-invalid {
    border-color: #F00;
}

app.component.css に書けば良いのでは?と思ってたけど、違った。cssはcomponentごとに独立しているらしい。

Angular入門 4日目

社内ツールをバックエンドをGo(goa)で、フロントエンドをAngularで作ることにした。

4日目。

今日やったこと

ルーティング

そもそもルーティングの設計がおかしかったのと、謎の思い込みがあって、関係ないところで時間をつぶしていた。

// 抜粋
const appRoutes: Routes = [
  {
    path: 'projects',
    component: ProjectsComponent
  },
  {
    path: 'projects/new',
    component: ProjectDetailComponent
  },
  {
    path: 'projects/:id',
    component: ProjectDetailComponent
  },
];

この状態で /projects/new にアクセスすると、route parameter(と言うらしい)のidはnewになる。当たり前なんだけど、整数値の時だけ /projects/:id に振り分けられるという思い込みをしていた。

それに、Serviceでデータを取得する箇所でエラーが出ていたのに、ルーティングでのエラーだと勘違いしていた。

新規用ページの作成

編集ページを流用して、idが取れないときには空のオブジェクトを作ってComponentで使うようにした。

ようやく、一覧、新規、編集、削除とできた。

rxjs

Observable について深く気にしてなかったけど、rxjsと関係があるらしい。

使いこなせると、多分このコードも上手く直せるんだろうな。

  getProject() {
    let id = +this.route.snapshot.params['id'];

    if (id > 0) {
      this.projectService.getProject(id)
      .subscribe(project => this.project = project as Project);
    } else {
      this.project = new Project();
    }
  }

雑感

細かいところで色々わかってないところはあるものの、CRUDを実装したので、ダミーデータで一応動くようになった。

あとは、バリデーションとそれのメッセージ通知辺りかな。

Angular入門 3日目

社内ツールをバックエンドをGo(goa)で、フロントエンドをAngularで作ることにした。

3日目。

今日やったこと

Observerableの対応

PromiseからObserverableに変えたら上手くいってなかった件、単純にデータの取得が間違っていただけだった。

  getProjects(): Observable<Project[]> {
    return this.http.get(this.projectURL)
      .map(res => res.json().data as Project[]);
  }

res.json().datadata が抜けていた。

チュートリアルの後半にsearch関係のがあって、関係ないと思って読んでなかったけど、ちゃんと書いてあった。 チュートリアルよく読もう…

Typescriptのキャストについて調べた

Typescript: cast an object to other type. How? And instanceof or typeof? - acdcjunior’s

as で良いっぽい。

編集、削除の機能追加

チュートリアルを参考に、update, deleteを実装した。

気になったのが、リストから個別に削除するのはまぁ簡単なんだけど、そのあとリストを更新しなくて良いんだろうか?

まぁでもブラウザ上は存在している(と思われる)データが実はなかったというのを知るタイミングの問題かな。

ルーティングがよくわからん

Rails のルーティングを参考に設定したいんだけど、

// 抜粋
const appRoutes: Routes = [
  {
    path: 'projects',
    component: ProjectsComponent
  },
  {
    path: 'projects/new',
    component: ProjectDetailComponent
  },
  {
    path: 'projects/:id',
    component: ProjectDetailComponent
  },
];

サブディレクトリのところが上手くいかない。:idの方が優先されるのかなぁ。

Angular2のRouting, Formsの簡単なサンプル。ついでにFlux。(2.0) - Qiita

この辺参考に明日やってみよう。

雑感

コードを書くときはTypescriptなので、型が間違ってるとかはちゃんとチェックしてくれるんだけど、as とかのキャストを使ったりするところでは、実際動かすと上手くいかない。
キャストする時はその辺注意した方が良さそう。

Angular入門 2日目

社内ツールをバックエンドをGo(goa)で、フロントエンドをAngularで作ることにした。

2日目。

今日やったこと

編集画面のフォーム周り

モデルとフォームをバインディングする辺りのコードを書いた。

「これが噂に聞いていた双方向バインディングかぁぁぁぁ!!!」とちょっと感動した。

formタグのところだけ別のComponentにして切り出してみて、データの渡し方(@Inputあたり)を確認した。でも、なんかめんどくさくなっただけかも。

モデルの修正

管理すべきデータを間違えていたので、モデルを作り直した。

Typescriptの型チェックのおかげで、ダミーデータが誤っているのもすぐ分かって快適だ。

Serviceを作った

チュートリアルを参考にしてServiceを作ってそこからダミーデータを返すようにした。
HTTP - ts - TUTORIAL

PromiseをServiceとComponentで受け渡ししてるんだけど、直接、特定の型の配列を返してはいけないんだろうか?

あと、Observerableの方が良いらしい?
Angular2のHttpモジュールを眺めてベストプラクティスを考える - Qiita

これを見てPromiseを使わないようにやろうとしてるとこで、上手くいかなくて時間切れ。なんか勘違いしてるっぽい。

次回はこの辺対応して、新規と削除までの一通りの操作できるようにしたい。