残りゲーム体力が少なくてもブラウザゲームをRubyで簡単に書きたい

crisp-game-libというゲーム制作体力が残り10%でもゲームが書けるJavaScriptのライブラリがあるのだが、それをOpal に移植してRubyで書けるようにした。(画面クリックで遊べます)

https://ongaeshi.github.io/crisp-opal-test/fall/

2色のりんご🍎が落ちてくるので、同じ色を取ると得点、違う色は避ける、画面タップでプレイヤーの色が変わるので状況によって使い分けよう。モバイルの場合は両手プレイで移動と色替えの操作の手を分けると遊びやすいかも。

コード

https://github.com/ongaeshi/crisp-opal-test/blob/master/fall/main.rb

setup(
  title: "FALL",
  description: <<~EOS,
    [SWIPE] Move
    [TAP] Color
  EOS
  characters: [
    <<~EOS,
      llllll
      ll l l
      ll l l
      llllll
       l  l
       l  l
    EOS
    <<~EOS,
       pp
       pp
     rrrrrr
     rrrrrr
     rrrrrr
     rrrrrr
    EOS
    <<~EOS,
       yy
       yy
     llllll
     llllll
     llllll
     llllll
    EOS
  ],
  options: {
    isPlayingBgm: true,
    isReplayEnabled: true,
    seed: 200
  }
)
 
class Falling
  def initialize(string, x, y)
    @string = string
    @pos = vec(x, y)
  end

  def update(player_color)
    @pos.y += 1
  
    if char(@string, @pos).is_colliding.char.a
      if player_color == "red" && @string == "b" ||
        player_color == "black" && @string == "c"
        add_score(1, @pos)
        return true
      else
        end_game
      end
    end

    @pos.y > 102
  end
end

@player_color = nil
@fallings = nil

def update
  if ticks == 0
    @player_color = "red"
    @fallings = []
  end

  if input.is_just_pressed
    @player_color = @player_color == "red" ? "black" : "red"
  end

  color(@player_color)
  char("a", input.pos.x, 95)

  color("black")
  @fallings.push(Falling.new(rndi(2) == 1 ? "b" : "c", rnd(100), 0)) if ticks % 5 == 0
  @fallings.delete_if do |e|
    e.update(@player_color)
  end
end

crisp-game-libはupdateという関数の中に処理を書いてゲームが作れる。初期化処理はif ticks==0の中に書く。

crisp-game-libの特徴として描画処理と当たり判定が一体化しているところがある。対象と重なっているかは描画したときの戻り値を使って判定する(まだ描画されていない対象との当たり判定は当然取得できないので描画順に気をつける必要がある)。これは極力短く書くための仕組みで、ゲームは通常ロジックと描画が分かれて記述することが多いが確かにこれならコード量が半分になり余計なことを考えずに済むと思った。テクスチャも貼れない(ドットは配列作れば打てる)、サウンドは自動生成されたものを番号指定するだけ、効果音組み込みで数種類の中から選択、とライブラリ全体としてゲームを高速に作るために特化していて書いていると心地よい。(このゲームも書き始めてから30分位で書けたと思う) 後リソースを一切要求しないのでコードだけで配布できるのもよい。

もっとたくさんのゲームを触ってみたい方は、crisp-game-lib作者のABAさんがすでに大量の面白いゲームを作られているので是非遊んでみて欲しい。ほぼ全てのAPIがOpalに移植できていると思うのでブラウザゲームRubyで記述したい、という趣味の方がいましたら是非お使いください。

Humankindを読んでニュースの読み方を変えた

話題になっている「Humankind」を読み終わった。

読む前は人間は基本いい人だから世の中は考え方1つで変わるよ、みたいな楽観的なことが書いてあるのかと思っていたがそれは前置きで、むしろ大半の人間は本来善良なのに

  1. 民衆が愚かで悪人が多いと認識されている方が都合が良い
  2. 事実と異なるデータや一面的な思想を伝えて他の人を思い通りの方向に誘導したい
  3. 自分の地位や資産を増やせるならどんな手段を取っても構わない

といった目的を持つ少数の人間によって「冷笑的な社会だと思い込まされている」ことに警鐘を鳴らしているのだと理解した。すごく面白かった。これから少しずつ自分の中の認識も見直していきたいと思う。

ニュースを避ける

少しの間、想像してみよう。新しい薬が市場に出る。その薬は中毒性が高く、誰もがすぐ夢中になる。科学者は調査して、その薬は「リスクの誤認、不安、気分の低下、無力感、他者に対する軽蔑と敵意、感情の麻痺を引き起こす」という結論を下す。
この薬をわたしたちは使おうとするだろうか。子どもたちが摂取するのを許すだろうか。政府は認可するだろうか。答えはすべてイエスだ。なぜなら、その薬はすでに、現代の最大の依存症の一つを引き起こしているからだ。わたしたちが毎日摂取し、多額の助成金を受けていて、子どもたちに大量に配られている薬。その薬とは、ニュースである。

書籍内には沢山の興味深いトピックがあるがその中でも「ニュースを避ける」はすぐに自分の中に取り入れられそうで色々試している(元々自分のスマホのアクティビティの中でニュースサイトの閲覧率が高めで、また調子が悪いときほどニュースばかり見る傾向があると感じていた)。

平日はニュースを見ない

平日にニュースを見るのをやめて、暇なときは漫画読んだりゲームやったり好きなことをするようになった。

世代的に日々新聞を読まないと社会人としてよくない、という思想を受けてきたので何となく心理的な束縛があったがHumankindのおかげで開放された。

手持ち無沙汰になったときに無意識的にニュースサイトにアクセスしてしまうので、体操など体を動かすことも含めて、暇なときにやることをもう少し揃えておくのがよさそう。

テレビもプライムタイムに付けると大体ニュース番組がやってる確率が高いのでその場合はチャンネルを切り替えるか、録画した別の番組を見るようにしている。

日曜版の新聞を読む

本当にニュースを何も見ないとさすがに世間から切り離されてしまうので、最小回数で効率的に情報を取得したい。

新聞の土曜版や日曜版は平日よりも比較的ゆったりしており、インパクトのある見出しでセンセーショナルに読者をゆさぶる度合いも少なめでよい。また今週あった主なニュースをまとめた記事があったりするので、ここだけ見ておけば重要なニュースを取りこぼすこともなさそう。

一部180円位なのでこれなら月1000円以内で済む。毎週違う新聞を買ったりもできる。

特定のニュースを深掘りしたくなったら後でインターネットで検索する。(これは能動的な調べものなのでニュースを読むとは違うという認識)

まとめ

今はこんな感じの作戦でやっている。まだまだ調整が必要そうだが自由時間が増えたような感触があるのが嬉しい。(この記事を書く時間も捻出できた)

まずは毎日それなりにあったニュースを見ていた時間を、それ以外のことに割り当て直していこうと思う。

Rubyの開発環境を2021年ぽくする for Windows

2021年なのでこれくらいは欲しい。

  • Ruby 2.7.4
  • VSCode
  • バイナリgemを確実にビルドできる
  • コードフォーマッタ
  • Lint
  • デバッガ
  • コード補完

それぞれは独立した機能なので全部入れなくてもいいと思います。(個人的には上から順に必須度が高い)

Ruby 2.7.4

Ruby3自体は安定しているがgem周りの挙動が若干安定していなかったのでこちらを採用した。 (RubyInstallerも2.7系をまだおすすめしていた。)

https://rubyinstaller.org/downloads/

rubyinstaller-devkit-2.7.4-1-x64.exeをダウンロードしてインストール。

Rubyのインストール終了後にmsysなどもインストールしてくるか聞いてくるので基本的には全てインストール。

バイナリgemのインストール

スタートメニューに「Start Command Prompt with Ruby」が作られるのでそこからgem installする。

f:id:tuto0621:20210807102945p:plain

(ビルドにかなり色々必要な) popplerもちゃんとインストールできて感動した。

コードフォーマッタ

コードフォーマッタにはrufoを使うことにした。 rubocopと比べてかなり高速に動作するのがよい。 VSCodeで編集中は定期的にShift+Alt+Fする派なのですぐに整形できる方がありがたい。prettierと違ってnodeを必要としないのもよい。

> gem install -N rufo

f:id:tuto0621:20210807103030g:plain

※ 整形したコードは有名なprettierのあれ

Lint

Toolboxで調べるとかなり勢いのあるstandardを採用。 面倒な設定無しですぐに使えるのがありがたい。

> gem install -N standard

適当なRubyプロジェクトの下に移動して、以下のコマンドを実行すればいい感じでコードを修正してくれる。

$ cd /path/to/project
$ standardrb --fix 

手持ちのmrubyプロジェクトのコードも問題なくコード修正できた。

ifのtrue節とfalse節で同じ変数に代入する場合に式の戻り値で代入するように変更してくれてなかなかすごい。(というかRubyのifが文じゃなくて式なことも知らなかった。こういうのも勝手に教えてくれるのがLintのいいところだと思う)

f:id:tuto0621:20210807103441p:plain

設定すればVSCodeからも問題のあるコードを教えてくれる。

f:id:tuto0621:20210807145448p:plain

デバッガ

これは色々なところでおすすめされていたgemをそのまま使った。

> gem install -N debase ruby-debug-ide

f:id:tuto0621:20210807145604g:plain

デバッガをF5で起動するには.vscode/launch.jsonの設定が必要。(VSCodeGUI上で自動生成してくれるし、手で書いてもよい)

f:id:tuto0621:20210807145718p:plain

VSCodeの設定

ここまでインストールしたgemをVSCodeでも使えるようにする。

いわゆるRubyプラグインをインストールして、以下の設定を加える。

https://github.com/testdouble/standard/wiki/IDE:-vscode

  • Language Serverを有効にする
    • ブロックのfold, unfoldなどが有効になる
  • インテリセンスにrubyLocate
    • F12で定義ジャンプができるようになる
  • Linterはstandard
  • Formatterはrufo
  • Debuggerは特に設定不要

settings.jsonは以下のような感じ。

{
    "ruby.lint": {
        "standard": true,
    },
    "ruby.format": "rufo",
    "ruby.useLanguageServer": true,
    "[ruby]": {
        "editor.tabSize": 2
    },
    "ruby.languageServer": {
        "logLevel": "info"
    },
    "ruby.intellisense": "rubyLocate",
}

コード補完

ここまででも十分に便利だが、さらにコード補完にも対応するならsolargraph gemを入れるとよい。

> gem install -N solargraph 

solargraphには専用のVSCodeプラグインが用意されている。"Ruby Solargraph"をインストール。

https://github.com/castwide/vscode-solargraph

補完だけじゃなくメソッドのドキュメントがその場で見れるのも便利。

f:id:tuto0621:20210807150126p:plain

まとめ

コード整形、Lint、デバッガ、コード補完とコーディングに必要な機能が一通り使えるようになり大分便利になった。

この辺りのコーディング支援機能がgolangやRustのような後発の言語と違って標準で用意されていない(もしくは選択肢が多すぎてどれを使ってよいか分からない)ため、どのgemを入れればよいかを検証するのにそこそこ時間がかかった(探せば何かしらあるであろうことは分かっていたのだが)。

irbも最近はコード補完が強力になってかなり使いやすくなったため、例えばコード整形機能だけでも標準で用意されているとすごく印象がすごくよくなりそうだなぁと思った。

Rubyは成熟した言語なのでこの辺りの周辺機能の充実がより重要なのかもしれない(https://github.com/ruby/debug などの開発が進んでいるのはとても楽しみです)。

Rustのライフタイムについて調べている

The Rust Programming Languageを読んでいる。GC無しでどうやって参照を安全に管理しているのかを知りたかったのだが、10章ライフタイムまで来てようやくぼんやりと分かってきた。

Rustの全ての参照には寿命が設定されている。書かなくていい場合もあるがそれは暗黙で設定されている。参照は当然何らかの実体を指しており(nullは無い)、実体の寿命より参照の寿命が長い場合はエラーになる。C++でこれをやった場合はいわゆるダングリングポインタになる。

ここまでは何となく分かっていたのだが、問題は関数の引数に渡した参照を戻り値で返すような場合だ。関数コールはいくらでも深くできるので正確にどの実体を指しているか把握しようとすると難しい。

例えば fn longest(x: str&, y: str&) -> str& のような、文字列参照を2つ受け取って長い方を返す関数を考えてみる。寿命にしても文字列長にしてもx, yどちらが長いかは呼び出し箇所によって異なる。どのように参照安全を確保すればよいのだろうか?Rustではこの問題を解決するためにライフタイムを明示させる。(実際のところ前述の書き方ではRustはコンパイルエラーになる)

fn longest<'a>(x: str&'a, y: str&'a) -> str&'a

'aがライフタイムで、この場合はx, yと同じ寿命の文字列参照を返す、という意味になる。

呼び出し側はx, yどちらの参照が返ってくるか分からないので「longest()の結果の参照は引数の短い方の寿命だと仮定してその後のコードを処理する」ことになる(ここが賢いところ)。確かによく考えたらコンパイラが実際の寿命よりも仮定すればコードは安全になる。

メンバ変数の参照にもライフタイムが設定されており、自分よりも早く実体が消えるものの参照はメンバ変数に代入できない。基本的な考え方は同じということだ。

まとめると

  • 寿命が自分よりも短い実体の参照は保持できない
  • 関数呼び出しで複数の候補から参照を返すような場合は、その中で最も短いものが返って来ると仮定して静的に解析する

となる。寿命のワーストケースを仮定しながら解決していけば静的に参照安全を保証できるのか。

Windowsの親指シフト入力用のソフトをDvorakJからやまぶきRに変更した

ずっとDvorakJを愛用していたのだが、勝間さんがやまぶきRを使っているという情報を聞いてやまぶきRを試してみることにした。

ここからダウンロード。インストールすればすぐに親指シフトが使えるようになる。

設定はほとんどデフォルトのままだが左親指シフトキーは「無変換」に変更する。(Thinkpadの日本語キーボードはスペースキーが小さいので無変換でもいける)

インストール後の話、やまぶきRだと入力のレスポンスが向上した気がする。https://kamosawa.hatenablog.com/entry/2019/12/21/232303 辺りが理由らしい。

さらに、DvorakJを有効にしているとコマンドプロンプトでの入力がもたつくという不具合があったのだが(英語入力のときも発生する、左シフト+右シフトで一時的にOFFにしてしのいでいた)、やまぶきRだとこれが発生しない!これが私的にはかなり嬉しい。

残念な点としてはエクスプローラーのクイックアクセスへの入力がなぜかローマ字入力のままになるようだ。雰囲気的にこれは他のアプリでも発生するかもしれない。

さておきメリットが大きいので一旦やまぶきRを使っていくことにする。

最後に、何年も自分の入力環境になくてはならない存在だったDvorakJには最大級の感謝を送りたい。

最近作っているClipScript

https://github.com/ongaeshi/clipscript

短い動画を簡単に作成するためのスクリプト言語です。

f:id:tuto0621:20210618235722g:plain

require 'clip'

App.window_size(400, 225)

font_s = Font.new(40)
font = Font.new(50)
smile = Texture.new(Emoji.new("😀"))

script do |root|
  Drawer.background "white"

  (0..10).each do |x|
    (0..10).each do |y|
      if (x + y) % 2 == 0
        root.rect(x * 40, y * 40, 40, 40, color: "gray")
        root.wait 0.02
      end
    end
  end
end  

script do |root|
  t = root.text(font, 200, 100, color: "black", text: "Hello, World!", length: 0, center: true)
  root.wait 0.2

  1.upto(t.text.length) do
    t.length += 1
    root.wait 0.1
  end

  root.until_time 3

  x = root.texture(smile, 180, 150)
  x.scale(0.4, 0.4)
end

App.run

タイムラインUI

記述したスクリプトは好きな場所から再生したり逆再生することができます。

f:id:tuto0621:20210619000458g:plain

gifアニメを再利用した動画の作成

gifアニメの再生機能に力を入れており、動画ファイルの編集無しで以下のようなことができます。

  • テロップの表示
  • 再生レートの変更
  • サイズの調整

f:id:tuto0621:20210619004049g:plain

これは直前に紹介したgifファイルを再利用して作成した動画です。追加で書いたのはこのスクリプトのみです。

インストール

最新版をGitHub Actionでビルドしたものがあるので興味がある人は触ってみてください。

  1. https://github.com/ongaeshi/ClipScript/actions から(成功した)最新のActionを開いてbuild-resultをダウンロード
  2. .rbファイルを.exeにドラッグ&ドロップすると動きます

使用ライブラリ

  • OpenSiv3D
  • murby

OpenSiv3dはgifアニメの再生が簡単なのがよいです。(次の0.6ではmp4の再生もできるようになるそうなのて大変楽しみです。)

mrubyは自作のongaeshi/mruby-packerを使ってソースコード丸ごとレポジトリに入れてあります。(ので、ソースコードを対応するプラットフォームのOpenSiv3DにコピーすればMacLinuxでも動くはず)

自分のホームページをリニューアルした

dev.toのBuilding a Kickass Portfolio - DEV Communityに触発されて、 しばらく消えていた https://ongaeshi.me を復活させた。

f:id:tuto0621:20210216224945p:plain

TwitterやBlogへのツールバーを作ったのでホームページ経由ですぐにアクセスできるのが便利。 自分がここ最近何をやっているのかも大分把握できた。 ドキュメントページをちゃんと整備するのはやはり重要。

HISTORYを眺めていてふと思ったのは、 FireLinkみたいに(Firefoxのバージョンがあがったことで)すでに使えなくなってしまったソフトウェアでも、 ちゃんとドキュメントのページを残しておくとそれがどんなツールで何ができるのかは他の人が見てもなんとなく分かる。 ドキュメントの寿命はソフトウェア本体よりも長いことが大半なのかもしれない。