Elixirで作る「DDJ-FLX4のジョグ対応ブロックくずし」　その9　ゲームのメイン処理をつくる

Elixirで作る「DDJ-FLX4のジョグ対応ブロックくずし」　その8　あたり判定を作る
のづつきです

https://qiita.com/t-yamanashi/items/548545e70b41f26c0cfc

今回は、ゲームのメイン処理をつくります

今回の部分のgithub

https://github.com/ymn-t-yamanashi/ddj_block/releases/tag/v0.05

# 仕様
#### Blockモジュール
- main(nil) 
  - マップを初期化します
    - wall: 壁の座標のリスト
        - [x 横座標, y 縦座標, w 横幅, h 縦幅]の形式　以後、座標のリストはこの形式
    - blocks: ブロックの座標のリスト
        - ブロックが当たると削除していく仕様にします
    - player: プレイヤー座標です　左右の移動のみ
    - ball: ボールの座標です　壁、ブロック、プレイヤーに当たると反射します
    - ball_speed_x,ball_speed_y: ボールの速度、反射の時は符号が反転します

- main(%{} = data)
  - ブロックのあたり判定
    -  ボールがブロックに当たると該当のブロックの座標がリストから消えます
  - ボールの反射処理（速度）
    - 壁、プレイヤー、ブロックにボールが当たった時に反転します
  - ボールの座標更新
    - 上記の`ボールの反射処理（速度）`を元に座標を加算します
  - プレイヤーの座標更新
    - Agentの元に座標を加算します（DJコントローラーを動かすとAgentの値が変化します）
    - Agentの値を0にします(変化量をクリアします)
  - マップを更新する


#### DdjBlockモジュール

前回からの仕様変更
 
- set_state
  - 加算でなくDJコントローラーの値を格納する
- main_loop
  - Block.main(data)を呼ぶ
  - 描画するリストを作る
  - drawで描画するリストの内容を描画
- draw(draw_list)
  - 描画するリストの内容を描画
  - 四角ののみ描画できる

# ソースを書く

```elixir:ddj_block/lib/block.ex
defmodule Block do
  def main(nil) do
    blocks =
      1..3
      |> Enum.flat_map(fn y -> creat_blocks(y) end)

    %{
      wall: [
        [0.0, 0.0, 10.0, 900.0],
        [790.0, 0.0, 10.0, 900.0],
        [0.0, 0.0, 900.0, 10.0],
        [0.0, 900.0, 900.0, 10.0]
      ],
      blocks: blocks,
      player: [[350.0, 790.0, 100.0, 10.0]],
      ball: [[350.0, 400.0, 10.0, 10.0]],
      ball_speed_x: -2.0,
      ball_speed_y: -2.0
    }
  end

  def main(%{} = data) do
    [[x, y, w, h]] = data.ball
    wall_lr = data.wall |> Enum.slice(0..1)
    wall_t = data.wall |> Enum.slice(2..3)

    {collided, blocks} = Game.collided_with_filter(x, y, w, h, data.blocks)

    ball_speed_y =
      if Game.collided?(x, y, w, h, wall_t ++ data.player) or collided,
        do: data.ball_speed_y * -1.0,
        else: data.ball_speed_y * 1.0

    ball_speed_x =
      if Game.collided?(x, y, w, h, wall_lr ++ data.player) or collided,
        do: data.ball_speed_x * -1.0,
        else: data.ball_speed_x * 1.0

    [[bx, by, bw, bh]] = data.ball
    ball = [[bx + ball_speed_x, by + ball_speed_y, bw, bh]]

    [[px, py, pw, ph]] = data.player
    player = [[px + DdjBlock.get_state() * 20, py, pw, ph]]
    DdjBlock.set_state(0.0)

    Map.merge(data, %{
      ball_speed_y: ball_speed_y,
      ball_speed_x: ball_speed_x,
      ball: ball,
      player: player,
      blocks: blocks
    })
  end

  def creat_blocks(y) do
    1..5
    |> Enum.map(fn x ->
      [120.0 * x, 100.0 * y, 100.0, 50.0]
    end)
  end
end

```


```elixir:ddj_block/lib/ddj_block.ex
defmodule DdjBlock do
  @moduledoc """
  Documentation for `DdjBlock`.
  """
  alias Rayex.Structs.Rectangle
  use Rayex
  alias Socket.Web

  @sleep 10
  @color %{r: 0, g: 255, b: 0, a: 255}

  def main() do
    Agent.start_link(fn -> 400.0 end, name: __MODULE__)
    socket = Web.connect!("localhost", 4000, path: "/socket/websocket?token=undefined&vsn=2.0.0")

    socket
    |> join()

    Task.async(fn ->
      socket_loop(socket)
    end)

    init_window(800, 800, "DdjBlock")
    main_loop(true, nil)
  end

  def socket_loop(socket) do
    {_, text} = Socket.Web.recv!(socket)

    text
    |> Jason.decode!()
    |> Enum.at(4)
    |> Map.get("body", "0.0")
    |> String.to_float()
    |> set_state()

    socket_loop(socket)
  end

  defp main_loop(true, data) do
    data = Block.main(data)
    draw_list = data.wall ++ data.blocks ++ data.player ++ data.ball

    begin_drawing()
    clear_background(%{r: 0, g: 0, b: 0, a: 0})
    draw(draw_list)
    end_drawing()

    Process.sleep(@sleep)
    main_loop(!window_should_close(), data)
  end

  defp main_loop(_, data), do: nil

  defp draw(draw_list) do
    draw_list
    |> Enum.each(fn draw ->
      [x, y, w, h] = draw
      rectangle = %Rectangle{x: x, y: y, width: w, height: h}
      draw_rectangle_lines_ex(rectangle, 1, @color)
    end)
  end

  def send_data(socket, data) do
    data = """
    ["3","4","ddj:lobby","new_msg",{"body":"#{data}"}]
    """

    Web.send!(socket, {:text, data})
    socket
  end

  def join(socket) do
    phx_join = """
    ["3","3","ddj:lobby","phx_join",{}]
    """

    Web.send!(socket, {:text, phx_join})
    socket
  end

  def get_state, do: Agent.get(__MODULE__, & &1)

  def set_state(v) do
    Agent.update(__MODULE__, fn _ -> v end)
  end
end


```

# 動かす

```bash:ddj_server
ddj_block/ddj_server$ mix phx.server
```

```bash:ddj_client
ddj_block/ddj_client$ mix run --eval "DdjClient.hello"
```


```bash:ddj_block
ddj_block/ddj_block$  mix run --eval "DdjBlock.main"
```

# 動作動画
<iframe width="560" height="315" src="https://www.youtube.com/embed/heOc6nqp89c?si=AuskjmKhFXGqVEHk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


今回はここまで

つづく

https://qiita.com/t-yamanashi/items/5eeb35ac2e62b2567a96