ElixirとNeosVRを使って打ち上げ花火をする仕組みを作りたいと思います
ざっくり言うと、ブラウザーからVRのオブジェクトを操作する遊びです
応用すれば、イベントでVRの外にいる参加者からリアクションを受け付けるアプリが作れますね
VRの画面をyoutubeやZoomで共有して、リアクション用にWebページを公開するだけです
VR外の人がVR空間に影響を与えることができるのです
役割
・Elixir
花火の打ち上げボタンと打ち上げる花火の順番を管理
・NeosVR
実際に花火を表示する(花火を上げるおもちゃが公開されていたので利用させてもらってます)
作成した部分はElixirとの通信部分
概略図
実際に動かしてみる
12:25分あたりに動かしているデモあります
Elixir ソースコード
概略図の①と⑦の部分 router
① live "/" , MessageLive.Index, :index がLiveView用
③ get "/state", StateController, :state NeosVRからステータス取得用
lib/neos_event_web/router.ex
defmodule NeosEventWeb.Router do
use NeosEventWeb, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_live_flash
plug :put_root_layout, {NeosEventWeb.LayoutView, :root}
plug :protect_from_forgery
plug :put_secure_browser_headers
end
pipeline :api do
plug :accepts, ["json"]
end
scope "/", NeosEventWeb do
pipe_through :browser
live "/" , MessageLive.Index, :index
get "/state", StateController, :state
end
# Other scopes may use custom stacks.
# scope "/api", NeosEventWeb do
# pipe_through :api
# end
# Enables LiveDashboard only for development
#
# If you want to use the LiveDashboard in production, you should put
# it behind authentication and allow only admins to access it.
# If your application does not have an admins-only section yet,
# you can use Plug.BasicAuth to set up some basic authentication
# as long as you are also using SSL (which you should anyway).
if Mix.env() in [:dev, :test] do
import Phoenix.LiveDashboard.Router
scope "/" do
pipe_through :browser
live_dashboard "/dashboard", metrics: NeosEventWeb.Telemetry
end
end
# Enables the Swoosh mailbox preview in development.
#
# Note that preview only shows emails that were sent by the same
# node running the Phoenix server.
if Mix.env() == :dev do
scope "/dev" do
pipe_through :browser
forward "/mailbox", Plug.Swoosh.MailboxPreview
end
end
end
概略図の②と⑤の部分 mountとhandle_event
・mount
ブラウザーからアクセスした時に一番初めに処理されます
・handle_event("launch", %{"id" => id}, socket)
ブラウザのボタンを押した時に発火します
idは押されたボタンです
GenServerにidを送っています(⑥花火の番号を最後に追加)
lib/neos_event_web/live/message_live/index.ex
defmodule NeosEventWeb.MessageLive.Index do
use NeosEventWeb, :live_view
@impl true
def mount(_params, _session, socket) do
{:ok, assign(socket, :messages, [])}
end
@impl true
def handle_params(params, _url, socket) do
{:noreply, apply_action(socket, socket.assigns.live_action, params)}
end
defp apply_action(socket, :index, _params) do
socket
|> assign(:page_title, "launch")
|> assign(:message, nil)
end
@impl true
def handle_event("launch", %{"id" => id}, socket) do
NeosEvent.Worker.set(id)
{:noreply, socket}
end
end
概略図の③ページ表示と④phx_click
・③ページ表示
このheexファイルです
・④phx_click
phx_click="launch" phx_value_id={index}
でイベントを指定してます
これをforで1〜5を繰り返してボタンを5つ作っています
lib/neos_event_web/live/message_live/index.html.heex
<br>
<.h2>
<span class="m-4 text-transparent bg-clip-text bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500">
NeosVR Fireworks Launch
</span>
</.h2>
<br>
<%=for index <- 1..5 do %>
<span class="m-4">
<.button class="transition duration-500 ease-in-out bg-blue-500 transform hover:-translate-y-1 hover:scale-150" color="primary" size="xl" label={index} phx_click="launch" phx_value_id={index} />
</span>
<% end %>
<br>
<br>
<br>
<br>
<div class="w-128 animate-bounce text-5xl"><span class="text-purple-500">↑↑Click the number↑↑</span></div>
概略図の⑥と⑨の部分 GenServer
・⑥花火の番号を最後に追加
set でidをListの一番最後に追加
・⑨ 最初の花火の番号を取得
get でidをListの一番最初を取得
defmodule NeosEvent.Worker do
use GenServer
def start_link(state) do
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
## Callbacks
@impl true
def init(stack) do
{:ok, stack}
end
@impl true
def handle_call({:set, val}, _from, stack) do
stack = stack ++ [val]
{:reply, :ok, stack}
end
@impl true
def handle_call({:get}, _from, stack) do
{h, stack} = getval(stack)
{:reply, h, stack}
end
def set(val) do
GenServer.call(__MODULE__, {:set, val} )
end
def get() do
GenServer.call(__MODULE__, {:get} )
end
defp getval([]), do: {[], []}
defp getval([h | stack]), do: {h, stack}
end
概略図⑧ 次に上げる花火の番号を取得 (NeosVRから呼び出される)
GenServerからIDを順次取り出しています(⑨ 最初の花火の番号を取得)
lib/neos_event_web/controllers/state_controller.ex
defmodule NeosEventWeb.StateController do
use NeosEventWeb, :controller
def state(conn, _) do
render(conn, "state.txt", val: NeosEvent.Worker.get())
end
end
NeosVR LogiX
今回NeosVR内でつくったElixirとの通信部分
・HttpのGET通信をしています
・0.1秒に一回取得してます
・取得した結果が1〜5の場合パルスを送ります
NeosVR内にあるkinokoさんのHA NA BIを利用させてもらいました
上記を元に、フリック(@FRICK)さんに通信部分と花火のおもちゃを連携してもらいました
もっと簡単なレベルであれば
本来はWebSocketを使うともっと高速なものが作れるのは知ってますが、あえて楽さをこのコラムでも意識してます。
このコラムのベースの知識はこちらをどうぞ
おまけ
このコラムの内容は
LiveView JP#9:Elixir生誕10周年記念!納涼LiveView何でも会で発表させてもらった内容です
イベント中はこんな感じでした