SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|

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
> }

  画像の説明

  回ったところで、ライブラリをTrueLegacyGraphicsCairoからTrueLegacyGraphicsOpenGLに置き換え、立体化してみる。

$ diff turtleTetran0.rb turtleTetran3.rb 
5c5
< require './TrueLegacyGraphicsCairo'
---
> require './TrueLegacyGraphicsOpenGL'
8a9
> win.depth3d(0, 16)
23a25
> win.depth3d(-8, 32)

  画像の説明

  立体化したはいいが、OpenGL版にはput_image_png関数が未実装なので、本体がコンクリート製に戻ってしまった。

  立体環境において「画像を描画する」ということは、つまりテクスチャマッピングを行う、ということだ。以前、やりかけたが勝手がわからず断念したコードが残っていた。で、がんばって試行錯誤しつつ、どうにか実装できた。それなりのコード追加が必要であった。

  本体のコードの変更はほぼ0→1と同じ。で、こうなる。

$ 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

 

  当初は「週刊テトランをつくる」なんて連載記事もいいかな、などと思っていたが、意外と時間がかかってしまった。しかし、最小限のコードで、複雑な動作を再現するのは楽しい。動けばいい、というコードを書く人もいるが、自分はコードを煮詰めていくこと自体が楽しいんだよなぁ。コード一式を置いておく。

  というわけで、今回はそこそこの結果が得られたが、テトランの足が単なる箱だったり、テクスチャが透過してなかったりという課題があるので、そのうちどうにかしたい。

  ちなみに、沙羅曼蛇のテトランは高次周回になると、こんな感じになる。

  画像の説明 画像の説明

  ……天然なのにネタ状態なんですがw。「ひっぱたき」の再現方法は容易に想像がついたが、逆にこっちの動きは、どういうコードを書いたら再現できるのか、想像がつかんわw。