16
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ElixirAdvent Calendar 2022

Day 20

Elixirを使ってNeosVRで花火を打ち上げる 〜ブラウザーからVRのオブジェクトをいじる〜

Last updated at Posted at 2022-11-26

ElixirとNeosVRを使って打ち上げ花火をする仕組みを作りたいと思います
ざっくり言うと、ブラウザーからVRのオブジェクトを操作する遊びです
応用すれば、イベントでVRの外にいる参加者からリアクションを受け付けるアプリが作れますね
VRの画面をyoutubeやZoomで共有して、リアクション用にWebページを公開するだけです
VR外の人がVR空間に影響を与えることができるのです

役割

・Elixir
  花火の打ち上げボタンと打ち上げる花火の順番を管理
・NeosVR
  実際に花火を表示する(花火を上げるおもちゃが公開されていたので利用させてもらってます)
  作成した部分はElixirとの通信部分

概略図

Screenshot from 2022-11-26 21-25-37.png

実際に動かしてみる

Screenshot from 2022-11-26 21-59-41.png

Screenshot from 2022-11-26 22-03-56.png

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の場合パルスを送ります

 Screenshot from 2022-11-26 22-10-33.png

NeosVR内にあるkinokoさんのHA NA BIを利用させてもらいました
Screenshot from 2022-11-26 22-28-15.png

上記を元に、フリック(@FRICK)さんに通信部分と花火のおもちゃを連携してもらいました
Screenshot from 2022-11-26 22-11-26.png

もっと簡単なレベルであれば

本来はWebSocketを使うともっと高速なものが作れるのは知ってますが、あえて楽さをこのコラムでも意識してます。

このコラムのベースの知識はこちらをどうぞ

おまけ

このコラムの内容は
LiveView JP#9:Elixir生誕10周年記念!納涼LiveView何でも会で発表させてもらった内容です

イベント中はこんな感じでした

16
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?