SVX日記
2022-03-19(Sat) テトランを建造する
なんだか「思考力が余っているような感じがある」こともあるので、プログラミングでもしようかと思い立った。で、なんとなく「テトラン」でも作ってみようかと。ディアゴスティーニみたいに「週刊テトランをつくる」なんて連載記事もいいかな。
さて、どう作ろうか。三角関数をゴリゴリに使う必要があるだろうな……と、考え始めたのだが、エラいことを思いついてしまった。タートルグラフィックスだ。自製のタートルグラフィックスライブラリがあるのだ。それを使う。
require './TrueLegacyGraphicsCairo'
win = LegacyGraphics.new(nil, nil, nil, nil, 720, 960, 16, 8, 0, { :file => 'tetran', :type => 'png' }) # png/pdf/svg/ps
win.tPenUp
win.tSetPenColor([0.6, 0.6, 0.6])
win.tSetPenWidth(16)
4.times {|n|
win.tHome
win.tRight(n * 90)
9.times {|n|
win.tPenDown unless(n < 2)
win.tForward(32)
win.tPenUp
win.tForward(6)
win.tRight(4)
}
}
ox = win.gx / 2; oy = win.gy / 2
win.fill(ox - 96, oy - 96, ox + 95, oy + 95, [0.6, 0.6, 0.6])
win.refresh
win.close
$ diff turtleTetran0.rb turtleTetran1.rb
25c25
< win.fill(ox - 96, oy - 96, ox + 95, oy + 95, [0.6, 0.6, 0.6])
---
> win.put_image_png(ox - 96, oy - 96, 'tetran0.png')
$ diff turtleTetran1.rb turtleTetran2.rb
7c7,10
< win = LegacyGraphics.new(nil, nil, nil, nil, 720, 960, 16, 8, 0, { :file => 'tetran', :type => 'png' }) # png/pdf/svg/ps
---
> # convert -delay 3 -loop 0 tetran.d/tetran*.png tetran.gif
> Dir.mkdir(@path = 'tetran.d') rescue true
> 30.times {|f|
> win = LegacyGraphics.new(nil, nil, nil, nil, 720, 960, 16, 8, 0, { :file => @path + '/tetran%03d' % f, :type => 'png' }) # png/pdf/svg/ps
13a17
> win.tRight(f * 3)
28a33
> }
$ diff turtleTetran0.rb turtleTetran3.rb
5c5
< require './TrueLegacyGraphicsCairo'
---
> require './TrueLegacyGraphicsOpenGL'
8a9
> win.depth3d(0, 16)
23a25
> win.depth3d(-8, 32)
立体環境において「画像を描画する」ということは、つまりテクスチャマッピングを行う、ということだ。以前、やりかけたが勝手がわからず断念したコードが残っていた。で、がんばって試行錯誤しつつ、どうにか実装できた。それなりのコード追加が必要であった。
$ diff turtleTetran3.rb turtleTetran4.rb
25c25
< win.depth3d(-8, 32)
---
> win.depth3d(32)
27c27
< win.fill(ox - 96, oy - 96, ox + 95, oy + 95, [0.6, 0.6, 0.6])
---
> win.put_image_png(ox - 96, oy - 96, 'tetran0.png')
再び、それっぽくなったところで、回してみたいところだが、自製のOpenGLライブラリでアニメーションさせるためには、足をスプライトの扱いにする必要がある。先のタートルグラフィックスのコードは、スプライト機能を実装する前に実装した機能なので、スプライトとして扱えない。そこで、改めてスプライトとして扱えるよう、タートルグラフィックスの処理をModule化して、スプライトクラスからもincludeするように変更した。いや、Rubyってスゴいな。で、本体コードにもちょっとコードを足す。
$ diff turtleTetran4.rb turtleTetran5.rb
6a7,8
> # ffmpeg -y -i tetran.d/3dtetran%06d.png -vcodec libx264 -r 30 -s 480x360 tetran.mp4
> Dir.mkdir(@path = 'tetran.d') rescue true
8a11
> def draw_sprites(win, f)
9a13
> win = win.init_sprites
14a19
> win.tRight(f * 3)
23a29,44
> end
> draw_sprites(win, 0)
>
> def callback(win, info, params)
> case(info[0])
> when(:VSYNC)
> while(it = win.inputs[:EDGE].shift)
> it == 'r' and @ar = win.arotate3d(@ar ? 0 : 1)
> it == 'q' and exit
> end
> win.write_to_file(@path + '/3dtetran%06d.png' % ($jiffies - 1))
> $jiffies > 359 and exit
> end
> draw_sprites(win, $jiffies)
> end
> win.setcallback3d(method(:callback).to_proc, {})
29c50,51
< win.refresh
---
> win.arotate3d(@ar = 1)
> win.refresh(30)
そして、テトランといえば外せないのが、グラディウスIIで見せた「ひっぱたき」である。たぶんそうだろうなぁ、と思いつつ実装してみたら、やっぱりそうだった。長らく「もぞもぞ」しているのも勝手に再現された。実は、実装はものすごく簡単なのだ。たった1行である。
$ diff turtleTetran5.rb turtleTetran6.rb
26c26
< win.tRight(4)
---
> win.tRight($jiffies * 2)
$ diff turtleTetran6.rb turtleTetran7.rb
10a11
> @hands = 4; @swing = 1440
17c18
< 4.times {|n|
---
> @hands.times {|n|
20c21
< win.tRight(n * 90)
---
> win.tRight(n * 360 / @hands)
26c27
< win.tRight($jiffies * 2)
---
> win.tRight(4 + @swing)
35a37,38
> it == 'h' and @hands += 1
> it == 's' and @swing = 0
40c43
< $jiffies > 359 and exit
---
> @swing < 1440 and @swing += 2
当初は「週刊テトランをつくる」なんて連載記事もいいかな、などと思っていたが、意外と時間がかかってしまった。しかし、最小限のコードで、複雑な動作を再現するのは楽しい。動けばいい、というコードを書く人もいるが、自分はコードを煮詰めていくこと自体が楽しいんだよなぁ。コード一式を置いておく。