SDL-Crystal-Bindingsをちょっと触ってみた

私はプログラムでお絵描きしたくなるタイプなので、 Crystal でももちろんお絵描きしたくなります。

というわけで SDL-Crystal-Bindings をちょっと触ってみました。

https://github.com/Hadeweka/SDL-Crystal-Bindings

---

ちなみに awesome-crystal の「Game Development」のセクションには他にもいくつかのライブラリが並んでいます。今回は定番感のある SDL を選んでみましたが、他のも触ってみたいですね（誰かやりませんか？ Raylib-cr とか）。

<!-- veelenga/awesome-crystal: :gem: A collection of awesome Crystal libraries, tools, frameworks and software -->
https://github.com/veelenga/awesome-crystal


---

何か手頃なお題はないか……としばし考え :thinking: 、[DXOpalのトップページ用のデモ](https://github.com/yhara/dxopal/blob/v1.6.0/examples/top_page/main.rb) を動かす（ために DXRuby/DXOpal のサブセットを作る）ことにしました。

---

できたもの:

https://github.com/sonota88/crystal-zatu/tree/main/sdl-mini-dxcrystal


![Peek 2025-01-04 13-09.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/9010e915-118b-d315-4c9f-dea4c9d953cd.gif)


冒頭で require している行を書き換えた以外は Ruby 向けに書かれたコードをそのまま動かすことができました。さすがですね（Crystal が）。

```crystal
# main.cr

# オリジナルでは
# require 'dxopal'; include DXOpal
require "./dxcrystal"; include DXCrystal
Window.width = 300
Window.height = 300
Window.bgcolor = C_WHITE
Window.load_resources do
  x = rand(Window.width)
  y = rand(Window.height)
  dx = dy = 2
  Window.loop do
    Window.draw_circle_fill(x, y, 10, C_RED)
    dx = -dx if x < 0 || x > Window.width
    dy = -dy if y < 0 || y > Window.height
    x += dx
    y += dy
  end
end
```

SDL を使っている処理は require 先の `dxcrystal.cr` の方に書いていて、200行ちょっとあります。全体は GitHub の方で見てください。


---

SDL-Crystal-Bindings には円を描く API がないので適当なものを自作しました。`LibSDL.render_geometry` を使って三角形を並べる方式です。

```crystal
    def self._draw_triangle(
          x1, y1,
          x2, y2,
          x3, y3,
          color : LibSDL::Color
        )
      vertices = [
        _sdl_vertex(_sdl_fpoint(x1, y1), color),
        _sdl_vertex(_sdl_fpoint(x2, y2), color),
        _sdl_vertex(_sdl_fpoint(x3, y3), color)
      ]

      LibSDL.render_geometry(
        @@sdl_renderer.not_nil!,
        nil, # texture
        vertices, vertices.size,
        nil, 0 # indices
      )
    end

    def self.draw_circle_fill(
          x, y, r,
          color : ArgbColor,
          z = 0
        )
      c = Color.from_argb(color).to_sdl_color

      n = 16
      pi2 = Math::PI * 2

      (0..n)
        .map { |i|
          rad = pi2 * (i.to_f / n)
          x2 = x + Math.cos(rad) * r
          y2 = y + Math.sin(rad) * r
          { x2, y2 }
        }
        .each_cons(2) { |pt_pair|
          pt1, pt2 = pt_pair
          _draw_triangle(x, y, *pt1, *pt2, c)
        }
    end
```


# メモ

- 今回はちゃんと `crystal init` でプロジェクトを作って shard を使う実績を解除できた
- [Hadeweka/SDL-Crystal-Bindings](https://github.com/Hadeweka/SDL-Crystal-Bindings) 内のコードと、examples/ に置かれているサンプル（git submodule になっていて、実態は [Hadeweka/sdl-crystal-bindings-examples](https://github.com/Hadeweka/sdl-crystal-bindings-examples) にある）を見る程度でなんとかなった。まあ今回はちょっとしたものなので……。
  - と書いている間に sdl-crystal-bindings-examples のリポジトリが消えていました。整理中なのかもしれません。



# この記事を読んだ人は（ひょっとしたら）こちらも読んでいます

<!-- 素朴な自作言語のコンパイラをCrystalに移植した #compiler - Qiita -->
https://qiita.com/sonota88/items/275c2b5407986b100cc8

以下は去年のアドベントカレンダー向けに書いたもの:

<!-- プログラミングのお題としてCrystalで簡単なコマンドラインRSSリーダーを作る #Crystal - Qiita -->
https://qiita.com/sonota88/items/013bcbd3f8a12e2687db

<!-- プログラミングのお題としてCrystalで簡単なbeepコマンドを作る #Crystal - Qiita -->
https://qiita.com/sonota88/items/fffe2588f8da15ac264d