💀

Liveviewで作るスロットゲーム

2021/10/26に公開

Phoenix LiveviewはSPAライクなUIをElixirのみで開発できる素晴らしいライブラリです。

つい最近バージョン0.17.0がリリースされ、アニメーションを使った機能がより豊富になりました。
こちら参考

どのくらい複雑なアニメーションが開発できるのか試したくなったので、パチスロを作ってみました。

パチスロのリポジトリ
localで遊べます

パチスロにしては見た目はシンプルです。。。(パチスロ未経験)
サンプル画像
slot_gif1.gif

スロット画面のボタンを押すと押された列のスロットが止まります。
三列止まったときに横か斜めで絵柄が3つ揃うと敵にダメージが加えられます。
7が揃った場合は倍のダメージとなります。

結論

ゲーム開発できる?くらいに複雑なアニメーションを作れます。
以下の3つの関数が大活躍しました。
Process.send_after/3
handle_event/3
handle_info/2

開発方法

まずはアニメーション用の画像を準備します。

ドクロの画像

CSSのアニメーションだとかゆいところに手が届かないので、画像をコマ送りにしてアニメにします。
サンプル画像のドクロの敵キャラはAsepriteというドット絵作成ツールを用いて連番のアニメーション画像を作成しました。

作成したシーンは
①登場シーン(13枚)

②通常時のシーン(11枚)

③ダメージを受けたシーン(17枚)

④死亡した時のシーン(55枚)

①〜④を一枚のwebp画像に変換し、スロットのアクションに応じて使い分けています。

スロットの画像

こちらもAsepriteを使って
①左列(16枚)
②真ん中列(16枚)
③右列(16枚)
④背景(1枚)
⑤ボタン(6枚)
を作成しました。

ドクロのアニメーションと異なる点は①〜⑤のアニメーションがそれぞれ別々の動作をするため、一枚のwebp画像にまとめるのではなく、全てSVGファイルとして出力した点です。
SVGを使うことで位置の細かい調整がしやすくなります。
また

use Phoenix.Component

とすることで

def button(assigns) do
  <svg ... >
    <path ... >
  </svg>

のように任意の名前でSVG画像を関数として定義でき、関数内でsocket.assignsに対する変更を加えることもできます。

詳しくは
Phoenix.Component

ドクロとスロットの絵柄をアニメーションにする

10fps前提で進めます。

基本的な考え方は
①mount時に最初の画像をsocket.assigns.hogeとして状態を持たせて、Process.send_after/3で自プロセスに画像更新用のメッセージを送る

def mount(_, _, socket) do
  Process.send_after(self(), :hoge2, 100)
  {:ok, assign(socket, %{ init_image: image }}
end

②メッセージをhandle_info/2で受けとり、次のフレームのためのメッセージを送る

def handle_info(:hoge, socket) do
  Process.send_after(self(), :hoge3, 100) 
  {:noreply, assign(socket, %{ init_image: image2 }}
end

③さらに続く画像のためのhanndle_infoを定義し、アニメーション全部分一周したら画面の動作を確認する。

ボタンをアニメーションにする

①ボタンのSVGのコードにphx-click="hoge"をつけてhandle_event/3で画像を切り替えました。

# template
<svg phx-click="hoge" ... >

# handle event
handle_event("hoge", _, socket) do
  {:noreply, assign(socket, %{ button_image: image }}
end

やってみて思ったこと

Liveviewを用いることでかなり柔軟なアニメーションが作成できいました。
ゲームエンジンを使わずとも初期のマリオ程度のゲームであれば開発可能ではないかと思います。

webでのアニメーションにもっと柔軟性が欲しいと考えるデザイナーやエンジニアの方は是非参考にしてみてください。

Discussion