DXOpalでスイカゲームみたいなのを作った

:::note info

これは [Ruby Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ruby) の6日目の記事です。一つ前の記事は[Rubyのプロダクトコードの複雑度の変化を可視化する](https://zenn.dev/ryutaromizokami/articles/333ef2dd6d9c51)、次の記事は[高校生向けプログラミング講座「福岡県Rubyキャンプ」にコーチとして参加してきました！](https://qiita.com/torifukukaiou/items/5165ac61a092002cf3a4) です。

:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/69c4f453-9eb8-ecf1-bbb0-c98f606defc8.png)

こんにちは。今年は mruby や picoruby にも触れてみたかったのですがもう12月ですね……おかしいな……。




<!-- ■■■■■■■■ -->
# できたもの

遊んでみたい方はこちらのリンクからどうぞ。

[https://sonota88.github.io/dxopal-suikalike/index.html](https://sonota88.github.io/dxopal-suikalike/index.html)

- マウス操作のみ対応しています
- ボール（正方形ですが）のサイズは 1 から 10 まで
  - 球にするのが面倒だったため正方形で作り始め、面倒だったため正方形のまま完成としました。この記事では「ボール」と言い張ります。
- オリジナルに寄せる方向ではがんばってません。それっぽければヨシ。

---

リポジトリはこちら。効果音はパブリックドメインとします。何かに使えそうなら使ってください。

<!-- sonota88/dxopal-suikalike -->
https://github.com/sonota88/dxopal-suikalike




<!-- ■■■■■■■■ -->
# DXOpal + 物理演算 → スイカゲーム

https://github.com/yhara/dxopal

DXOpal についてざっくり特徴を挙げると次のような感じでしょうか。

- 手軽にゲームを作れるフレームワーク
- [DXRuby](https://github.com/mirichi/dxruby) のブラウザ向け移植
  - API に互換性があり、仕組み上しかたないもの以外は DXRuby に合わせるみたいな方針だと思います
- 作ったゲームはブラウザで実行する
  - ここで [Opal](https://opalrb.com/) が使われる（Ruby のコードが JavaScript に変換されて実行される）
  - OS などの環境に依存せず同じように動かせる
  - ウェブで公開する形にすればダウンロードやインストールなしで使ってもらえる・遊んでもらえる・見てもらえる
    - 参考: [DXOpalで作ったゲームを無料で公開する](https://qiita.com/yhara/items/24141953af652ad96db0)


入門用のドキュメントとしては以下2つがおすすめです。

- [Rubyで始めるゲームプログラミング - DXOpal編 -](https://magazine.rubyist.net/articles/0057/0057-GameProgramingWithDXOpal.html)
- [Opal Advent Calendar 2018](https://qiita.com/advent-calendar/2018/opal) の「DXOpalでUndertaleっぽい画面を作る」シリーズ

---

DXOpal には物理演算ライブラリ（[matter.js](https://brm.io/matter-js/)）を利用した機能が実験的に用意されており、リポジトリの examples ディレクトリにサンプルがあります。

[https://github.com/yhara/dxopal/tree/master/examples/matter](https://github.com/yhara/dxopal/tree/master/examples/matter)

これを使って何かできないかなーと以前からボンヤリ考えていたのですが、今年は[スイカゲーム](https://www.aladdinx.jp/pages/suika-game)が流行りましたから、じゃあ作ろうかと。

作り自体は割と簡単です。難しいところ（ボール同士の接触判定や物理演算）は DXOpal と matter.js がやってくれるので基本部分は数時間で動くようになりました。

できあがりのサイズはこのくらい。小さく作るのが好き。

```
  $ wc -l *.rb
  57 ball.rb
 314 game.rb
 119 main.rb
 490 合計
```



<!-- ■■■■■■■■ -->
# メモ

以下、開発メモです。

## 物体の消去

なるべく DXOpal 本体に手を加えない範囲に収めたかったのですが、物体を消去する処理は必要なのでここだけ追加しました。

```diff_ruby
--- a/lib/dxopal/sprite/physics.rb
+++ b/lib/dxopal/sprite/physics.rb
@@ -56,6 +56,15 @@ module DXOpal
       `Matter.World.addBody(#{Sprite._matter_engine}.world, body)`
     end
 
+    def remove_matter_body
+      Sprite._remove_matter_body(@_matter_body)
+    end
+
+    def self._remove_matter_body(body)
+      _matter_sprites.delete(`body.id`)
+      `Matter.World.remove(#{Sprite._matter_engine}.world, body, null)`
+    end
+
     # Return true if `physical_body=` is ever called
     def self.matter_enabled?
       # Note: we cannot use `!!` here because @matter_engine may be a JS object,
```

これを追加するために matter.js のドキュメントとソースを軽く眺めましたが、matter.js 絡みの手間はその程度で済みました。見様見真似の間に合わせなのでひょっとしたらおかしなことしてるかもしれません。




## 音量の変更

やっぱり音量調節もしたいということで。Sound クラスと SoundEffect クラスの代わりに少しいじったクラスを用意してそっちを使っています。DXOpal 本体に手を入れなくて済むので今回はこの方式にしました。




## 事前コンパイル

今回は事前コンパイルする方式で開発を進めました。以下の記事を参照してください。

<!-- Dockerを利用してDXOpalアプリケーションを手軽に事前コンパイルする #Ruby - Qiita -->

https://qiita.com/sonota88/items/536c76d7383356592b92




<!-- ■■■■■■■■ -->
# まとめ

- DXOpal を使うとブラウザで動かせるゲームが手軽に作れて便利
- スイカゲームみたいなのを作った



以下おまけです。


<!-- ■■■■■■■■ -->
# dxopal-demos

今回作ったものとは無関係ですが DXOpal 関連ということでご紹介です。
思いつきで書いてみた小物をときどき追加しているリポジトリです。アルゴリズムの解説とか見るとこういうの作りたくなりますよね。

https://github.com/sonota88/dxopal-demos

---

メタボール

![Peek 2022-07-24 12-42_metaball.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/5797e0ab-db15-88c0-a4d1-72bf234558fc.gif)


---

Marching squares

![Peek 2022-08-13 15-06_marching-sq.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/ab3bfc57-c391-dbc9-de4b-abb68724d2fa.gif)


---

連立方程式を解くやつ（ガウス・ジョルダンの消去法）

![Peek 2023-10-01 14-13_gaus_jordan.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/17188300-21f9-4208-79ab-52b249e28a58.gif)


---

フーリエ変換

![Peek 2023-11-12 13-58_ft.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/b4db2b75-eb3d-d2a3-c410-880d7c3cceca.gif)



<!-- ■■■■■■■■ -->
# 簡単なCPUシミュレータ

今回作ったものとは無関係ですが DXOpal 関連ということでご紹介です。

DXOpal は「ゲームを作るためのフレームワーク」という位置づけにはなっていますが、いわゆる普通のゲームを作る以外にも

- Ruby でちょっとしたお絵描きやデータ可視化をしたい（特に動きのあるもの）
- ついでにマウスやキーボードからの入力も扱ってインタラクティブにしたい
- どうせなら音も出したい
- 作ったものを手軽に他の人に見せたい、使ってもらいたい

という場合にサッと使えて重宝しています。

そのような「ゲームではないもの」の例として、以前簡単な CPU シミュレータを作ったことがありました。

- 回路を描画して、
- マウスクリックでスイッチを切り替えられて、
- リレーが切り替わる時にカシャッと音が鳴る

というものです。

https://qiita.com/sonota88/items/a5d6d3539e0fb8040f74

![x00_1bit_cpu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/5a779825-a252-aad1-2aab-7d51fd708363.gif)

これ作るの結構楽しかったのでみなさんもぜひ！ と言いたい気持ち。



---

以下の書籍にもゲーム以外のアプリケーションの例が載っています。参考までに。

- [実践Opal](https://nextpublishing.jp/book/11812.html)（[達人出版会](https://tatsu-zine.com/books/practical-opal)）
  - DXOpal を使ってお絵かきアプリを作る
- [Pragmatic Opal　Rubyで作るブラウザアプリケーション開発ガイド](https://nextpublishing.jp/book/9555.html)（[達人出版会](https://tatsu-zine.com/books/pragmatic-opal)）
  - ICFPCビジュアライザ

<!-- ■■■■■■■■ -->
# 今年 Ruby 関連で書いたもの

せっかくのアドベントカレンダーですので、今年書いたものを貼っておきます。

<!-- Lramaで簡単な自作言語のパーサを書いた #Ruby - Qiita -->

https://qiita.com/sonota88/items/372da20581c16cbd4eea


<!-- Ruby + PyCall でデータサイエンス100本ノック（構造化データ加工編）をやってみた #Ruby - Qiita -->

https://qiita.com/sonota88/items/52d0e35dc5e6e112c02f


<!-- PyCall.rb + Pandas: DataFrame#query の代わりにS式っぽく書けないか試してみた #Ruby - Qiita -->

https://qiita.com/sonota88/items/7c81beca2c86fdf21556


<!-- Ruby/StringScanner: scan_full, search_full 系メソッドの整理 #Ruby - Qiita -->

https://qiita.com/sonota88/items/a7da299b84f17459967c




<!-- ■■■■■■■■ -->
# 去年の

去年のアドベントカレンダーではこういうのを書いていました。

<!-- 『RubyでつくるRuby』のMinRubyのパーサを書いた（手書きの再帰下降パーサ） #Ruby - Qiita -->

https://qiita.com/sonota88/items/84e4c16e5602b9771e92

「世は大パーサー時代！」より数カ月早く書いてしまってタイミングを逃した（？）感がなくもないですが、パーサにご興味あればこちらもどうぞ！