mieki256's diary



2014/01/01(水) [n年前の日記]

#1 [nitijyou] あけおめことよろ

あけましておめでとうございます。本年もどうぞよろしくお願い申し上げます。

#2 [cg_tools] Mitsuba 0.4.5を試用

Mitsuba というレンダラーの新版 0.4.5 が公開されてたようなので試しに使ってみようかと。

_2013/11/03の日記 を頼りに作業をしていったのだけど、mtsgui.exe を実行しても、「MSVCR120.dll がねえよ」と文句を言われて起動できず。… Mitsuba の解凍フォルダの中に、vcredist_2013_x64.exe という、それらしいファイルがあることに気がついた。実行してインストールしてみたら、mtsgui.exe が起動できるようになった。

_マイクロソフト公式ダウンロード センター - Visual Studio 2013 の Visual C++ 再頒布可能パッケージ からも入手できる模様。こっちはインストーラが日本語表示だった。

Mitsubaを呼び出すための blender のアドオンは、前回から更新されてないっぽい。

Mitsuba でガラスっぽいマテリアルを割り当てる方法が分からず…。

#3 [nitijyou][cg_tools] 年賀状を作成

福島の三春駒を画像検索して参考にしながら、blender でモデリング。Cycles と LuxRender でレンダリングしてみて、ヨサゲなほうを GIMP で色調補正・コントラスト変更・フォント描画。筆まめに読み込んでレイアウト。

3DCGツールについて。 :

例年、Shade で年賀状用画像を作っていたのだけど。今年は珍しく blender を使用してみたり。三春駒が、どう見ても、「ボクをポリゴンでモデリングしてよ!」てな形状だったので…。最近の Shade も、ポリゴンでモデリングができるようにはなってるはずだけど、未だに操作方法が全然分からなくて。

年賀状ソフトについて。 :

筆まめを触ってて気になったけど、これってフォントの縁取り機能とかどこかにあるのかな。よく分からないから、GIMP で作業してしまったけど。…む。「文字飾り」ってのがソレなのかな。しかし試してみたら、なんだか輪郭がガクガクしてるような。図形描画もそうだけど、筆まめは全般的にアンチエイリアスをかけないまま描画してるらしい。もっとも、プリントアウト時はそのほうが都合が良い、等の可能性もありそうな気もする。

図形描画時に、影のオフセット位置を変更できないとか、影を半透明にできないとか、そのあたりも少し気になる。半透明の影を作る時は、同じ形状で色だけ変えた図形を作って、右クリックで図形全体の透明度を変更、とかになるのだろうか。面倒臭いな…。でも、こういう仕様のほうが、一般の方にはまだ分かりやすいのだろうか。

でもまあ、こういう機会でもないと扱わないソフトなので、今回多少は勉強できたかなと。

#4 [nitijyou][pc] 弟が帰省

夕方4:30頃に到着。

BDを再生しようとして四苦八苦。 :

「パシフィック・リム」の3D版BDを持ってきたそうで、自分の3D対応ディスプレイ、MITSUBISHI RDT233WX-3D(BK) で見るとどんな感じに見えるのか、てな実験を。

ところがそもそも、Cyberlink PowerDVD 11 Ultra で、件のBDが再生できず。何度試しても、「ストレージデバイスが見つからない」だの、リージョンコード選択ダイアログの次の画面で「dllがエラー」と文句を言ってくる。

どうやら、以下の症状と同じらしい。

_カスタマーサポート - PowerDVD 11 Ultra/Proでブルーレイ再生ができない (ストレージデバイス、エラーコード 101、エラー 800401F9) | CyberLink

_PowerDVD をアップデート | CyberLink から、PowerDVD 11 Ultra 用のビルド4423パッチをDL、インストールしてOS再起動をしたところ、見れるようになった。

ちなみに、件のページからDLできるのは、CyberLink_PowerDVD_Downloader.exe という、ダウンローダプログラムだけで、実際のパッチ、PowerDVD_v4423_r85663_Ultra_DVD130410-05.exe は、ダウンローダプログラムがダウンロードしてくれる。ただ、140MByte程の巨大なサイズなので、パッチと言うよりも、製品本体を丸々アップデートしてるっぽい。更新時に前のバージョンを削除、てなメッセージも表示されてたし。

さておき、「パシフィック・リム」3D版の見た目だけど。RDT233WX-3D(BK)は偏光方式なので、縦の解像度は半分になっているはずだけど、そのあたりは意外と気にならなかった。もちろん、フレーム切替(?)式や、SONY HMZ-T1 等の3D対応HMDなら、もっと迫力があるのだろうなと。

2014/01/02(木) [n年前の日記]

#1 [movie] 「パシフィック・リム」を視聴

弟に薦められて(?)、視聴してみたり。

素晴らしい。まるで夢でも見ているかのような映像・展開だなと。巷の評判にも納得。これは素晴らしい…。大絶賛されるわけだなと。

誰かが、「これはポルノムービーだ」と称してたけど、たしかにそれも一理あるなと。男の子の夢を存分に突っ込んでしまった映画のように思えたり。巨大ロボットと怪獣のプロレスバトル全開、だもの…。

2014/01/03(金) [n年前の日記]

#1 [ruby] Rubyで画像をダウンロード

数日前から、「Rubyスクリプトを実行するだけでドット絵が表示されたら楽だろうなー」と思って、アレコレ考えてたのだけど。ふと、「手元に画像ファイルが存在しなかったら、その時だけ、Webサーバから持ってくればいいんじゃないか?」と閃いて。

検索してみたら、 _ネット上の画像をRubyでダウンロードする方法 - ダークな糸 で解説されてる内容がそのものズバリな感じで。参考にしつつ、自分も実験。

_http_access_test1.rb
# 画像をネットから読み込むテストその1

require 'open-uri'
require 'dxruby'

uri = "http://www.geocities.jp/mieki256jp/resource/_dxruby_image_ufo.png"
fn = File.basename(uri)
unless File.exist?(fn)
  # カレントフォルダ内に画像が無いのでダウンロード
  puts "not found #{fn}"
  puts "download #{uri}"
  src = open(uri)
  dst = open(fn, "wb")
  dst.write(src.read())
  dst.close()
  src.close()
else
  puts "found #{fn}"
end

img = Image.load(fn)

Window.loop do
  break if Input.keyPush?(K_ESCAPE)
  Window.draw(0, 0, img)
end

_http_access_test2.rb
# 画像をネットから読み込むテストその2

require 'net/http'
require 'uri'
require 'dxruby'

uri = URI("http://www.geocities.jp/mieki256jp/resource/_dxruby_image_sprite1.png")
fn = File.basename(uri.path)
unless File.exist?(fn)
  # カレントフォルダ内に画像が無ければダウンロード
  puts "not found #{fn}"
  puts "download #{uri.host} #{uri.path}"
  http = Net::HTTP.new(uri.host)
  response = http.get(uri.path)
  image = response.body
  file = open(fn, "wb")
  file.write(image)
  file.close()
else
  puts "found #{fn}"
end

img = Image.load(fn)

Window.loop do
  break if Input.keyPush?(K_ESCAPE)
  Window.draw(0, 0, img)
end

一応、Geocities 内の自分のアカウントにアップロードした画像を、DLかつ表示することはできるようになった。

ただ、自分が最初にやってみたいと思った処理は、Dropbox に置いてある画像を取得する処理、だったわけで…。Geocities に一々アップロードするのは、ちと面倒臭いけど。Dropbox ならローカルで該当フォルダにコピーするだけだし。

Dropbox と言っても、共有リンクを取得する形、つまりは、一般に公開されてるURLから取得するソレなのだけど。試してみたら、なんだかエラーが。Dropbox の共有リンクって、http ではなくて https から始まるURLなので、おそらくソレが原因なのかなと。しかし、Ruby で https アクセスをしようとすると、証明書がどうのこうのという話になってくるようで。

_library net/https
_HTTPSでサーバーに接続 - うなの日記
_RubyにおけるHTTPS通信 - 狼ニコ生うらやまけしからん日記

どう考えても、「Rubyスクリプトを動かすだけで画像がサクッと表示される」という状況から、かけ離れてしまうよなと…。

もしかすると、 _OpenGameArt.org あたりに画像を登録して、そこに httpアクセスしたほうがいいのかな、という気もしてきたり。

2014/01/15追記。 :

色々実験してるうちに、上記URLの画像を削除してしまったらしい…。どの画像を使おうとしてたのか忘れた…。

何にせよ、こういう手もありそうだと分かった、ということで。

2014/01/04() [n年前の日記]

#1 [cg_tools] ドット絵云々

簡単なシューティングゲームサンプルでも作ろうかなと思い立ち、雑魚敵のドット絵を描き始めたものの、アニメをさせるのが面倒で。雑魚敵の3DCGモデルを作ってレンダリング。

しかし、雑魚敵はともかく、自機のドット絵を描こうとしたところで悩んでしまったり。2〜3回描き直してみたけど、どうも何かが違う…。

既存タイトルのソレを参考にしようと、TF4あたりをエミュで起動して画面をキャプチャ。MD本体もROMカセットも持ってるから、使ってもいいよね…。さておき、画面を眺めているうちに、ますます悩んでしまったり。デザインの方向性も、ドットの打ち方も、よく分からん…。3DCGツールでモデル作ってレンダリングしたほうがいいのかな。上下移動時に少し斜めになったりもするだろうし。

それはともかくTF4ってステージセレクトの隠しコマンド無いんか…。拷問だ…。

#2 [pc] USBフットスイッチを発掘

画面キャプチャする際、一々キーボードを押すのがツラかったので、USB接続のフットスイッチを発掘して使ってみたり。 _サイズ USBダブルフットスイッチ USB 2FOOT SWITCH だと思うけど。スイッチに、キャプチャのホットキーを割り当てて、両手はゲームコントローラを操作しながら、ここぞという時に足で「カチッ」「カチッ」と。

「カチカチ」がうるさくて夜中には使えないし、チャタリングがヒドイのもアレだけど、圧倒的に作業が楽になった。両手がふさがってる状態で、何かしらのタイミングを取りたい場合には、最高にナイスな入力機器。ここまで快適になるとは思わなかった。買っといて良かった…。

自分の買ったソレは初期の製品なので「カチカチ」がうるさかったけど。 _最後の頃に出た製品は静音仕様らしい ですな。ただ、この手の商品は販売終了しちゃってるようだけど、Amazon で、まだ多少は売ってる模様。在庫が無くなったら困る人が出てきたりしそうだけど、 *1 その時は、昔のように、工作・自作する時代に逆戻り、なのかな…。

ていうか今回は、ジョイパッドの空いてるボタンに、キャプチャのソレを割り当てられたら良かったのだけど。
*1: テープ起こしをする人には、便利らしいですな。足で再生・一時停止をして、両手はキーボードを打つ、みたいな。

2014/01/05() [n年前の日記]

#1 [anime] ログホライズン、魂魄とやらの解説回を視聴

おそらくは世界設定の説明回だと思うのだけど。単語が ―― 「山吹色の波紋疾走」と書いて「サンライトイエローオーバードライブ」と呼ぶ、みたいな独自用語が次々にメッセージウインドウに表示されていく、そんな感じの説明シーンに、「なるほど、これは分かりやすい」と感心したり。ゲーム世界だから、ウインドウ表示も不自然ではない、ということかなと。

昔、NHKで、「十二国記」が放送されてた頃、この手の独自用語の説明が、色々工夫を盛り込みながらもちょっと上手くできてない感じでアレだったのだけど。この、「ログホライズン」に関しては、上手な説明シーンが作れてる、そんな気がしたり。小説や漫画ならテキスト情報をいくらでも提示できるけど、アニメでは、そういうわけにもいかないし…。個人的に、そのあたりをどう処理するのか、気にしつつ観賞させていただいております。

#2 [anime] 蒼き鋼のアルペジオ最終回を視聴

面白かった…。潜水艦モノって個人的には全く興味がないのだけど。薄い自分でも楽しめました。素晴らしい。

3DCG云々。 :

キャラクターまで全て3DCGで描いてるのに、見ていて可愛らしいと思えるあたり、やっぱり凄いなと。さすが、サンジゲン…。作品中で、「風穴を開けられたか」云々という台詞があったけど。もしかするとこの作品自体が、業界に対して風穴を開けた云々の位置づけになるかもしれないなと。おそらく将来的には、こういう見た目のTVアニメが増えるんじゃないかと思えるし、潜水艦モノでもTVアニメ作れるんやで、みたいな意味でも…。いや、昔も、ブルーノアとかナディアとかあったから、ソレはちょっとアレかしら。何にせよ、3DCG使ってキャラクターを描いても、シリアス、かつ、萌え萌えなTVアニメをガッチリ作れるんやで、てな見事な前例になっていた気がしたり。 *1

艦と美少女。 :

昔、幾原邦彦監督が書いた小説の中で、巨大宇宙船が少女、という設定があって。設定の奇抜さに感心しながらも、映像化は難しいだろうなと思った記憶があるのですけど。

でも、「蒼き鋼の〜」のような見せ方をすれば、映像化も全然OKなんだなと今頃気付かされたり。メンタルモデルという設定は、色々便利だなと。

「うぽって!!」も似たような見せ方をしていたような気もするのだけど、あっちは、兵器なのに兵器らしさをかなり隠してたあたりが、個人的にとても気持ち悪くて。その点こっちは、破壊兵器としてガッチリ描いてたので、見ていて納得。更に、兵器が 人間に近づいていく様子を描く点も、なんとなくSFっぽいなと。

今気がついたけど、「無限のリヴァイアス」も、先輩のような気がしてきたり。

ミサイルは○○○。 :

ミサイルは 乗り物であり 足場でもあり。あのシチュエーションを目にしただけでテンション上がりました。

自分が初めて見たのは「プロジェクトA子」だった気がするのだけど。最初にアレをやったのは、何の作品なんだろう…。

ファミコンゲームでも、そういうシチュエーションがいくつかあったみたいだけど。マリオやギミック(ゆめたろー)が、 砲弾に乗ってた気がする。

*1: ダンスシーンだけとか、EDだけとか、ファミリー向けアニメとか、そういうアレコレなら3DCGで制作を、てな前例はあったわけだけど。1クール丸々全部、いかにもそっち系のジャンルなのに3DCGで、てな事例は珍しいんじゃないかと思うのだけど、どうなんだろう。

2014/01/06(月) [n年前の日記]

#1 [anime] ドキドキプリキュア、衝撃の真実の回

今回のプリキュア、考えてるなあ…。王女様の、 「自己中と愛は表裏一体」の台詞を聞いて、感心しました。たしかに、そういう面があることは否定できない。このプリキュア、ところどころで、なんだか深い…。

前にも書いたけど、巨大化怪人が出現するあたりの流れも、自分は感心していて。誰だってちょっぴりいけないことを考えちゃう瞬間があるのだけれど、えてして、皆、思い止まって、平和な日々を暮してる。だけど、そこでブレーキが利かなかったら、誰でもモンスターになってしまうんだよ…てな展開で怪人が発生するわけで。そこらへんも上手いよなあ、と思ってるわけですけど。

まあ、そういうアレコレって、幼女達には理解できない設定かもしれないのだけど。もし、大人になってから改めて目にしたら、そこで描かれてた内容の真摯さに、ちょっとビックリしてもらえそうな予感も。

さておき。敵の美少女が全てを知って 洗脳が溶けたからこそ敵ボス側につくという展開も上手いなと。 たしかに愛されていたことが分かったのだからますます裏切れないというのは、納得が行くというか…。

#2 [zatta][neta] バスケ漫才とかサッカー漫才とか無いのかな

スポーツ選手が外国のプロチームに云々というニュースを見かけて、「スポーツ選手はプロとしての職場数が限られてるあたり大変だな…」と思ったのだけど。

「他にその能力を活かせそうな仕事って無いのかな? …そうだ。漫才はどうだろう」とバカなことを思ったり。バスケ漫才とか、サッカー漫才とか…。ノコギリをポワンポワンと鳴らすことを芸にしてる人だって居るわけだから、バスケやサッカーを使って、見た瞬間「おおー」と思わせて、とかできんかなと。まあ、イメージがさっぱり湧かないですけど。

ググってみたら、バスケ漫才は見つからなかったけど、サッカー漫才はあった。ただ、自分のバカ妄想と、方向性は全然違う。もしかして、ブルーオーシャンなのでは…?

でも、フツー、バスケやサッカーしてる人は、試合で勝つとか、レギュラーに選ばれるとか、そういう方向で頑張るはずで。そういう方向で頑張ろうとしてる人に、「これで漫才できないか」などと投げかけてみても、やるわけないよな…。ていうかたぶんそういうのって、おそらく雑技団でカバーしてる気がしてきた。

2014/01/07(火) [n年前の日記]

#1 [movie] 「のぼうの城」を視聴

なんだかどこかでタイトルを聞いたような気がしたので、HDDレコーダで録画しておいたのだけど。樋口監督が参加してたのだな…。まあ、犬童一心監督と樋口監督の共同監督、らしいけど。

結構面白かった。と言っても、どこらへんのカットが特撮使ってるのか想像しながら眺める、みたいな感じのソレなので、映画として面白いのかどうかは自分はよく分からんのですけど。

水攻めシーンがリアル過ぎて震災直後に公開するのはちょっと、てな流れで公開延期になっていたと知り。何か釈然としないものを感じたりもして。パンダコパンダの雨降りサーカスや未来少年コナンも、洪水シーンや津波シーンがあるから、今後は放送禁止になるのだろうか。宇宙戦艦ヤマトもメカンダーロボもジャイアントロボも、原子力だの放射能だの出てきてフキンシンだから、放送禁止になるのだろうか。…ワシはそういうのは好かん。

2014/01/08(水) [n年前の日記]

#1 [pc][windows] Google Chrome の文字がボケボケで首を捻ったり

なんでだろと思ったら、NVIDIAコントロールパネル上で、ステレオスコピック3D(立体視表示機能)を有効にしてたから、だった。こんなところにも影響が出てくるのか…。

まあ、自分の環境は 9800GTGE なので。あまりに古いビデオカードだから、こういう不具合が起きるのかもしれず。現行製品以外は、ビデオドライバの動作チェックもしてないんじゃないかな…。

#2 [web] リオンドールのサイトの地図が開けない

_リオン・ドール 須賀川東店 の「周辺地図」リンクをクリックしても、地図が出てこない。URLからして、Googleマップを呼び出してそう。何か仕様が変わったのかな?

ソースを眺めたら、タイトルに「Google Maps JavaScript API Example」と書いてあった。該当文字列でググったら、あちこちのサイトが同じ記述を使ってる模様。このソースは、どこで公開されてるんだろう…。

ただ、他のサイトは、ちゃんとGoogleマップが表示されてるのに、リオンドールのサイトの地図だけが開けない。何が違うのやら。

_Google Maps API v2が2013年11月19日に廃止になる話 - Pastalablog in はてな という記事を目にしたけど、もしかするとリオンドールのサイトのソレは、そういう理由で表示されないのだろうか…?

さておき。ググってたら、 _Google Maps API有料化の詳細発表、該当ユーザーは2012年初めに強制課金開始 -INTERNET Watch という記事に到達。Googleマップを使う場合はお金がかかるようになったと今頃知りました。アクセス数が少ない場合は無料でも使えるらしいから、個人で使う分には大丈夫そうだけど。

#3 [nitijyou] 自転車で買い物に

親父さんの電動自転車を借りて以下略。リオンドール、ダイソー、ホーマック、Seria、サンドラッグ、ヨークベニマルを回って夜食その他を購入。

Seria でメモ用紙を購入。中身は上質紙と書いてあったので、ちょっと期待。この手のメモ用紙って、リサイクル紙を使ったモノしか見かけないわけで。スキャナで取り込もうとしたときに紙に混じってるゴミも入るからちと都合が悪かったわけで。でもまあ、上質紙を買ってきて、カッター等使って自分で細かく切れば済む話ではあるのですけど。

#4 [windows] HPBの操作について質問メールが届いた

HPB(ホームページビルダー)の操作に関して、某氏から質問メールが。ウチの自宅サーバ用のサイト設定を作り直したら転送が上手く行かないとの話で。サイト転送をしようとすると、HPBがメモリ不足と文句を言って止まってしまうのだとか。

おそらく、膨大なファイル数を処理しようとしていてトラブルが起きてるのだろうと想像したのだけど。調べた範囲では、どうもシステムリソースが足りなくなった時に出てくるメッセージらしい。単純にメインメモリ容量が少なくて、というわけではナサゲ。どういう作りになってるんだ…HPB…。

HPBにはFTPツール(ファイル転送ツール)が同梱されてるので、ソレを使って手動で丸々、サイト内のファイルをアップロードしてほしい、と伝えたのだけど。「転送してもページが見れない」と言われて確認してみたら、public_htmlディレクトリと同階層に転送されていて。

一般的なFTPサーバは、各ユーザの転送先フォルダを public_html/ に設定済みで、故に転送先フォルダを空欄にして接続しても問題無いのだろうけど。ウチもそういう設定にしておくべき、なのだろうか。しかし、public_html/ と同階層に転送したいときもあるんだよなあ…。ユーザ毎に設定変えられないかな…。

調べてみたら、proftpd の場合、group単位ならログイン時のディレクトリを変えられると今頃知った。失敗した。各アカウントをgroupで分けとけばよかった…。

#5 [web][neta] 写真公開用のWebサービスって無いのかな

ウチの親父さんは、写真を公開する際に、HPBで静的htmlを作ってサイトにアップロード、みたいなことをしてるわけだけど。

今時なら、blogみたいな感じで、写真をアップロードして題名つければ公開できる、みたいなサービスがどこかにあるんじゃないのかなと。わざわざ自分でサイト作らないといかんのかなあ、もっと簡単な何かがあるんじゃないのか、と、ずっともやもやしていて。

とりあえず、親父さんのサイトも、親父さんの友人も、2GB近いファイル容量を公開してる状態なので、そのぐらいは公開できるWebサービスじゃないと代替できないのですが。

日本のデジカメメーカが、ちょっと似たようなサービスを提供してたりするけれど。 等々、要件を満たしてなくて。…まあ、デフォルトは未公開設定にしといたほうがトラブルは起きにくいので、事なかれ主義の日本企業にとっては大変妥当な仕様なのでしょうなと思っていますけど。所詮、デジカメというハードウェアのオマケ感覚でやってるところもあるのだろうし。家族や仲間内に見せられればそれでOKという場面も多いだろうから、それはそれでアリとも思うし。

_Flickr は…。英語オンリーだから、お爺さんお婆さんにはオススメできないし。 _livedoor PICS は…サービス終了しちゃったし。そもそも基本的に、それらサービスってSNS寄りなので、なんか違うのだけど。

_フォト蔵 はどうなんだろう。容量面ではヨサゲだけど。

もっとも、そもそも親父さんあたりは、「自分のサイト・アルバムという感じがしない」と文句を言いそうだな…。パッと見で「俺の部屋」「俺の庭」感がビンビンに出ないサービスだと、満足してもらえない予感。

これは想像だけど、「俺を、他のユーザといっしょくたにするな」=「俺をぞんざいに扱うな」的感情が、それらWebサービスに向けられてるのであろう気もしたり。「ユーザ達」でまとめて扱うなと。「お客様一人一人」として扱ってるように建前だけでも見せかけろと。そういう話だと思うのだけど。まあ、このあたり、具体的には、パッと見で違うページに見えるようにカスタマイズ機能を設ける・充実させる、てな話になるんだろうけど。はてなダイアリーやTumblrみたいな感じ? わからんけど。

でも、パッと見で別サイトに見えるようでは、使い勝手の面でも、広告表示その他の面でも、困っちゃう状態になるし…。バランスを取るのが難しそう。

2014/01/09(木) [n年前の日記]

#1 [cg_tools] DOGA L1を試用

STGの自機画像が作れなくて悩んでいるのだけど。ふと、「DOGA L1を使ったら、デザイン検討ぐらいはできないかな」と思いついて。

_DOGA L1 は、3DCGアニメーションの啓蒙用に作られた、無料で使える3DCG入門ソフト。500種類ほど用意された3Dのパーツを、積み木のように組み合わせて、戦闘機や宇宙戦艦を作り、それをビュンビュン動かしてアニメを作れる。

啓蒙用を目的として作ったソフトなので、商用利用はできない・営利目的では使えないのだけど。 *1 ラフデザインをぼんやり考える、ぐらいなら使えるんじゃないのかなと…。

てなわけで少し触ってみたのだけど。入門ソフトと称してるだけあって、圧倒的に簡単で、感心してしまったり。チュートリアルを見ながら操作していけば、小学生でもサクサク使えるんじゃないのかなと。少なくとも、MMD(MikuMikuDance)よりは簡単。…もちろん、やれることをかなり制限してるから、簡単になってるわけだけど。今まで3DCGアニメなんて作ったことが無い人でも、「ボクだって3DCGアニメが作れるぞ!」と思わせる、という目的であればバッチリOKな印象。

この、膨大なパーツ数を用意して、それぞれをくっつけて何かを作る、というアプローチは、今でもかなり有効じゃないのかと思えたり。 いや。考えてみたら、 _コミPo! もそうだったか…。もしかすると、ガンダムのゲーム等でも、そういうのがありそうな…。blender や Metasequoia で、こういうことができたら一気にハードルが下がりそうだけど、UIが問題かしら。形状追加のメニューに500種類も項目が並んでたら…うむ…それはヤバイ…。
*1: どうしても商用利用したい場合は、 _20,000円ほど払ってDOGA L3の商用利用ライセンスを購入 しないといけない。

#2 [cg_tools] Effekseerを試用

_Effekseer は、ゲーム向けのエフェクトをGUIで作れるツール。MIT License で公開されてる。自作ゲームに組み込んで再生したり、Unity と組み合わせて使えるらしい。ヘルプ上では、 _BISHAMON にインスパイアされて作られたソフト、と書いてあった。

ちなみに、 _すべての講演資料 - 全日本学生ゲーム開発者連合(全ゲ連)資料置き場_エフェクト作成ツールまとめ : ぴぽや 経由で、存在を知りました。

少し触ってみたけれど。カッコよくて綺麗なエフェクトが、パラメータを調整するだけですぐに作れて、かなりイイ感じ。親子関係を活用しながら動かすと、複雑な動きも作れるようで。

ただ、画像としてエクスポートする機能がよく分からず。チュートリアルのデータを出力してみたけど、画像サイズをどこで指定すればいいのやら。また、背景が一部透明になってないような…? ノードとやらの設定でそのあたり違ってくるのだろうか。

これを DXRuby で使えないかな。と一瞬思ったけど、どうやって使うのかで考え込んでしまったり。3D計算してるし、再生用ランタイムライブラリがどうのこうのと説明されてるから、組み込めないよな…。出力画像を表示するのが無難だろうか。でも、元ツールはリアルタイムにパーティクルを動かしてるわけだから、DXRuby上でもリアルタイムに動かしたほうが…。いや、Rubyの上でそんなの動かしたら、処理速度が間に合わないかもしれないか。数百〜数千のパーティクルを出してるわけだし。

もしかすると、JavaScript + Canvas や JavaScript + WebGL で再生できるようになったら、また違う使われ方を…。いや、どうかな…。

保存ファイル .efkproj を眺めたところ、xml形式で保存されてた。xml解析して別環境で再生、等はやりやすいほうかもしれない…?

#3 [blender] blenderでエッジを出すソレ

blender で形状を作ってスムースを有効にすると、全ての面が滑らか表示になってしまって悩んでたのだけど。Edge Split モディフィアーを使えば解決するらしいと知ったのでメモ。

_Blenderモディファイア(EdgeSplit)
_Doc:JA/2.6/Manual/Modifiers/Generate/Edge Split - BlenderWiki
_Blender.jp - リリースノート-Blenderスムージング問題の解決法

しかし、コレ、辺を分割して実現するようで。まあ、モディフィアーの適用ボタンを押さないままにしておけばいいのだろうけど…。

2014/01/10(金) [n年前の日記]

#1 [cg_tools] 自機画像っぽいものを作成

STGのサンプルに使えそうな自機画像っぽいものを作成。
  1. blender でモデリング・レンダリングして。
  2. GIMPでトリミング、色調補正、コントラスト変更、縮小して。
  3. ImageMagcik で、複数の画像ファイルを一つの画像ファイルにまとめて。
  4. OPTPiX、または EDGE2 で減色。
  5. EDGE2 でドット修正。

不透明部分と透明部分の境界をドット単位でパキッとさせたかったので、GIMP上でレイヤーマスクを作って、そのレイヤーマスクに対して、色 → しきい値で二値化してみたり。ただ、これだと、境界の部分に変な色が載ってしまう…。

そこで、もうちょっと工夫したり。
  1. あらかじめレイヤーのアルファチャンネルをどこか(チャンネル or 別レイヤー)に仮保存。
  2. 黒ベタ画像を背景にしてレイヤー統合。
  3. そのレイヤーに対してレイヤーマスクを作る。
  4. 仮保存してあったアルファチャンネル分をレイヤーマスクに反映。
これだと、輪郭のところに黒い線が入ってしまうけど…まあ、輪郭線がついてるドット絵にしてしまえばいいかなと。

ちなみに、メニュー → レイヤーで、アルファチャンネルをしきい値で二値化、という機能もあるのだけど。これだと見た目でどんな結果になるかプレビューできないので、使ってなかったり。どの程度の値にすればいいのか、既に分かってる場合は一番手っ取り早いのだろうけど。

ImageMagick で一つの画像にするのは、以下のような記述で。
convert +append hoge*.png output.png
montage -tile 4x4 -geometry 64x64 -background none hoge*.png output.png

で、一応、64x64 で数パターンの画像はできたのだけど。どう見ても…「ああ、かなり昔の同人ゲームのソレですね」てな感じの悲しい画像に。自分の作るその手の画像は、根本的なところで何か重大な要素が欠落しているような気がする。

そもそも、64x64ぐらいなら、3DCGツールなど使わずにドット打っていったほうが早いだろ、と笑われそうな気もする。…ピタリと止まってるドット絵なら、自分もなんとかなりそうだけど、アニメのさせ方がよくわからないわけで。いや、3DCGのソレは下絵に使って、とかやればいいのかもしれないけど。

爆発パターン画像を作成。 :

爆発パターン画像の作成は、 _Detonation を使わせてもらったり。ありがたや。

このツールで作った画像のライセンスはどうなるのかな…? と思ったけど、ヘルプhtmlに書いてあった。
○著作権とか

 このソフトの著作権はRRRQにありますが、
 このソフトで作ったデータはそれを作成した本人に著作権が発生します。
 よ〜するに、作った爆発はどんどん使っていーよんってことです(w
 あ、あとサンプルに付けたデータも著作権フリーということで。
なるほど。てことは、作成した画像を OpenGameArt あたりに CC0ライセンスで投稿してもOK、なのかな…?

#2 [dxruby] STGっぽいものを作成中

自機画像もできたので、DXRuby を使って、横スクロールSTGっぽいものを作成中。

Sprite を使っていて、ちょっとハマったり。アニメパターンの最初の1フレームだけ、変な位置に表示されてしまって。

initialize() 中で super しか呼んでなかったのがマズかったらしい。super(x, y, image) までやらないとダメなのだな。…と思ったけれど、それでもやっぱり何かの拍子におかしくなるような。alpha、blend、angle、scale_x、scale_y、全部使うと、何かの拍子に変な位置に表示される。ただ、必ずいつでも起きるわけでもなくて。どんな条件で発生するのか、よく分からない。そのうち検証してみよう…。

Shader を使って背景をラスタースクロールさせたいなと思ったのだけど、HLSL とやらが、よく分からず。脳内イメージとしては、サンダーフォースIVのステージ5の宇宙面っぽい背景がやりたいわけで。

_Thunder Force IV HD Full Run (Long Play) No Miss (Progressive) - YouTube (14:00頃)

昔の2Dゲーム機って、ラスター本数分のスクロール値を格納するメモリ領域があって、その領域にCPUがあらかじめスクロール値を書き込んでおくと、後はハードウェア側でその通りにラスタースクロールしてくれる、てな感じだったのだけど。

そういうことを、DXRuby の Shader + HLSL でやれないかなと思えてきたり。そういう仕組みができれば、処理が変わるたびにHLSL側を弄ったりせず、Ruby側で配列内の値を生成・変更すれば事足りるはずで。つまり、HLSLの知識まで要求せずに済むだろうなと。たぶん、そういうクラスを作って、 require すればOKであろう予感。

ただ、HLSL側で配列相当を扱えるのかどうかが分からなくて。Shader/HLSLのサンプルの中に、ラスタースクロール関係のサンプルがあるかもしれないので、眺めてみないと…。

でも、もしかすると、今時のGPUなら、BG相当の画像を十数枚ほどそのまま重ねて描画しちゃったほうが速かったりするのかな、てな疑問も。

2014/01/11() [n年前の日記]

#1 [blender] blenderとバングマッピングと白黒と凸凹

バンプマッピングなのかノーマルマップ(法線マップ)なのか分からないけど、画像を使って表面が凸凹してるように見せるソレを勉強中。

バンプマッピングは白黒の画像を使うらしい。ノーマルマップは、RGB画像を使うようで。とりあえず、設定してる様子をキャプチャ。

バンプマッピング

ノーマルマップ

レンダリング結果


白が凹で、黒が凸、てな記事を見かけたのだけど、実際試してみたら逆になったような…? 白いところが浮かび上がって、黒いところが凹んでるように見えるのだけど…。ちょっと自分の目に自信が無いです。使ってるのは blender 2.69 だけど、バージョンによって違うのかな?

気になってググってみたら、「白が凸、黒が凹」と書いてる人も居るし、「白が凹、黒が凸」と書いてる人も居るし…。一体どっちなんだろう。

もっとも、blenderの場合、Normalの値をマイナス値にすれば凸凹が反転するみたいなので、どっちでも見た目ヨサゲになればいいような気もする。ただ、他のレンダラーを使おうとする場合はハマるらしい。 _たまに更新されるblenderとかの記録: LuxRenderのディスプレイメントマップ によると、LuxRender は白が凸で固定されてるらしいので…。黒地に白で出っ張り部分を、と意識して描いたほうがいいような気もする。

ノーマルマップ画像の作成方法。 :

ノーマルマップの作成には、Photoshop Elements 8 + _NVIDIA Texture Tools for Adobe Photoshop を使用。NVIDIA Texture Tools のインストール先フォルダは、Photoshop Elements のインストールフォルダを指定すればいいらしい。

以下、参考ページ。

_【Blender】テクスチャマッピング:ノーマル(法線)マップとバンプマップの比較
_【その他】その他/フリーソフト:ノーマル(法線)マップ作成Photoshop用プラグイン
_アドビ フォトショップ用DDSプラグインの導入方法 [ 〜 覚書きメモ 〜 ]
_アドビ フォトショップ用DDSプラグイン導入方法 [ 〜 覚書きメモ 〜 ]

GIMP でもノーマルマップは作れるらしい。 _gimp-normalmap から、gimp-normalmap-win32-1.2.3.zip をDL、解凍。*.dll を、GIMPのインストールフォルダにコピー。*.exe を、ユーザフォルダ\plug-ins\ 以下にコピー。 とりあえず、Windows7 x64 + GIMP 2.8 Portable では動作した。

以下、参考ページ。

_GIMP normalmap pluginの導入方法 | 狭き桃
_模擬実験録(仮) Gimpで法線マップ作成
_【Lesson 夏休み編】GIMPでnormalmapを編集してみる | おいでよ♪Trenza Cafe "ORION"

#2 [cg_tools] 爆発生成ソフトDetonationの仕様がよく分からず

自作画像を読み込んで爆発画像を作ろうとしたら、「レイヤー0のFireでパレットが〜」なるエラーが。変だな…。パレット相当の画像と、爆発のチップは、同じ16色画像から切り出しているのに…。そもそも、Detonation はレイヤー1,2,3... と続くので、レイヤー0なんて無いような気もするのだけど。

Detonation のサンプルファイルからエクスポートした画像をEDGE2で修正してそのまま保存したら、エラーが出なかった。うーん。よくわからん。

2014/01/12() [n年前の日記]

#1 [dxruby][game] DXRubyでHLSL勉強中

DXRubyでラスタースクロールをやってみたいわけですよ。配列の中に入ってるスクロール値を使ってラスタースクロールさせる、みたいなことをやってみたい。てなわけで、Shader だか HLSL だかを勉強中。

ググってるうちに、 HLSL でも配列を ―― int param[50]; みたいな書き方をすれば確保はできるらしい、と知ったものの。DXRuby から「このグローバル変数は配列だよ」と伝える記述が不明で。

また、HLSLで配列を用意すると、レジスターサイズ云々という問題も出てくるらしいと知り、実際に使えそうなのかどうか、ちょっとよく分かってない状態。

その前に、ラスター単位でスクロール量を変更することができるのかどうか、確認してみないといかんなと。そこで、昔の2Dゲームでよくやってた、天井だの床だのをラスタースクロールをするソレを実験。

しかし、上手く行かない。何故か、画面がやたらとチラチラする…。
画面チラチララスタースクロール

色々ググってるうちに、もしかしたらフィルターとやらが関係してたりするのかなと。試しに、以下のように変更してみたり。
  sampler Samp0 = sampler_state
  {
    Texture =<tex0>;

    // こっちだと画面がチラチラする (´・ω・`)
    // MinFilter = LINEAR;
    // MagFilter = LINEAR;
    // MipFilter = LINEAR;

    // こっちならチラチラしない
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = NONE;

    AddressU = WRAP;
    AddressV = WRAP;
  };
画面がさほどチラチラしないラスタースクロール
かなり改善された。ただ、これでもまだ、チラチラしてるけど…。

とりあえず、それっぽく動いた版を貼ってみたり。

_shader_test7_b.rb
# Shaderを使ってラスタスクロールのテスト
# 天井、壁、床をラスタースクロールさせるっぽい感じの処理

require 'dxruby'

hlsl = <<EOS
  float2 size;
  float d;
  float dd;
  texture tex0;

  sampler Samp0 = sampler_state
  {
    Texture =<tex0>;

    // こっちだと画面がチラチラする (´・ω・`)
    // MinFilter = LINEAR;
    // MagFilter = LINEAR;
    // MipFilter = LINEAR;

    // こっちならチラチラしない
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = NONE;

    AddressU = WRAP;
    AddressV = WRAP;
  };

  struct PixelIn
  {
    float2 UV : TEXCOORD0;
  };

  struct PixelOut
  {
    float4 Color : COLOR0;
  };

  PixelOut PS1(PixelIn input)
  {
    PixelOut output;
    input.UV.x = input.UV.x + ((d + (d * input.UV.y * dd)) / size.x);
    output.Color = tex2D( Samp0, input.UV );
    return output;
  }

  PixelOut PS2(PixelIn input)
  {
    PixelOut output;
    input.UV.x = input.UV.x + ((d + (d * (1.0 - input.UV.y) * dd)) / size.x);
    output.Color = tex2D( Samp0, input.UV );
    return output;
  }

  PixelOut PS3(PixelIn input)
  {
    PixelOut output;
    input.UV.x = input.UV.x + d / size.x;
    output.Color = tex2D( Samp0, input.UV );
    return output;
  }

//   float4 PS(float2 input : TEXCOORD0) : COLOR0
//   {
//     float4 output;
//     // input.x -= ((d / size.x) + ((d / size.x) * input.y * dd));
//     input.x = input.x - ((d + (d * input.y * dd)) / size.x);
//     output = tex2D( Samp0, input);
//     return output;
//   }

  technique FloorScroll
  {
    pass P0
    {
      PixelShader = compile ps_2_0 PS1();
    }
  }
  technique CeilingScroll
  {
    pass P0
    {
      PixelShader = compile ps_2_0 PS2();
    }
  }
  technique WallScroll
  {
    pass P0
    {
      PixelShader = compile ps_2_0 PS3();
    }
  }
EOS

core = Shader::Core.new(hlsl, {:size=>:float, :d=>:float, :dd=>:float})
shader1 = Shader.new(core, "CeilingScroll")
shader2 = Shader.new(core, "WallScroll")
shader3 = Shader.new(core, "FloorScroll")

# 画像に対してランダムにドットを打つ
def plot_random_dot(image)
  dt = [[10, [255, 0, 64, 255]],
    [5, [255, 0, 255, 255]],
    [3, [255, 255, 255, 255]]]
  srand(0)
  image.height.times do |y|
    # next if y % 2 != 0
    dt.each do |d|
      d[0].times {|i| image[rand(image.width), y] = d[1]}
    end
  end
end

image1 = Image.new(640, 150, [0, 0, 0, 0]) # 天井
image2 = Image.new(640, 180, [0, 0, 0, 0]) # 壁
image3 = Image.new(640, 150, [0, 0, 0, 0]) # 床
plot_random_dot(image1)
plot_random_dot(image2)
plot_random_dot(image3)

shader1.size = [image1.width, image1.height]
shader1.d = 0
shader1.dd = 2.0 # yが1ドット進むごとにつける角度

shader2.size = [image2.width, image2.height]
shader2.d = 0
shader2.dd = 0

shader3.size = [image3.width, image3.height]
shader3.d = 0
shader3.dd = 16.0

# Window.fps = 10
x = 0
Window.loop do
  break if Input.keyPush?(K_ESCAPE)
  x += 1
  shader1.d = x # スクロール量を指定
  shader2.d = x
  shader3.d = x

  y = 0
  Window.draw_shader(0, y, image1, shader1)
  y += image1.height
  Window.draw_shader(0, y, image2, shader2)
  y += image2.height
  Window.draw_shader(0, y, image3, shader3)
end
HLSLについては、サンプルスクリプトをコピペして、数行書き換えてるだけなので…。サンプルと同様に Public Doamin として扱ってくれれば、と。

画像を用意するのが一番の問題。 :

プログラム部分は動いたので、天井だの床だのに使う画像を作ってみようとしたのだけど、これがまったく上手く行かず。GIMP で、タイル画像を並べて、遠近法変形?をしてみる、等の作業をしていたのだけど…。実際に表示してスクロールさせてみると、見た目がどんどんグチャグチャになっていく。

スクロール量と、画像内の傾き具合が一致してないと、見た目がおかしくなるのだなと。また、ラスター毎にループするようにしておいて、一定量変化したらスクロール値をリセット、等の処理が必要かもしれないなと。何にせよ、元画像を用意する際に、何か制作上のコツがありそうな。

しかし、考えてみたら、そもそもHLSLを使ってるのだから…。ただのタイリング画像を渡して、プログラム側で遠近法っぽい変形を ―― ラスター毎の拡大縮小をして表示したほうがいいんじゃないかと思えてきたり。元画像を遠近法のソレっぽい見た目にしなきゃいけないのは、ハードウェア的に縦横のスクロールしかできなかった、大昔のゲーム機に限定された話だよなと。今なら、というかDXRubyなら、XEXEXや、レイディアントシルバーガンの背景みたいな見せ方もできるはず。

まあ、そのあたりの処理は、今後の課題です…。

背景も何もかもポリゴン描画するのが当たり前になってしまった時代に、自分は一体何をやってるのだろう、てな気もしますが。

#2 [cg_tools] 爆発エフェクト画像作成ツールについてメモ

_エフェクト作成ツール : ぴぽや 経由で、パーティクルエディタ、 _パチクリ なるソフトを知ったので、試しに使ってみたり。

_Detonation 、プラス、その場でドットパターン(?)を作成できる機能付き、みたいな印象。かなりイイ感じ。ドット絵と組み合わせて使う爆発エフェクト画像を作る際には重宝しそうな予感。

2014/01/13(月) [n年前の日記]

#1 [dxruby] DXRubyでラスタースクロールっぽいのができた

DXRuby の Shader を使って、サンダーフォースIVのステージ5っぽい背景ができた、ような気がする。下のような感じになりました。

ラスタースクロールっぽい処理
GIFアニメのフレームレートが低くてなんだかアレだけど、PC上で動かして、60FPSのソレを眺めれば、結構イイ感じ。

一応、ソース貼っときますね。

_starrasterbg.rb
require 'dxruby'

# Shaderを使ってラスタスクロールさせるクラス
#
# 宇宙空間っぽいBG画像をラスタースクロールさせる
#
class StarRasterScrollShader < DXRuby::Shader
  hlsl = <<EOS
  float2 size;    // 画像サイズ
  float d;        // スクロール位置
  float spd;      // ラスター毎の変化量
  float afactor;  // アルファ値
  texture tex0;

  // ラスター毎のスクロール量。16ラスターずつ繰り返し
  float dd[16] = {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15};

  sampler Samp0 = sampler_state
  {
    Texture =<tex0>;

    // こっちだと画面チラチラ
    // MinFilter = LINEAR;
    // MagFilter = LINEAR;
    // MipFilter = LINEAR;

    // こっちならチラチラしない
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = NONE;

    AddressU = WRAP;
    AddressV = WRAP;
  };

  struct PixelIn
  {
    float2 UV : TEXCOORD0;
  };

  struct PixelOut
  {
    float4 Color : COLOR0;
  };

  PixelOut PS(PixelIn input)
  {
    PixelOut output;
    input.UV.x += ((d + (d * spd * dd[fmod(input.UV.y * size.y, 16)])) / size.x);
    output.Color = tex2D( Samp0, input.UV );
    output.Color.a *= afactor;
    return output;
  }

  technique StarRasterScroll
  {
    pass P0
    {
      PixelShader = compile ps_2_0 PS();
    }
  }
EOS

  @@core = DXRuby::Shader::Core.new(hlsl,
                                    {
                                      :size => :float,
                                      :d => :float,
                                      :spd => :float,
                                      :afactor => :float
                                    })

  attr_accessor :speed

  #
  # 初期化処理
  #
  # @param [float] speed   スクロール速度
  # @param [float] v       ラスター毎の速度変化量。大きくすると速度差が大きくなる
  # @param [int]   width   画面横幅。省略するとウインドウ横幅
  # @param [int]   height  画面縦幅。省略するとウインドウ縦幅
  #
  def initialize(speed=1, v=0.4, width=nil, height=nil)
    super(@@core, "StarRasterScroll")
    w, h = Window.width, Window.height
    w = width if width
    h = height if height
    self.size = [w, h]
    self.d = 0.0
    self.spd = v
    self.afactor = 1.0
    @speed = speed
  end

  #
  # スクロール値更新処理
  #
  def update
    self.d += @speed
  end

  #
  # スクロール値初期化
  #
  def reset_scroll
    self.d = 0
  end

  #
  # スクロール速度変更
  #
  # @param [float] speed   スクロール速度
  # @param [float] v       ラスター毎の速度変化量。大きくすると速度差が大きくなる
  #
  def set_speed(speed=1, v=0.4)
    @speed = speed
    self.spd = v
  end

  #
  # アルファ値変更
  #
  # @param [float] v   アルファ値、0.0で透明、1.0で不透明
  #
  def set_alpha(v)
    self.afactor = v
  end

  #
  # 星が描かれた背景用画像を作成して返す
  #
  # @param [int] width      画像横幅(省略時はWindow横幅)
  # @param [int] height     画像縦幅(省略時はWindow縦幅)
  # @param [int] num        散布するドット数
  # @param [bool] len_sync  trueなら線の長さをラスタースクロール速度に合わせる
  #                         デフォルトはfalse
  #
  # @return [Image] 作成した画像(Image)
  #
  def get_star_image(width=nil, height=nil, num=0x3fff, len_sync=false)
    width = Window.width unless width
    height = Window.height unless height
    img = Image.new(width, height, [0, 0, 0, 0])

    srand(0)

    # 速度テーブル(16ラスター分)
    spdtbl = [0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]

    # 速度と対応した線の長さ
    wtbl = [5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 12, 24, 36, 480]

    # ドットの色データ(3色に決め打ち)
    colordata = [[0, 47, 191], [69, 119, 209], [180, 191, 227]]

    # 画像に対してランダムに線を引く
    img.height.times do |y|
      # next if y % 16 != 0 and y % 16 != 1
      ymod = y % spdtbl.length

      if len_sync
        # ラスター毎のスクロール速度と対応した線の長さにする場合
        w = wtbl[spdtbl[ymod]]
      else
        # ラスター毎のスクロール速度と対応してない線の長さにする場合
        w = wtbl[ymod]
      end

      b = ((spdtbl[ymod] + 1) / spdtbl.length.to_f) * 0.5 + 0.5

      if w == wtbl[-1]
        w = rand(w) + wtbl[0]
        b *= (rand(70) + 31) / 100.0
      end

      l = ((spdtbl.length - ymod) * 0.75).to_i
      l = 1 if l == 0
      l.times do |i|
        x = rand(img.width - w)
        if w <= 8
          wt = [[x, w], [x + 1, w - 2], [x + 2, w - 4]]
        else
          wt = [[x, w], [x + w * 0.08, w * 0.84], [x + w * 0.16, w * 0.68]]
        end
        colordata.each_with_index do |d, i|
          lx, lw = wt[i]
          img.line(lx, y,  lx + lw, y, [d[0] * b, d[1] * b, d[2] *b])
          break if i== 1 and wt[i][0] == wt[i+1][0]
        end
      end
    end

    # 画像に対してランダムに点を打つ
    num.times do |i|
      c = colordata[rand(3)]
      b = rand(50) / 100.0 + 0.5
      img[rand(img.width), rand(img.height)] = [c[0] * b, c[1] * b, c[2] * b]
    end

    return img
  end

  #
  # 動作確認用。垂直な線を引いた画像を作成して返す
  #
  # @param [int] width   画像横幅(省略時はWindow横幅)
  # @param [int] height  画像縦幅(省略時はWindow縦幅)
  #
  # @return [Image] 作成した画像(Image)
  #
  def get_vertical_line_image(width=nil, height=nil)
    width = Window.width unless width
    height = Window.height unless height
    img = Image.new(width, height, [0, 0, 0, 0])
    img.line(0, 0, 0, img.height, C_WHITE)
    return img
  end
end

if __FILE__ == $0
  # 動作テスト。使い方

  spd = 1 # スクロール速度
  spd_sub = 0.5 # ラスター毎のスクロール速度差

  # Window.resize(1280, 720)
  # Window.fps = 10
  # "Window.scale = 2.0

  # スクロール速度を渡して生成
  shader = StarRasterScrollShader.new(spd, spd_sub, Window.width, Window.height)

  # 星画像を生成・取得(引数無しならWindowのサイズで作る)
  img_nosync = shader.get_star_image(Window.width, Window.height, 0x3fff, false)
  img_sync = shader.get_star_image(Window.width, Window.height, 0x3fff, true)

  # 動作テスト用。垂直線画像を生成・取得
  img_vline = shader.get_vertical_line_image(Window.width, Window.height)

  alpha_chg = false
  framecnt = 0

  # メインループ
  Window.loop do
    break if Input.keyPush?(K_ESCAPE)

    # スクロール値更新
    shader.update

    # 描画
    if Input.keyDown?(K_X)
      # Xキーで垂直線を表示
      Window.draw_shader(0, 0, img_vline, shader)
    elsif Input.keyDown?(K_Z)
      # Zキーでスクロール速度と対応した星画像を表示
      Window.draw_shader(0, 0, img_sync, shader)
    else
      Window.draw_shader(0, 0, img_nosync, shader)
    end

    # ----------------------------------------
    # 以下は動作テスト用
    #
    # Fキー : フルスクリーン表示
    # Cキー : スクロール値リセット
    # Vキー : 押してる間はスクロール速度を変更
    # Aキー : アルファ値の変化を切替
    #
    Window.windowed = !Window.windowed? if Input.keyPush?(K_F)
    shader.reset_scroll if Input.keyPush?(K_C)
    if Input.keyDown?(K_V)
      shader.set_speed(0.5, 0.05)
    else
      shader.set_speed(spd, spd_sub)
    end
    alpha_chg = !alpha_chg if Input.keyPush?(K_A)
    if alpha_chg
      v = Math.sin(framecnt * Math::PI / 180.0).abs
      shader.set_alpha(v)
    else
      shader.set_alpha(1.0)
    end

    framecnt += 1
  end
end
サンプルスクリプトをコピペして少し弄って、みたいな感じで作ったので、コレもサンプル同様、Public Domain ってことで。

上記のソースを単体で実行しても表示できるけど、他のファイルから呼び出して使えるようにもしておきました。下のような感じで使えるかなと…。
require 'dxruby'
require_relative 'starrasterbg'

shader = StarRasterScrollShader.new(0.5, 0.6)
img = shader.get_star_image()

Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  shader.update
  Window.draw_shader(0, 0, img, shader)
end

ちなみに、先日から書いてる途中の、STGサンプルからも呼び出してみたり。
STGサンプルで使ったみた例
一気に雰囲気出てきた。これで勉強する気も湧くというもの。ちなみに、こっちのサンプル(?)は、まだ、アタリ処理だの、ゲームオーバー処理だのを書いてませんで。もうちょっと追加してから公開しようかなと。

さておき。HLSLについて、ちょっと分からないところが。

配列を作って、ラスター毎の速度差を並べて、16ラスター分を繰り返して処理しているのだけど。配列の添え字が float のままなわけで…。コレ、いいんだろうか? 一応動いちゃってるけど…。添え字の中に、小数点以下も持ってる値が入ってるはずなのだけど。HLSL側で、上手いことやってくれてるのかな。それともやっぱり何かマズイのかな。

配列と言えば、画面縦幅分のラスタースクロール値をどこかに格納しておかないといかんかなと思っていたけど、この程度の処理ならそこまで持たなくてもそれらしく見えてくれるようで。ただ、やっぱり汎用性(?)は無いかなと…。HLSL側は極力弄らずに、Ruby側で全てのラスターのスクロール値を作って、配列で渡せればいいのだけど。いや、そんなことができるのかどうか、まだ分からないのですが。

もしかして Sprite の変形機能を使えばよいのでは。 :

先日試した、床だの天井だのをラスタースクロールで云々、てな見せ方は、画像を変形描画できる Window.draw_morph とやらを使えばできるのではと今頃気付いたり。

ホントにそうかな…? ちょっと調べてみないと。

2014/01/14(火) [n年前の日記]

#1 [dxruby] DXRubyのWindow.drawMorph()について調べてるところ

どういう機能かよく分かってないので確認作業を。

以下のような表示ができるらしい。
Window.drawMorphの描画結果

_drawmorph_simple_test1.rb
# Window.drawMorph の描画テスト

require 'dxruby'

# グリッド画像を生成
def get_grid_image(w, h)
  img = Image.new(w, h, [64, 128, 255])
  dt = [[10, 8, C_BLUE], [5, 4, C_WHITE]]
  dt.each do |d|
    d[0].times do |i|
      x = (w / d[1]) * i
      img.line(x, 0, x, h, d[2])
      img.line(0, x, w, x, d[2])
    end
  end
  img.line(w - 1, 0, w - 1, h, dt[1][2])
  img.line(0, h - 1, w, h - 1, dt[1][2])
  return img
end

img = get_grid_image(64, 64)

Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  # 元画像を描画
  Window.draw(Window.width - img.width - 8, 2, img)

  # ただの真四角として描画
  w = 200
  d = 70
  x, y = 10, 10
  Window.drawMorph(x, y, x + w, y, x + w, y + w, x, y + w,
                   img)

  # 台形に変形して描画
  x, y = 220, 10
  Window.drawMorph(x + d, y, x + w - d, y, x + w, y + w, x, y + w,
                   img)

  # 台形に変形して描画。分割数を変更
  x, y = 10, 240
  Window.drawMorph(x + d, y, x + w - d, y, x + w, y + w, x, y + w,
                   img, :dividex => 2, :dividey => 2)

  # 台形に変形して描画。分割数を変更
  x, y = 220, 240
  Window.drawMorph(x + d, y, x + w - d, y, x + w, y + w, x, y + w,
                   img, :dividex => 4, :dividey => 4)
end
実際に描画してみて、以下のことが分かった。
テクスチャが歪む理由については、以下が参考になりそう。

_The Realm of Revery
_MascotCapsule Developer Network - 日本語 - WIPI - サンプル - WIPI + MascotCapsule V3 API - テクスチャの歪み軽減

さて、これを使って床ラスタースクロールっぽいことができるかどうか…?

気になるのは、遠近感がついてない点。元画像の縦方向に、あらかじめ遠近感をつけておけば、それらしくなるかもしれないけど。できれば、遠近感もプログラム側でつけてみたい。

遠近感をつける方法を検討する。 :

四角形の4点が指定されたとして、下のような分割方法ができれば、遠近感をつけることもできそうだなと。
遠近感をつけて分割する方法の試案

分かりづらいので、GIFアニメも用意してみたり。
遠近感をつけて分割する方法の試案(GIFアニメ版)

このあたり考えてるうちに、なんだか、学生時代に読んだ、建築パースの本を思い出してしまったり…。電子科に通ってたけど、建築科がある大学だった上に、自分は漫研に入ってたので、図書館で建築パースの描き方本を見て「ふむふむなるほど。こうやって漫画の背景を描けばいいのか」とかやってましたです。…今はそういう情報も、ネットで調べられるから便利な時代になりましたな。

さておき。2つの線分の交点を求めることさえできれば、次々に交点を求めて、遠近感のついた分割ができそうな予感。ググったところ、外積を使えば交点を求めることもできるらしくて。

_4点からなる交点の求め方 | 水玉製作所
_4点からなる交点の求め方 画像処理ソリューション

ただ、線分が平行だった場合にどうすればいいのか…。
線分が平行だと困る
黄色い線を求めたいわけだけど…。さて、どうすれば。

なんだかこのあたり、PS(プレイステーション1)やSS(セガサターン)の時代に、色々な技が駆使されてたような気がするのだけど。検索キーワードが思い出せず…。

射影変換を使えないかな。 :

色々ググってたら、射影変換だかホモグラフィだかのデモページに遭遇して。もしかしてそっち使ったほうがいいのかなと。例えば以下のデモページを眺めると…。

_重なる気持ち -台形補正- | _level0 | Kayac Front End Engineer's Blog

赤い四角を正方形に整えて、青い四角を台形っぽくすれば、赤い四角の中のマウスカーソル位置が、青い四角の中で表示されるわけで。つまり、元画像のこのあたり、を指定すると、変形後の四角ではこのあたり、になって返ってくるよなと。その、返ってきた値を使って、分割していけば、と。

そもそもShaderを使うべきかも。 :

_XEXEXのステージ1の背景 っぽいソレを、Window.drawMorph を使ってできないかなと思って調べてたけど。ああいった見せ方であれば、Shader を使ったほうがいいのかなと思えてきたり。

DXRuby開発版の、サンプルの中の、shader_sample\sample_spehari.rb を動かしたら、それっぽいことができそうな気がしてきたわけで…。

#2 [emacs] emacsのre-builderでちょっとハマった

emacs の re-builder を使えば、正規表現がちゃんと書けてるか、見た目で分かりやすくなるのだけど。\(...\) を記述する際にちょっとハマった。

emacs 上では、\(...\) のように、バックスラッシュが1つで済むのだけど。

re-builder 上では、\\(...\\) と書かないといかんらしい…。

_Emacsの正規表現編集モード re-builder とややこしいバックスラッシュ問題について。 - こせきの技術日記
_でらうま倶楽部 : Emacs re-builderで正規表現を「見える化」する
_re-builder で作った正規表現を query-replace-regexp に渡しても動作しない? - 刺身☆ブーメランのはてなダイアリー

C-c TAB、もしくは C-c C-i を叩いて、シンタックスモードを、デフォルトの read から string に変更すれば、emacs のソレと同様、バックスラッシュ1つで済む状態になるそうで。

一々変えるのは面倒臭いので、 ~/.emacs に、
(setq reb-re-syntax 'string)
と書いておきました。

こうしてメモしたけど、たぶんそのうち忘れる予感。

#3 [cg_tools] 新しいデフォルトカラー

_Colors を眺めて、綺麗だなと。デフォルトの16色に対して新しい定義を提案したい、みたいな話なのかなと。

せっかくだから、 _colors/hex.txt at master ・ mrmrs/colors をコピペして、GIMP or Inkscape用のパレットファイルも作ってみたり。一応置いときますね。

_new_defaults_colors.gpl.txt

new_defaults_colors.gpl.txt を、new_defaults_colors.gpl にリネームして、GIMPのユーザフォルダ\palettes\ 以下に置くなりしてみてください。…いや、GIMPってパレットのインポートもできるんだっけか。そのへんよく分からなかったり。

2014/01/15(水) [n年前の日記]

#1 [cg_tools] GeoGebraのインストールでハマったり

説明画像を作ろうとして、作図ツールを探したり。

幾何のアレコレをを描いて、動かして、その変更結果を見た目で確認できる、 _GeoGebra をインストールしてみようかなと。昔インストールした記憶もあるのだけど、今のPCには入れてなかったみたいで。

しかし、公式サイトから GeoGebra-Windows-Installer-4-4-7-0.exe をDLして実行しても、インストーラが、ライセンス確認画面の次に行こうとするところで不正終了してしまう。環境は Windows7 x64。

ググってみたら、同じ症状も見かけたのだけど。掲示板その他で質問しても、回答者は「ウチでは問題なくインストールできたよ?」みたいな話ばかり。てことは、自分の環境に何か原因があるのだろうか…。

さては常駐ソフトが悪さをしてるのだろうか。あらゆる常駐ソフトを終了させて試してみたけど、結果は変わらず。うーん。

_Index of /installers から辿って、GeoGebra-Windows-Installer-4-4-7-0.msi を入手してみた。.exe じゃなくて .msi版。こちらなら、インストーラは英語だけど、インストールができた。

まあ、GeoGebra-Windows-Portable-4-4-7-0.zip をDL・解凍すれば、バイナリ一式が入っているので、最悪の場合、ソレを使うのでもよさそうだけど。

#2 [dxruby] DXRuby+Shaderで床ラスタースクロールっぽいことができたような気がする

Shader利用サンプルの sample_spehari.rb を弄って、望んだ感じの床ラスタースクロール、っぽいことができたような気がする。たぶん。

sample_spehari.rb を眺めていて、気づいた点を列挙。 そのあたりを意識しながら弄ってみたり。

こんな感じに。
床ラスタースクロール

STGサンプルに組み込んでみたら、こんな感じ。
STGサンプルに組み込んでみた例
どう見ても、 _「お願い、私の星を助けて…」 ですな。アレも、こんな感じのことをしてたのかしら。

長いけど、一応ソースも貼っときます。

_floorrasterscroll.rb
require 'dxruby'

#
# Shaderを使って床ラスタースクロールっぽい処理をするクラス
#
class FloorRasterScroll

  @@hlsl = <<EOS
  float floor_distance;
  float screen_distance;
  float x_diff;
  float y_diff;
  float alpha;
  texture tex0;

  sampler Samp0 = sampler_state
  {
    Texture =<tex0>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
  };

  float4 PS(float2 input : TEXCOORD0) : COLOR0
  {
    float4 output;
    input.y = screen_distance * floor_distance / -(input.y + y_diff);
    input.x = (input.x - x_diff) * -input.y / screen_distance;
    output = tex2D( Samp0, input );
    output.a = alpha;
    return output;
  }

  technique FloorRasterScroll
  {
   pass P0
   {
    PixelShader = compile ps_2_0 PS();
   }
  }
EOS

  attr_accessor :core, :shader, :image
  attr_accessor :w, :h
  attr_accessor :tilew, :tileh
  attr_accessor :rt_w, :rt_h, :rt_scaley
  attr_accessor :rt
  attr_accessor :floor_dist, :scr_dist, :x_diff, :y_diff, :alpha

  #
  # 初期化処理
  #
  # @param [Image] image 使用する画像
  # @param [float] floor_dist 床までの距離。1.0前後が目安
  # @param [float] scr_dist 画面までの距離。1.0以下が目安
  # @param [float] y_diff 視線方向を上下にずらす量。0.0から1.0の範囲
  # @param [float] alpha 透明度。0.0で透明。1.0で不透明
  # @param [int] w 描画横幅。nil ならウインドウ横幅
  # @param [int] h 描画縦幅。nil ならウインドウ縦幅
  #
  def initialize(image, floor_dist=0.3, scr_dist=0.3, y_diff=0.1, alpha=1.0,
                 w=nil, h=nil)
    self.core = Shader::Core.new(@@hlsl,{
                                   :floor_distance=>:float,
                                   :screen_distance=>:float,
                                   :x_diff=>:float,
                                   :y_diff=>:float,
                                   :alpha=>:float
                                 })
    self.shader = Shader.new(self.core, "FloorRasterScroll")

    self.image = image

    self.w = (w == nil)? Window.width : w
    self.h = (h == nil)? Window.height : h

    self.floor_dist = floor_dist
    self.scr_dist = scr_dist
    self.y_diff = y_diff
    self.alpha = alpha

    # 画面を覆い隠すタイル数を求める
    self.tilew = self.w.quo(self.image.width).ceil
    self.tileh = self.h.quo(self.image.height).ceil

    # RenderTarget の必要サイズを求める
    self.rt_w = self.image.width * self.tilew
    self.rt_h = self.image.height * self.tileh

    # y方向で拡大縮小して描画縦横幅に合わせる
    self.rt_scaley = self.h.quo(2).quo(rt_h)

    # Rendertarget生成
    self.rt = RenderTarget.new(rt_w, rt_h)

    # 画面の真ん中に消失点?を持ってくるための補正値を算出
    self.shader.x_diff = 0.5 * self.w.quo(self.rt_w)
  end

  #
  # 描画処理
  #
  # @param [int] x 描画x座標
  # @param [int] y 描画y座標
  # @param [float] scrl_x 横方向スクロール位置
  # @param [float] scrl_z 奥行方向スクロール位置
  # @param [Hash] opts オプション
  # @option opts [float] floor_dist 床との距離。1.0前後が目安
  # @option opts [float] scr_dist 画面との距離。1.0以下が目安
  # @option opts [float] y_dist 視線方向を上下にずらす量。0.0 - 1.0 の範囲
  # @option opts [int] z 描画奥行情報
  # @option opts [float] alpha 透明度。0.0で透明。1.0で不透明
  # @option opts [Boolean] vflip trueなら上下判定。falseなら反転しない
  #
  def draw(x, y, scrl_x, scrl_z, opts={})
    self.floor_dist = opts[:floor_dist] if opts.has_key?(:floor_dist)
    self.scr_dist = opts[:scr_dist] if opts.has_key?(:scr_dist)
    self.y_diff = opts[:y_diff] if opts.has_key?(:y_diff)
    self.alpha = opts[:alpha] if opts.has_key?(:alpha)

    self.shader.floor_distance = self.floor_dist
    self.shader.screen_distance = self.scr_dist
    self.shader.y_diff = self.y_diff
    self.shader.alpha = self.alpha

    vflip = 1
    vflip *= -1 if opts.has_key?(:vflip) and opts[:vflip] == true

    z = 0
    z = opts[:z] if opts.has_key?(:z)


    # Shader が参照する画像を RenderTarget に作成
    self.rt.draw_tile(0, 0, [[0]], [self.image],
                      scrl_x, scrl_z, self.tilew, self.tileh).update

    # Shader を使って透視変換しつつ描画
    Window.drawEx(x, y, self.rt, :shader=>self.shader,
                  :scaley=>self.rt_scaley * vflip, :centery=>0, :z=>z)
  end

  #
  # 動作確認用。グリッド画像を生成して返す
  #
  # @param [int] w 画像横幅
  # @param [int] h 画像縦幅
  # @return [Image] 生成した画像
  #
  def self.get_grid_image(w, h)
    image = Image.new(w, h, [0, 255, 0])
    image.box_fill(0, 0, w / 2 - 1, h / 2 - 1, [150,250,150])
    image.box_fill(w / 2, 0, w - 1, h / 2 - 1, [100,250,100])
    image.box_fill(0, h / 2, w / 2 - 1, h - 1, [200,250,200])
    image.box_fill(w / 2, h / 2, w - 1, h - 1, [0,220,0])
    return image
  end
end

# ----------------------------------------

if $0 == __FILE__
  # 動作テスト

  # 画像を読み込む
  # imgfilename = "lena.png"
  imgfilename = "cloud.png"

  if File.file?(imgfilename)
    image = Image.load(imgfilename)
  else
    # 画像ファイルが見つからなければ、タイル画像を作成
    image = FloorRasterScroll.get_grid_image(160, 160)
  end

  test_mode = 1

  case test_mode
  when 0
    # ----------------------------------------
    # シンプルな使い方の例

    bg = FloorRasterScroll.new(image)
    x = 0
    z = 0
    Window.loop do
      break if Input.keyPush?(K_ESCAPE)
      x += 8
      z += 5
      bg.draw(0, 240, x, z)
    end

  when 1
    # ----------------------------------------
    # 動作テスト。各パラメータを細かく変更しながら描画してみる

    bg = FloorRasterScroll.new(image) # 床用
    bg2 = FloorRasterScroll.new(image) # 天井用

    x = 0
    z = 0
    x_spd = 8
    z_spd = 5
    floor_dist = 0.3
    scr_dist = 0.3
    y_diff = 0.1

    font = Font.new(16)
    fade_enable = false
    count = 0

    Window.loop do
      break if Input.keyPush?(K_ESCAPE)

      # 速度リセット
      if Input.keyDown?(K_C)
        x_spd = 0
        z_spd = 0
      end

      # 横方向スクロール速度を変更
      x_spd += 0.1 * Input.x

      # 奥行方向スクロール速度を変更
      z_spd -= 0.1 * Input.y

      # 床との距離を変更
      floor_dist += 0.01 if Input.keyDown?(K_W)
      floor_dist -= 0.01 if Input.keyDown?(K_S)

      # 画面までの距離を変更
      scr_dist += 0.005 if Input.keyDown?(K_D)
      scr_dist -= 0.005 if Input.keyDown?(K_A)
      scr_dist = 0.05 if scr_dist < 0.05

      # 視線を上下にずらす値を変更
      y_diff += 0.005 if Input.keyDown?(K_R)
      y_diff -= 0.005 if Input.keyDown?(K_F)

      # 透明度を変更
      fade_enable = !fade_enable if Input.keyPush?(K_Q)
      alpha = (fade_enable)? Math.sin(3 * count * Math::PI / 180.0).abs : 1.0

      # スクロール位置変更
      x += x_spd
      z += z_spd

      # 描画
      bg.draw(0, 240, x, z,
              :vflip=>false,
              :floor_dist=>floor_dist,
              :scr_dist=>scr_dist,
              :y_diff=>y_diff,
              :z=>-255, :alpha=>alpha)

      bg2.draw(0, 240, x, -z,
               :vflip=>true,
               :floor_dist=>floor_dist,
               :scr_dist=>scr_dist,
               :y_diff=>y_diff,
               :z=>-255, :alpha=>alpha)

      # 説明文を描画
      tx = 4
      h = 20
      Window.drawFont(tx, h * 0, "RIGHT , LEFT : x speed = #{x_spd}", font)
      Window.drawFont(tx, h * 1, "UP , DOWN    : z speed = #{z_spd}", font)
      Window.drawFont(tx, h * 2, "W , S : floor dist = #{floor_dist}", font)
      Window.drawFont(tx, h * 3, "D , A : screen dist = #{scr_dist}", font)
      Window.drawFont(tx, h * 4, "R , F : y_diff = #{y_diff}", font)
      Window.drawFont(tx, h * 5, "Q : Fade in/out", font)
      Window.drawFont(tx, h * 6, "C : Speed clear", font)

      count += 1
    end

  end

end
動作テスト部分のほうが、長いという…。

今回も、単体で実行しても動くけど、他のファイルから呼んでも使えるようにしておきました。本体ファイル?と同じフォルダに floorrasterscroll.rb を入れといて、以下のような記述で使えるかなと…。
# 床ラスター処理のテスト

require 'dxruby'
require_relative 'floorrasterscroll'

image = Image.load("lena.png")
bg = FloorRasterScroll.new(image)
x = 0
z = 0
Window.loop do
  break if Input.keyPush?(K_ESCAPE)
  x += 8
  z += 4
  bg.draw(0, 240, x, z)
end

サンプルを弄って作っただけなので、これもサンプル同様、Public Domain にしておきます。それと、画像は以下からDLして使ってみてください。

_cloud.png (320x320 dot)
cloud.png


もやもやした模様ではよく分からないでしょうから、あの有名な、 _lena様画像 も置いときますね…。

_lena.png (160x160 dot)

使うと、以下のような感じになります。
lena様で試した結果

やっぱり、lena様のような画像で実験すると、なんだかやる気がでてくるなあ。

透視変換で頭が混乱してきたのでメモ。 :

透視変換をするShader ―― HLSLを書くあたりで、ちょっとハマった。なんだかややこしいので、一応図解してメモ。

まず、画面座標は、縦横 0.0 〜 1.0 の範囲になってる、と考える。また、元画像は、床に、つまり平面に、ずらりと並んでると考える。z値として得た値は、uv値のvとして使う、とでも言えばいいのか…。
概念図
もしかしたら v の方向は逆かもしれないけど。上が1.0なのか、下が1.0なのか、よく分かってません…。

真横から見た状態を考える。
真横から見た状態
作図ツールの関係で、上方向が +y になってるけど、プログラム上では、下方向が +y なので、ちと注意。

値が分かってるものと、値が分かってないものを、分類してみる。
  • sy は、画面上の座標。HLSL 内の input.y が、そのものズバリなので、これは値が分かってる。
  • sz は、画面までの距離。これはテキトーに決めるので、値が分かってる。画面の縦横が 0.0 〜 1.0 なので、sz も 0.n みたいな値になりそう。
  • py は、床までの距離。これもテキトーに決めるので、値が分かってる。おそらくこれも、値が 0.n ぐらいになりそう。
  • pz は、参照する画像の、v値。これが分からない。
ということで。pz を求めればいい。

(sz, sy) が作る三角形と (pz, py) が作る三角形は相似なので、以下が成り立つ。
sz : sy = pz : py
sz * py = sy * pz
pz = (sz * py) / sy
これで、参照する画像の v 値は得られるので、HLSL内の input.y に代入してやればいい。はず。

さらに、真上から見た状態を考える。
真上から見た状態

これも、値が分かってるものと、分かってないものを、分類してみる。
  • sx は、画面上の座標。HLSL内の input.x から得られる。これは値が分かってる。
  • sz は、画面までの距離。これはテキトーに決めるので、値が分かってる。
  • pz は、参照する画像の v値。これはさっき求めたから、値が分かってる。
  • px は、参照する画像の u値。これが分からない。コイツを求めたい。
ということで。
sz : sx = pz : px
sz * px = sx * pz
px = sx * pz / sz

で、注意点が。

sx は input.x から求まるのだけど、input.x の範囲は、画面左から右に向かって 0.0〜1.0 なので、真ん中は 0.5。なので、sx = input.x - 0.5 として、ずらしてやらないといけない。

sy は…。今回は、床を表示したいなーと思ってるので、画面の下半分だけ考えればいいよなと。だったら、sy = input.y でいいかなと。画面の上半分も含めて何かしたいなら、sy = input.y - 0.5 になるのだろうけど。

そんな感じで、HLSL内では、以下のようなソレを書けば良さそう。
sz = 画面までの距離
py = 床までの距離

input.y = sz * py / input.y
input.x = (input.x - 0.5) * input.y / sz
と思ったけど、試してみたら上下が逆になった。やっぱり v値は、上が1.0なのかもしれない…? そんなわけで、ちょっと修正。
input.y = sz * py / -input.y
input.x = (input.x - 0.5) * -input.y / sz
見た目はそれらしくなったから、これでいいか…。

天井はどうしてるかというと。 :

天井は、床を上下反転して描画してるだけです。Window.drawEx() で描画する際に、:scaley => -n を渡してるだけ。

#3 [dxruby] DXRubyで四角形の自由変形をする際に遠近感をつけるとかえってマズイのかもしれない

DXRuby の Window.drawMorph() を使えば、四角形の自由変形ができるので、もしかすると疑似3Dっぽい見せ方ができるかな、と夢想してたけど。変形する際に、というか分割する際に、遠近感をつけて分割したら、なんだかマズイような気がしてきたわけで。

たとえば、ゼビウスのバキュラみたいなソレに、テクスチャを貼ったら…回転軸上のテクスチャは、表示する際、位置がずれないだろうなと。

だけど、変形した四角形の4点から、対角線の交点を求めて分割を、とかやったりすると、位置がずれるよなと。4点の位置が変わるたびに中心部分があっちこっちにうねうね動いちゃうのってどうなんだろう、かえって不自然にならないかと思えてきたり。

ん? もしかして、回転軸が、本来なら対角線の交点上になってるのが正しいのかな? なんだかよく分からなくなってきた…。

変形した際の謎

図にしてみたけど、やっぱりよく分からない…。どれが自然な見た目になるんだろう。動かしてみないとわからんかな…。

#4 [pc] Javaのアップデート時にMcAfeeのソレも入れてよと言ってくるのが困る

コレ、親父さんや、親父さんの友人知人は、うっかり入れちゃいそうだな…。そして、ウイルス対策ソフトが、1つのPC上で2つも3つも同時に動いてて、何をするにしても遅い、なんだか最近PCの調子がおかしい、てな話になりそうな予感。

と思ったけどググってみたら、そもそもウイルス対策ソフトじゃないっぽい。

_McAfee Security Scan Plus削除アンインストール方法 【宣伝広告ソフト】
_ロイドベンチャーシステム雑記<ロイドの日記>: McAfee Security Scan Plus がいつの間にかインストールされている
_【スタッフ日記】McAfee Security Scan Plusってなんだ?「アンインストール手順」|パソコン修理設定・データ復旧・救出のコムサス~栃木県小山市


しかし、Windows起動時にファイルを走査?したりはするらしくて。となると、最近OSの起動がやたらと遅い、みたいな話が出てきそうだな…。

2014/01/16(木) [n年前の日記]

#1 [dxruby] 床ラスター処理をもうちょっと修正

DXRubyを使って床ラスター処理の実験をしていたのだけど、回転して描画したらもうちょっと色々できるんじゃないかと思えてきたので少し修正。

こんなのとか。
壁として描画
floorraster_test2.rb
# 壁として表示

require 'dxruby'
require_relative 'floorraster'

image = Image.load("wall1.png")

# 90度回転して表示するので、正方形の描画サイズを指定してる
bg = FloorRaster.new(image, 640, 640)

x = 0
z = 0
Window.loop do
  break if Input.keyPush?(K_ESCAPE)
  x -= 3
  z -= 2
  bg.draw(320, 240, x, z, :angle=>90, :y_diff=>0.05, :vflip=>false)
  bg.draw(320, 240, x, z, :angle=>90, :y_diff=>0.05, :vflip=>true)
end

こんなのとか。
壁として描画その2
floorraster_test3.rb
# 壁として表示その2。右から左へ壁が動いていく

require 'dxruby'
require_relative 'floorraster'

image = Image.load("wall2.png")
bg = FloorRaster.new(image, 640, 640)
draw_x = 0

Window.loop do
  break if Input.keyPush?(K_ESCAPE)
  draw_x -= 3
  draw_x = 640 if draw_x < 0 # 画面左端に来たら右端に戻す
  floor_dist = (draw_x - 320).quo(2000) # 床までの距離を変更
  vflip = (floor_dist < 0)? true : false # 画面右側と左側で、反転描画フラグを変更

  bg.draw(draw_x, 240, 0, 0,
          :angle=>270, # 反時計回りに90度回転
          :floor_dist=>floor_dist.abs, # 床までの距離
          :scr_dist=>0.5, # 画面までの距離
          :y_diff=>0,
          :vflip=>vflip)
end

こんなのとか。
壁として描画その3
floorraster_test4.rb
# 壁として表示その3。無限の平面ではなく、四角い壁として描画

require 'dxruby'
require_relative 'floorraster'

image = Image.load("wall1.png")

# uv値を繰り返さないモードを指定してる
bg = FloorRaster.new(image, 640, 640, false, false)

move_value = 0
move_value_max = 640
Window.loop do
  break if Input.keyPush?(K_ESCAPE)
  move_value -= 2
  move_value = move_value_max if move_value < 0
  floor_dist = (move_value - (move_value_max / 2)).quo(400)
  vflip = (floor_dist < 0)? true : false

  bg.draw(move_value, 240, 0, 0,
          :angle=>270,
          :floor_dist=>floor_dist.abs,
          :scr_dist=>0.3,
          :y_diff=>0.0,
          :v_offset=>1.1, # v値の参照位置を調整して、画面内に映るようにしてる
          :vflip=>vflip)
end

一応、ソースと画像の一式を、zipにして置いときます。Public Domain ってことで。

_floorraster.zip

フォグをかけたいのだけど。 :

遠いところがチラチラするのが気になるので、フォグもかけてみたいのだけど。

フォグってのは、遠いところは霧がかかってるような見え方にすることで、ちょっと誤魔化す(?)手法、という言い方でいいのだろうか。ググってみても分かりやすいページが出てこなくて。自分、呼称を間違えてるのかもしれず。

さておき。試しに、input.y の値を ―― 0.0〜1.0の値を使って、単純にフォグの色と差し替えたら、ほとんどの部分がフォグの色になってしまって失敗してしまった。うーん。

であれば、例えば0.1以下の場合だけフォグが働く、等の処理を書けばいいのかな。でも、HLSL内でif文を使うとめちゃくちゃ遅くなる、という話も見かけてしまったわけで。

要するに、0.0〜1.0の入力値に対して1.0〜0.0の値が出てくるけど、急激に値が減っていって、ほとんどの場合は出力値が0.0近辺でジリジリしてる、みたいな数式を書ければ良さそうだなと。でも、どう書けばいいのやら。たぶんコレ、中学生レベルの数学な気もしますけど。

GeoGebra を使ってグラフを作りつつ検討。GeoGebra の下の入力欄に、y=1/x などと打ち込むと、即座にグラフを描いてくれる。実に便利。例えば y=0.05/(0.05+x*16) と打ち込んだら、結構それっぽいグラフになってくれた。
GeoGebraでグラフ作成

一応HLSL内に、件の式を書いて、フォグを実装してみたり。結局、input.y の値でやっていて z値でやってるわけではないから、ちと不自然かもしれず。でも、多少は隠せてるから、これはこれで。

む。HLSLって、maxとかminとかそのあたりの関数もあるのか…。だったら、それを使って条件分岐に近いこともできるのかな。

一応本体部分も貼っておくです。 :

HLSL内で似たような記述がズラズラ並んでるあたり、気になるのですけど。たった1〜2行違うだけなのに、関数(?)をコピペしてるとか、なんだかアレだよなと。まとめて一つに書けないのかな…。

floorraster.rb
require 'dxruby'

#
# Shaderを使って床ラスタースクロールっぽい処理をするクラス
#
class FloorRaster

  @@hlsl = <<EOS
  float floor_distance;
  float screen_distance;
  float x_diff;
  float y_diff;
  float alpha;
  float v_offset;
  float fog_d;
  float4 fogcol;
  texture tex0;

  sampler Samp0 = sampler_state
  {
    Texture =<tex0>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
  };

  sampler Samp1 = sampler_state
  {
    Texture =<tex0>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    AddressU = WRAP;
    AddressV = BORDER;
  };

  sampler Samp2 = sampler_state
  {
    Texture =<tex0>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    AddressU = BORDER;
    AddressV = WRAP;
  };

  sampler Samp3 = sampler_state
  {
    Texture =<tex0>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    AddressU = BORDER;
    AddressV = BORDER;
  };

  float4 PS1(float2 input : TEXCOORD0) : COLOR0
  {
    float4 output;
    float pz;
    float fogv;
    fogv = fog_d / (fog_d + (input.y * 16));
    pz = screen_distance * floor_distance / -(input.y + y_diff);
    input.y = pz + v_offset;
    input.x = (input.x - x_diff) * -pz / screen_distance + 0.5;
    output = tex2D( Samp0, input );
    output *= (1.0 - fogv);
    output += (fogcol * fogv);
    output.a *= alpha;
    return output;
  }

  float4 PS2(float2 input : TEXCOORD0) : COLOR0
  {
    float4 output;
    float pz;
    float fogv;
    fogv = fog_d / (fog_d + (input.y * 16));
    pz = screen_distance * floor_distance / -(input.y + y_diff);
    input.y = pz + v_offset;
    input.x = (input.x - x_diff) * -pz / screen_distance + 0.5;
    output = tex2D( Samp1, input );
    output *= (1.0 - fogv);
    output += (fogcol * fogv);
    output.a = alpha;
    return output;
  }

  float4 PS3(float2 input : TEXCOORD0) : COLOR0
  {
    float4 output;
    float pz;
    float fogv;
    fogv = fog_d / (fog_d + (input.y * 16));
    pz = screen_distance * floor_distance / -(input.y + y_diff);
    input.y = pz + v_offset;
    input.x = (input.x - x_diff) * -pz / screen_distance + 0.5;
    output = tex2D( Samp2, input );
    output *= (1.0 - fogv);
    output += (fogcol * fogv);
    output.a = alpha;
    return output;
  }

  float4 PS4(float2 input : TEXCOORD0) : COLOR0
  {
    float4 output;
    float pz;
    float fogv;
    fogv = fog_d / (fog_d + (input.y * 16));
    pz = screen_distance * floor_distance / -(input.y + y_diff);
    input.y = pz + v_offset;
    input.x = (input.x - x_diff) * -pz / screen_distance + 0.5;
    output = tex2D( Samp3, input );
    output *= (1.0 - fogv);
    output += (fogcol * fogv);
    output.a = alpha;
    return output;
  }

  technique RasScrollWrapUV
  {
   pass
   {
    PixelShader = compile ps_2_0 PS1();
   }
  }

  technique RasScrollWrapU
  {
   pass
   {
    PixelShader = compile ps_2_0 PS2();
   }
  }

  technique RasScrollWrapV
  {
   pass
   {
    PixelShader = compile ps_2_0 PS3();
   }
  }

  technique RasScrollNotWrap
  {
   pass
   {
    PixelShader = compile ps_2_0 PS4();
   }
  }
EOS

  attr_accessor :core, :shader, :image
  attr_accessor :w, :h
  attr_accessor :tilew, :tileh
  attr_accessor :rt_w, :rt_h, :rt_scaley
  attr_accessor :rt
  attr_accessor :floor_dist, :scr_dist, :x_diff, :y_diff, :alpha
  attr_accessor :fogcol, :fog_d
  attr_accessor :v_offset

  #
  # 初期化処理
  #
  # @param [Image] image 使用する画像
  # @param [int] w 描画横幅。nilならウインドウ横幅
  # @param [int] h 描画縦幅。nilならウインドウ縦幅
  # @param [Boolean] uwrap u値をwrapするか。falseならwrapしない
  # @param [Boolean] vwrap v値をwrapするか。falseならwrapしない
  # @param [float] floor_dist 床との距離
  # @param [float] scr_dist 画面との距離
  # @param [float] y_dist 視線方向を上下にずらす量。範囲は 0.0 - 1.0
  # @param [float] v_offset v値の参照をどのくらいずらすか
  # @param [float] alpha 透明度。0.0で透明。1.0で不透明
  # @param [float] fog_d フォグのかかり具合。値を大きくするとかかり具合が大きくなる
  # @param [Array] fogcol フォグの色。r,g,b,a の並びで、0-1.0の値
  #
  def initialize(image, w=nil, h=nil, uwrap=true, vwrap=true,
                 floor_dist=0.3, scr_dist=0.3, y_diff=0.1, v_offset=0.0,
                 alpha=1.0, fog_d=0.05, fogcol=[0,0,0,0])
    
    self.core = Shader::Core.new(@@hlsl,{
                                   :floor_distance=>:float,
                                   :screen_distance=>:float,
                                   :x_diff=>:float,
                                   :y_diff=>:float,
                                   :alpha=>:float,
                                   :v_offset=>:float,
                                   :fog_d=>:float,
                                   :fogcol=>:float
                                 })

    # wrap状態を変える
    mode_str = "RasScrollWrapUV"
    mode_str = "RasScrollWrapU" if uwrap and !vwrap
    mode_str = "RasScrollWrapV" if !uwrap and vwrap
    mode_str = "RasScrollNotWrap" if !uwrap and !vwrap

    self.shader = Shader.new(self.core, mode_str)

    self.image = image
    self.w = (w == nil)? Window.width : w
    self.h = (h == nil)? Window.height : h
    self.floor_dist = floor_dist
    self.scr_dist = scr_dist
    self.y_diff = y_diff
    self.v_offset = v_offset
    self.alpha = alpha
    self.fog_d = fog_d
    self.fogcol = fogcol

    # 画面を覆い隠すタイル数を求める
    self.tilew = self.w.quo(self.image.width).ceil
    self.tileh = self.h.quo(self.image.height).ceil

    # RenderTarget の必要サイズを求める
    self.rt_w = self.image.width * self.tilew
    self.rt_h = self.image.height * self.tileh

    # y方向で拡大縮小して描画縦横幅に合わせる
    self.rt_scaley = self.h.quo(2).quo(rt_h)

    # Rendertarget生成
    self.rt = RenderTarget.new(rt_w, rt_h)

    # 画面の真ん中に消失点?を持ってくるための補正値を算出
    self.shader.x_diff = 0.5 * self.w.quo(self.rt_w)

    # フォグのかかり具合と色を指定
    set_fog(fog_d, fogcol)
  end

  #
  # フォグのかかり具合と色を指定
  #
  # @param [float] fog_d フォグのかかり具合を指定。値が大きければかかり方が大きくなる
  # @param [Array] fogcol フォグの色。r,g,b,a の並びで、0-1.0の値
  #
  def set_fog(fog_d, fogcol)
    self.shader.fog_d = fog_d
    self.shader.fogcol = fogcol
  end
  
  #
  # 描画処理
  #
  # @param [int] x 描画x座標
  # @param [int] y 描画y座標
  # @param [float] scrl_x 横方向スクロール位置
  # @param [float] scrl_z 奥行方向スクロール位置
  # @param [Hash] opts オプション
  # @option opts [float] floor_dist 床との距離。1.0前後が目安
  # @option opts [float] scr_dist 画面との距離。1.0以下が目安
  # @option opts [float] y_dist 視線方向を上下にずらす量。0.0 - 1.0 の範囲
  # @option opts [float] v_offset v値の参照をどのくらいずらすか
  # @option opts [int] z 描画奥行情報
  # @option opts [float] alpha 透明度。0.0で透明。1.0で不透明
  # @option opts [float] angle 回転角度。単位は度
  # @option opts [Boolean] vflip trueなら上下判定。falseなら反転しない
  #
  def draw(x, y, scrl_x, scrl_z, opts={})
    self.floor_dist = opts[:floor_dist] if opts.has_key?(:floor_dist)
    self.scr_dist = opts[:scr_dist] if opts.has_key?(:scr_dist)
    self.y_diff = opts[:y_diff] if opts.has_key?(:y_diff)
    self.v_offset = opts[:v_offset] if opts.has_key?(:v_offset)
    self.alpha = opts[:alpha] if opts.has_key?(:alpha)
    z = (opts.has_key?(:z))? opts[:z] : 0
    angle = (opts.has_key?(:angle))? opts[:angle] : 0
    vflip = 1
    if opts.has_key?(:vflip)
      vflip = (opts[:vflip])? -1 : 1
    end

    self.shader.floor_distance = self.floor_dist
    self.shader.screen_distance = self.scr_dist
    self.shader.y_diff = self.y_diff
    self.shader.v_offset = self.v_offset
    self.shader.alpha = self.alpha

    # Shader が参照する画像を RenderTarget に作成
    self.rt.draw_tile(0, 0, [[0]], [self.image],
                      scrl_x, scrl_z, self.tilew, self.tileh).update

    # Shader を使って透視変換しつつ描画
    wh = self.rt_w / 2
    Window.drawEx(x - wh, y, self.rt, :shader=>self.shader,
                  :scaley=>self.rt_scaley * vflip,
                  :centerx=>wh, :centery=>0,
                  :z=>z, :angle=>angle)
  end

  #
  # 動作確認用。グリッド画像を生成して返す
  #
  # @param [int] w 画像横幅
  # @param [int] h 画像縦幅
  # @return [Image] 生成した画像
  #
  def self.get_grid_image(w, h)
    image = Image.new(w, h, [0, 255, 0])
    image.box_fill(0, 0, w / 2 - 1, h / 2 - 1, [150,250,150])
    image.box_fill(w / 2, 0, w - 1, h / 2 - 1, [100,250,100])
    image.box_fill(0, h / 2, w / 2 - 1, h - 1, [200,250,200])
    image.box_fill(w / 2, h / 2, w - 1, h - 1, [0,220,0])
    return image
  end
end

# ----------------------------------------
# 以下は、使用例

if __FILE__ == $0
  image = Image.load("lena.png")
  bg = FloorRaster.new(image)
  x = 0
  z = 0
  Window.loop do
    break if Input.keyPush?(K_ESCAPE)
    x += 8
    z += 2
    bg.draw(320, 240, x, z)
  end
end

#2 [anime] ウイッチクラフトワークスのED映像が気になる

曲調がなんだか面白くて。また、ED映像中のキャラの動きが曲とピッタリ合っていて、とても好みなのですが。

ふと、各カットの切り替わるタイミングが、曲のソレより数フレーム早く変わってることに気付いたり。考えてみたら、キャラが何かしら曲に合わせてアクションをするということは、そのアクションをする前の、何もしてない状態を、事前に見せておかないといけないわけで。故に、数フレーム前にカットを切り替えておかないといかんのだなと。

で、これは勝手な想像なので、もしかしたら外してるかもしれないけど、一部分だけそういう切り替え方にしていると、他のカットの切り替え部分で感覚的に合わなくなってくるので、全体を通してカットの切替タイミングを調節して早くしてる、のかなと。

こういうところでも、プロは、細かい技・工夫を盛り込んでるのだな…。などと、感心した次第。

#3 [tv] schola 音楽の学校、シーズン4が始まってた

今回は電子音楽がテーマ。前回はテルミンを演奏して、今回はオープンリールのテープを切り張りして…。そして次回は、いよいよシンセサイザの紹介。

亀田音楽専門学校も面白かったけど、scholaもやっぱり面白いなと。こういう番組は、ありがたい…。

2014/01/17(金) [n年前の日記]

#1 [dxruby] STGサンプルを弄ってたり

STGサンプルをちょこちょこ弄っているところ。

シーン管理部分を追加中。 :

ゲームオーバーの仕様を入れるためには、少なくともタイトル画面ぐらいは必須。なので、シーン管理を追加したり。

「えっ? どうしてゲームオーバーを入れるために、タイトル画面が必要なの?」と思う人も居るかもしれない…。

ゲームオーバーになったら、次はどんな画面になるか、思い出すべし。えてして、ランキング入力画面か、タイトル画面に戻ってきますよね。

これがもし、タイトル画面すらないプログラムでは、ゲームオーバー仕様も存在できない。「区切り」が一切無かったら、ゲームオーバーと言う区切りも、あり得ない。

故に、ゲームオーバー仕様を入れるためには、タイトル画面ぐらいは必須。そしてタイトル画面を出すためには、シーン管理が必要。つまり、ゲームオーバー仕様を入れるためには、シーン管理を実装しないといかんわけで。

さておき、シーン管理の中身だけど。自分は頭が悪いので、switch〜case で ―― Rubyなら case〜when でやってしまうのですが。オブジェクト指向とやらを意識した場合は、これは望ましくないのだろうなと。まあ、そのあたりは今後の課題ってことで。

ファイルを分割中。 :

今まで、一つのファイルにずらずらと書いていたけど、さすがに見通しが悪くなってきたので、各クラスを別ファイルにする作業もし始めたり。

しかし、ちょっと悩んでしまう部分が。グローバル変数の扱いをどうすべきか、みたいな。

その手のゲームにおいては、グローバル変数ってどうしても必要になりそうな気がしていて。

例えば、敵がプレイヤーを狙って弾を撃つ、てな場合。敵は、プレイヤー座標を見て、角度を決めて、弾クラスのインスタンス生成時して、どこかに登録する、という処理をするのだけど。プレイヤー座標にしろ、オブジェクトを登録する何かにしろ、グローバル変数に近い状態になってないと、処理ができないわけで。

「自分の座標とプレイヤー座標から角度を返す」メソッドや、「発生座標や角度を与えると弾を生成・登録してくれる」メソッドを用意すればいいだろう、と言われそうだけど。そのメソッドは、色んな種類の敵から呼ばれるので、結局、変数がグローバルだったあたりが、メソッドがグローバル(?)相当になりましたよ、ぐらいの違いしか無いだろうと。

こういう場合は、どういう書き方、まとめ方をするのが、ベターなんだろう…? _Singleton を使うとか?

2014/01/18() [n年前の日記]

#1 [anime] 「生徒会役員共*」を視聴

映像中の、描き文字? 書き文字?の使い方に感心。おそらくは漫画原作の各コマの再現なのかなと思うのだけど。ここまで徹底的に挿入してくると、ある種、スタイリッシュだなと。さらに、それぞれの描き文字に必ずモーションを付加してる拘り具合も好印象。まあ、内容は下ネタだらけなので、一般の方には全くオススメできないのですが…。

昔、佐藤順一監督が、「きんぎょ注意報!」「セーラームーン」等の中で漫符を活用して、それがアニメの表現としてもフツーに定着しちゃった、てなことがあったわけだけど。アニメにおいては、漫符の活用から、更に描き文字まで活用されるところまできたのかな、てなことを考えたり。また、実写じゃちょっとやりづらい・見た目が漫画に比較的近いアニメという映像表現だからこそ使える見せ方、とも思うわけで。だったら活用しなきゃもったいない、かもしれないな、とも。

特に感心したのは、「ティッシュ持ってない?」と声優さんに喋らせると同時に、「切れちゃって」という描き文字を並べていたカット。「ティッシュ持ってない?」という台詞を喋る実時間分で、「切れちゃって」という情報までも同時に伝えてしまった ―― 直列ではなく並列で情報伝達していたわけで。これがフツーの演出家・脚本家なら、「ティッシュ持ってない? 切れちゃって」と、そのまま喋らせてしまうだろうなと。結果、実時間が増えて、おそらくは漫画原作にあるのであろうテンポの良さなんてさっぱり再現されずに間延びしてしまうはずで。

こういう時間の切り詰め方は、喋ることで情報を伝達せざるを得ない落語や漫才では実現できないよなと…。また、前述のとおり、実写映像作品でも、ちょっと難しい。もしかすると、アニメでしかできない見せ方かもしれん、と。

内容は、どこを切り取っても、実にくだらないのに…。しかし、各所で技が駆使されていて、このアニメ、スゴイ作りだなと大変感心しました。勉強になります…。や、何の勉強だか分かりませんけど。

#2 [dxruby] STGサンプルを弄ってるところ

ゲームオーバーの仕様までは入れることができた。

しかし、見た目はソレっぽいのに、敵は乱数で発生させてるので、ちっとも面白くない。やっぱり敵発生テーブルを書かないとダメかな。敵発生テーブルを作るなら、地形マップも欲しくなるなと。…この際、そこまでやってしまおうか。

2014/01/19() [n年前の日記]

#1 [dxruby] STGサンプルのマップ画像を作成中

EDGE2 でドットを打って、Tiled でタイルマップとして配置。

マップエディタについて。 :

Googleで、 _「tile editor」で画像検索 すると、それらしいマップエディタがたくさん出てくるようで。個人的には以下が気になったり。

_tIDE (Tilemap Integrated Development Environment) - Home
_Tile Studio

なんだかXNAとやらの絡みで作られてる事例が多いように見受けられたり。当然、Windows用が多い。

2014/01/20(月) [n年前の日記]

#1 [cg_tools] Tile Studio を少し試用

_Tile Studio

tIDEも少し試用。 :

_tIDE (Tilemap Integrated Development Environment) - Home
  • 保存ファイル、.tide を開いてみたら、xmlファイルっぽい感じだった。これなら他のアレコレに流用できそう。
  • 全般的に動作が遅い。例えば、コピー&ペーストをしたら、数秒経ってから処理が行われた。フリーズしたのかと勘違いするぐらいに遅い。
  • BGの一部をアニメーションさせる仕様まで入ってるあたりは、ちょっと面白そうな気がする。xTile PC Demo v2.0.7 をDL・実行すると、その様子が確認できる。

DXRubyでタイルマップBGの一部をアニメーションさせるとしたら…どういう手があるのだろう。マップの並びを管理してる配列内を書き換えればできるのかな? それだけじゃ無理?

#2 [prog][dxruby] 地形アタリについてヨサゲな記事に遭遇

マップエディタその他で検索してたら、地形アタリ処理について解説してる英文記事に遭遇。

_The guide to implementing 2D platformers | Higher-Order Fun

2Dゲームにおける地形アタリ判定について、大別して4種類あるよ、と解説してる。斜め床や、動く床についても少し解説してるあたり、勉強になりそう。

やっぱり、ソニックの挙動は簡単に説明できるものではないらしい。

#3 [dxruby] STGサンプルでマップ表示できるようにした

Tiled で作成した .tmx を読み込んで、表示するようにした。 _2013/12/25の日記 に置いといた、dxrbtmx.rb と bgatari.rb を使って、.tmx を表示、かつ、BGアタリを取ってみたり。

ついでに、マップに Object Layer を追加して、敵の発生位置を指定できるようにしてみたり。dxrtmx.rb に、以下のような処理を追加して、Object Layer の情報を読み取ってみたりして。
  #
  # Object Layer 情報を配列にして返す
  #
  # @return [Array] Object Layer 情報の配列
  #
  def get_object_group_info
    a = []
    @tmx.object_groups.each do |objg|
      b = []
      objg.objects.each do |o|
        b.push({:name=>o.name, :x=>o.x, :y=>o.y})
      end
      a.push({:layername=>objg.name, :data=>b})
    end
    return a
  end
ただ、敵の種類・プロパティを、どのように指定したらいいのか、そのあたりで少し悩んでいたりもして。今現在、 _Rubyのtmxライブラリ は、Object Layer名と、各Object の名前(.name)、座標(.x と .y)を取得できることは分かってるので、とりあえず文字列で種類判定しちゃってるけど。プロパティも参照できるなら、そこで指定したほうが良さそうな。

最後にスクロールが止まるようにしたのだけど。そうなると、やっぱり最後にボス敵が出てこないと、ゲームっぽくないなと…。この際、そこも作ってしまおうか…。しかし、仕様が思いつかない…。

2014/01/21(火) [n年前の日記]

#1 [anime] バディ・コンプレックス2話を視聴

空中戦映像が凄いなあ…。背景に3DCGを使うと、こんなにも空間が感じられる映像になるのかと感心したり。 見ていて思わず「おおう…」と唸ってしまいました。それほど難しい使い方じゃないだろうと想像するのだけど、得られる効果は絶大だなと。これはコンテを描いた人が「技有り」なんだろうか。それとも演出の人が「技有り」なんだろうか。

主人公が巨大ロボットを動かすために用意された設定について。 :

カップリングをすると、相手の持ってる 知識を共有できるという設定を見て、なんだかパシフィック・リムを連想したり。アレからのインスパイアなのかなと邪推。もっとも、ド素人の主人公が早々に活躍するための設定としては便利だなと感心したりもして。この設定は、かなり美味しい気がします。

一般人であったはずの主人公が、巨大ロボットをサクサク動かして活躍する、てな男の子の願望っぽい展開を実現するために、ロボットアニメは色々設定を工夫してるわけだけど。例えば1stガンダムでは、以下のような設定が盛り込んであって。
  • アムロは機械弄りが大好きなオタク少年。
  • アムロの父親は、MSの開発者。おそらく家には、基礎を学べる資料・書籍がそこそこあって、アムロはそれを目にしていたはず。
  • ガンダムにはマニュアルがついてる。
  • ガンダムは学習型コンピュータを積んでいるので、次第に性能が上がっていく。
「こんだけ理由があれば、動かしてもおかしくないだろ?」と。

FSS、というかエルガイムあたりは、巨大ロボットを動かすなんて人間一人じゃ無理だろうから、細かいアレコレをやってくれる専用の生体コンピュータ載せましょうか、と。で、せっかく載せるなら、可愛い女の子がいいよね、と。てなわけで、ファティマという設定が出来上がって、そこから更に、パイロットとファティマの関係、みたいな別の展開も見せられるようになっていくわけで。…いや、アレは、巨大ロボットに女の子を載せたいがために捻り出した設定で、考えた順番は逆かもしれんけど。

ということで。「ド素人の主人公が、巨大ロボットをいきなりガンガン動かしちゃうためには」てな設定を妄想すると、なんか別のところにも繋がる面白い案が出てくるかもしれませんなと。

バディ・コンプレックスも、 知識が共有されるということは、記憶も共有されてしまう展開もあり得るだろうし。すると、それはつまり、自分と相手の自我の境界が怪しくなってきて、みたいな攻殻機動隊SACっぽい展開だって、その気になればやれそうだよなと。

例えば、元々主人公は巨乳好きだったのに、カップリングした相手がロリコンだったものだから、出撃を繰り返すうちに気づいたら貧乳好きになってて苦悩するとか。って別にそんなことでは苦悩しないか…。

#2 [dxruby] STGサンプルで砲台を作ってたり

地形がスクロールしていくのだから、砲台ぐらいは欲しいよなと。てなわけで、EDGE2で砲台のドット絵を描いて、敵として追加。

昔だったら、角度が異なる砲台のドット絵を何パターンか描いて、左右反転描画や上下反転描画をして〜みたいなことをしてただろうけど。せっかく回転描画が使えるのだから、砲台部分と砲塔部分を別に分けて、砲塔だけを回転させて対応したり。

しかし、これはこれで、なんか見た目が悲しい感じ。完全に2Dになってるというか…。角度の違うパターンをちゃんと描くやり方なら、角度は意図通り、かつ、手前を向いてる砲台、みたいなものも描けるわけで。昔のゲームは、そういうところで、制限を逆に利用して、見た目のゴージャス感を出してたのかな、などと今頃気付いたり。

自機を狙って砲塔が回転するあたりの処理で、少し悩んだりもして。360度ぐるぐる回る砲台ではアレなので、砲塔が回転する角度には制限をつけないといけないのだけど。上下左右に配置するから、ちと頭がこんがらがってくる…。

2014/01/22(水) [n年前の日記]

#1 [dxruby] まだ砲台を作ってたり

自機に砲塔を向ける際に、ピッタリ正確な角度で向けちゃうのもなんだかアレだよなと。360度を、12方向とか24方向とかに制限したほうがいいのかも。てなわけでそのあたりの処理を。
    #
    # 角度をn方向に制限して返す
    #
    # @param [float] ang 元の角度
    # @param [float] rest 何方向にするか。デフォルトは16
    # @return [float] rest方向に制限した角度
    #
    def get_dir_rest(ang, rest=16)
      d = 360.0 / rest
      dir = ((ang + (d / 2)) % 360.0).div(d)
      return dir * d
    end
こんな感じで合ってるのだろうか。動作確認したら、一応合ってるっぽいけど。

砲塔の向く角度に制限を加えるあたりでまだ悩んでたり。上下左右に配置したそれぞれの場合の処理をベタに全部書いちゃう、というソレで一応それらしく動いてはいるものの。もっとスマートにかけるはずだよなと…。

2014/01/23(木) [n年前の日記]

#1 [dxruby] 砲台はなんとかなってきた

とりあえず、砲台を動かすために書いた処理を、別ファイルにまとめたり。

_gamemath.rb
# ゲーム関係の計算用メソッド
#
# ruby gamemath.rb 1 といった指定で、テスト種類を変えられる

require 'dxruby'

# 計算用メソッド
class GameMath

  class << self

    #
    # 度からラジアンへの変換
    #
    # @param [float] deg 角度。単位は度
    # @retrun [float] 角度。単位はラジアン
    #
    def deg2rad(deg)
      return (deg * Math::PI / 180.0)
    end

    #
    # ラジアンから度への変換
    #
    # @param [float] rad 角度。単位はラジアン
    # @retrun [float] 角度。単位は度
    #
    def rad2deg(rad)
      return (rad * 180.0 / Math::PI)
    end

    #
    # 基準座標から目標座標への角度を返す
    #
    # @param [int] tgt_x 目標座標x
    # @param [int] tgt_y 目標座標y
    # @param [int] base_x 基準座標x
    # @param [int] base_y 基準座標y
    # @return [float] 目標座標までの角度。単位はラジアン
    #
    def get_dir_rad(tgt_x, tgt_y, base_x, base_y)
      tx = tgt_x - base_x
      ty = tgt_y - base_y
      return Math.atan2(ty, tx)
    end

    #
    # 角度をn方向に制限して返す
    #
    # @param [float] ang 元の角度
    # @param [float] rest 何方向にするか。デフォルトは16
    # @return [float] rest方向に制限した角度
    #
    def get_dir_rest(ang, rest=16)
      d = 360.0 / rest
      dir = ((ang + (d / 2)) % 360.0).div(d)
      return dir * d
    end

    #
    # 角度と速度(距離)からx,y方向の移動量を求める(度を与える版)
    #
    # @param [float] ang 角度。単位は度
    # @param [float] spd 速度(距離)
    # @param [float] bx  原点となる座標x。省略時は0
    # @param [float] by  原点となる座標y。省略時は0
    # @return [Array] 移動量x,y
    #
    def get_vec(ang, spd, bx=0, by=0)
      return GameMath.get_vec_rad(GameMath.deg2rad(ang), spd, bx, by)
    end

    #
    # 角度と速度(距離)からx,y方向の移動量を求める(ラジアンを与える版)
    #
    # @param [float] ang 角度。単位はラジアン
    # @param [float] spd 速度(距離)
    # @param [float] bx  原点となる座標x。省略時は0
    # @param [float] by  原点となる座標y。省略時は0
    # @return [Array] 移動量x,y
    #
    def get_vec_rad(rad, spd, bx=0, by=0)
      x = spd * Math.cos(rad) + bx
      y = spd * Math.sin(rad) + by
      return x, y
    end

    #
    # 外積を返す
    #
    # @param [int] tgt_x 目標ベクトルx
    # @param [int] tgt_y 目標ベクトルy
    # @param [int] base_x 基準ベクトルx
    # @param [int] base_y 基準ベクトルy
    # @return [float] 外積。
    #                 外積 > 0 なら、目標は右にあり、
    #                 外積 < 0 なら、目標は左にある
    #                 外積 = 0 なら、同じ方向
    #
    def get_cross_product(tgt_x, tgt_y, base_x, base_y)
      return base_x * tgt_y - base_y * tgt_x
    end

    #
    # 徐々に目標方向を向く角度を求める
    #
    # @param [float] ang 現在の角度。単位は度
    # @param [float] ang_spd 角度変化量。単位は度
    # @param [float] tgt_x 目標座標x
    # @param [float] tgt_y 目標座標y
    # @param [float] base_x 基準座標x
    # @param [float] base_y 基準座標y
    # @return [float] 新しい角度。単位は度
    #
    def get_chase_angle(ang, ang_spd, tgt_x, tgt_y, base_x, base_y)
      ang %= 360
      tgt_ang = GameMath.rad2deg(GameMath.get_dir_rad(tgt_x, tgt_y, base_x, base_y))
      tgt_ang %= 360
      d = tgt_ang - ang
      d += 360 if d < -180
      d -= 360 if d > 180
      if d > ang_spd
        ang += ang_spd
      elsif d < -ang_spd
        ang -= ang_spd
      else
        ang = tgt_ang
      end
      return ang
    end

    #
    # 指定範囲に収まった角度を返す
    #
    # @param [float] now_ang 現在の角度。単位は度
    # @param [float] base_ang 基準角度。単位は度
    # @param [float] min_d 角度範囲の最小値算出用。基準角度に加算する値
    # @param [float] max_d 角度範囲の最大値算出用。基準角度に加算する値
    # @return [float] 範囲に収まった角度。単位は度
    #
    def get_dir_range(now_ang, base_ang, min_d, max_d)
      base_ang %= 360
      now_ang %= 360

      # 現在角度が、基準角度の正反対の角度を超える角度だったら補正する
      if now_ang > base_ang + 180
        now_ang -= 360
      elsif now_ang <= base_ang - 180
        now_ang += 360
      end

      min_ang = base_ang + min_d
      max_ang = base_ang + max_d
      now_ang = min_ang if now_ang < min_ang
      now_ang = max_ang if now_ang > max_ang
      return now_ang
    end

    #
    # 2点の座標間の距離が指定距離より小さいかを返す
    #
    # @param [int] tx 目標座標x
    # @param [int] ty 目標座標y
    # @param [int] bx 基準座標x
    # @param [int] by 基準座標y
    # @param [int] dist 判定距離
    # @return {Boolean] trueなら、判定距離内に入ってる。falseなら、判定距離外
    #
    def check_dist(tx, ty, bx, by, dist)
      w = tx - bx
      h = ty - by
      return (w * w + h * h <= dist * dist)? true : false
    end

  end
end

if $0 == __FILE__
  # 動作確認

  # 動作種類を変える
  if ARGV.length >= 1
    test_kind = ARGV[0].to_i
  else
    test_kind = 0
  end

  font = Font.new(16)
  fh = 20

  bx = 320
  by = 240
  ang = 0
  base_ang = 0

  Window.loop do
    break if Input.keyPush?(K_ESCAPE)
    px, py = Input.mousePosX, Input.mousePosY

    case test_kind
    when 0
      # 方向(角度)に制限を加えるテスト
      # n方向に制限する

      n = 12
      rad = GameMath.get_dir_rad(px, py, bx, by) # 本来の角度
      new_deg = GameMath.get_dir_rest(GameMath.rad2deg(rad), n) # 制限した角度

      # 描画
      d = 360 / n
      s = d / 2
      n.times do |i|
        nx, ny = GameMath.get_vec(s + d * i, 80, bx, by)
        Window.drawLine(bx, by, nx, ny, C_RED)
      end

      nx, ny = GameMath.get_vec(GameMath.rad2deg(rad), 200, bx, by)
      Window.drawLine(bx, by, nx, ny, C_CYAN)

      rad = GameMath.deg2rad(new_deg)
      nx, ny = GameMath.get_vec(GameMath.rad2deg(rad), 150, bx, by)
      Window.drawLine(bx, by, nx, ny, C_YELLOW)

      Window.drawFont(0, 0, "#{n}方向にする制限を加える例", font)

    when 1
      # 外積を使って目標を追尾するように角度を変えるテスト
      # プルプル震えてしまう

      ang_spd = 3

      # 目標ベクトル
      tx = px - bx
      ty = py - by

      # 基準ベクトル
      vx = Math.cos(GameMath.deg2rad(ang))
      vy = Math.sin(GameMath.deg2rad(ang))

      # 外積を求める
      cp = GameMath.get_cross_product(tx, ty, vx, vy)

      # 外積の符号を見て、目標が右にあるか左にあるか判別
      ang += ang_spd if cp > 0
      ang -= ang_spd if cp < 0
      ang %= 360

      # 描画
      nx, ny = GameMath.get_vec(ang, 200, bx, by)
      Window.drawLine(bx, by, nx, ny, C_CYAN)

      Window.drawFont(0, 0, "外積の符号で追尾する例", font)

    when 2
      # 目標への角度を求めて、追尾するように角度を変えるテスト

      ang = GameMath.get_chase_angle(ang, 2, px, py, bx, by)

      # 描画
      nx, ny = GameMath.get_vec(ang, 200, bx, by)
      Window.drawLine(bx, by, nx, ny, C_CYAN)

      Window.drawFont(0, 0, "角度を見て追尾する例", font)

    when 3
      # 角度に制限を加える例

      base_ang += Input.x * 2 # 基準角度を左右キーで変更

      rad = GameMath.get_dir_rad(px, py, bx, by) # 本来の角度
      ang = GameMath.rad2deg(rad)

      # 基準角度からプラスマイナス aw の範囲に角度を制限する
      aw = 90
      ang = GameMath.get_dir_range(ang, base_ang, -aw, aw)

      # 描画
      nx, ny = GameMath.get_vec(base_ang, 250, bx, by)
      Window.drawLine(bx, by, nx, ny, C_RED)
      nx, ny = GameMath.get_vec(base_ang - aw, 150, bx, by)
      Window.drawLine(bx, by, nx, ny, C_YELLOW)
      nx, ny = GameMath.get_vec(base_ang + aw, 150, bx, by)
      Window.drawLine(bx, by, nx, ny, C_YELLOW)

      nx, ny = GameMath.get_vec(ang, 200, bx, by)
      Window.drawLine(bx, by, nx, ny, C_CYAN)

      Window.drawFont(0, 0, "角度に制限を加える例 : 左右キーで角度変更", font)
    end
  end
end
各スクリプトに直接書いてしまってもいいぐらいのソレばかりですが…。

単体でも動くけど、他のファイルから呼んで使うこともできるはず。Public Domain ってことで。

ruby gamemath.rb 0 とか ruby gamemath.rb 3 とか打てば、下のような画面が出るので、マウスカーソルを動かして動作確認できるかと。0〜3 まで指定可能。

動作確認その1

動作確認その2

動作確認その3

動作確認その4

以下のページが参考になりました。ありがたや。

_[ Azure Contrail ] アズールコントレイル - Tips
_Flashゲーム講座&ASサンプル集【狙撃の計算方法について】

ちなみに。DXRuby開発版なら、外積とか内積とかそのへん求めるメソッドもあるのですが…。開発版は、常時最新版しか公開されてなくて、入手にちょっと難があるので、そのあたり、DXRuby 1.4.1 に取り込まれることを期待。

DXRubyの便利なところ。 :

ここ数日、DXRubyを触っていて「便利だなー」と思った点が。メインループが数行で済む点は、ありがたい。おかげで、各ファイルの動作テストが書きやすいなと。

フツー、スクロールゲーム用のアレコレを書く場合は…。
  1. ゲーム本体のプログラムを動かして、
  2. 目的の場所までスクロールさせて、
  3. そこで出てくる敵その他の動きを確認して、
  4. 敵のプログラムを調整して、
みたいな流れになるのですが。

DXRubyなら、敵のプログラムが書いてあるスクリプトファイルに、ちょこっと動作確認用のメインループを書いちゃえば、そのスクリプトだけで動きの確認がそこそこできるわけで。

特に Ruby の場合、以下のような書き方が可能で。
if $0 == __FILE__

  # このスクリプトを単体で動かした時だけ、この部分を通る

end
まあ、Python なども、同じことができますけど…。何にせよ、この部分に動作確認用のソレを書いとけば、そのスクリプトファイルをそっくりそのまま、本体プログラム側で使うこともできてしまう。

ということで、Ruby + DXRuby は、プロトタイプ作成に便利だなと再認識。

2014/01/24(金) [n年前の日記]

#1 [dxruby] ボス敵作成中

EDGE2でボス敵っぽいドット絵を。しかし、どうにも平面っぽい…。どうして自分がドット打ちすると立体感が全く出ないのだろう…。

それでもひとまず一応それらしく見えなくもないっぽい画像が出来たので、スクリプトを書き始めたり。

各パーツの位置合わせがなんだか面倒。本来こういうのはCGツールでどうにかしたほうがいいような。

2014/01/25() [n年前の日記]

#1 [tv] スコラ、シンセサイザーの回を視聴

録画してたソレを消化。

学生さんにモーグシンセサイザーを ―― 通称「箪笥」を触らせるコーナーが面白かった。音を出す仕組みについて、ローパスフィルター、ハイパスフィルター、レゾナンスのあたりを簡単に説明していて。かなり分かりやすい説明映像になってたなと。また、シーケンサーやサンプラーについてもちょこっと触れていたりして。

自分がそのあたりに興味を持ったのは、8bit PCに触った頃で。MZ-700は矩形波単音しか出なかったけど、一応、音楽の自動演奏ができたので、そこから興味を、みたいな。月刊マイコンに、音源モジュールの広告が載ってたりして、「コレを買えばスゴイ音が出るのかな…」とか想像してたっけ。それが今はソフトウェアで波形作れるんだから…。しかも下手するとブラウザ上で動いちゃうし。

何はともあれ、初音ミクの大先輩が実際に動いてる様子を目にすることができて満足。

#2 [dxruby] ボス敵の爆発エフェクトで試行錯誤中

ド派手な爆発にしたいのだけど、どうもなんだか、どことなく大人しい…。

透視変換をしてスプライトを飛ばしてみたものの、画面が真っ白になってるので、見た目ほとんど分からず。こんなはずでは…。

2014/01/26() [n年前の日記]

#1 [dxruby] ステージクリアロゴを表示させてみたり

画面外からロゴが1文字ずつ飛んできて、また画面外に飛んでいく、みたいな動きにしてみたけど。メガドライブ時代のソレをわざわざ再現せんでも、てな気もしてきたり。拡大縮小回転半透明加算減算自由変形、なんでも使えるのだから、もっとゴイスな見せ方ができそうな気もするのだけど。

何にせよ、ゲームオーバーもステージクリアも入ったから、メインの流れはゲームっぽい感じになったような気がする。

せっかくだから、サウンドも入れたほうがいいのかな。さて、どうしたもんか。

2014/01/27(月) [n年前の日記]

#1 [dxruby] タイトル画面をグネグネするアレを書いてみたり

DXRuby の Shader を使って、タイトル画面がグネグネするアレを書いてみたりして。

タイトル画面がグネグネするアレ

与えるパラメータの適切な値がよく分からなくて、オリジナルのソレとはどうも動きが違うのですけど…。それでもまあ、原理と言うか、仕組みとしては、こういう感じなのかなと。

_mbraster.rb
require 'dxruby'

#
# Shaderを使ってMETALBLACKのタイトル画面っぽい処理をするクラス
#
class MbRaster

  @@hlsl = <<EOS
  float ang;
  float lvl;
  float spd;
  float alpha;
  texture tex0;

  sampler Samp = sampler_state
  {
    Texture =<tex0>;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    MipFilter = LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
    // AddressV = BORDER;
  };

  struct PixelIn
  {
    float2 UV : TEXCOORD0;
  };
  struct PixelOut
  {
    float4 Color : COLOR0;
  };

  PixelOut PS1(PixelIn input)
  {
    PixelOut output;
    // input.UV.x = input.UV.x + sin(radians(input.UV.x * 360 * spd + ang)) * lvl * 0.25;
    input.UV.y = input.UV.y + sin(radians(input.UV.y * 360 * spd - ang)) * lvl;
    output.Color = tex2D( Samp, input.UV );
    output.Color.a *= alpha;
    return output;
  }

  technique MbRasScroll
  {
   pass P0
   {
    PixelShader = compile ps_2_0 PS1();
   }
  }
EOS

  attr_accessor :core, :shader, :image, :w, :h, :tilew, :tileh
  attr_accessor :rt, :rt_w, :rt_h, :rt_scaley

  #
  # 初期化処理
  #
  # @param [Image] image 使用する画像
  # @param [int] w 描画横幅。nilならウインドウ横幅
  # @param [int] h 描画縦幅。nilならウインドウ縦幅
  #
  def initialize(image, w=nil, h=nil)
    self.core = Shader::Core.new(@@hlsl,
                                 {:ang=>:float, :lvl=>:float,
                                   :spd=>:float, :alpha=>:float})
    self.shader = Shader.new(self.core, "MbRasScroll")

    @image = image
    @w = (w == nil)? Window.width : w
    @h = (h == nil)? Window.height : h

    # 画面を覆い隠すタイル数を求める
    @tilew = @w.quo(@image.width).ceil
    @tileh = @h.quo(@image.height).ceil

    # RenderTarget の必要サイズを求める
    @rt_w = @image.width * @tilew
    @rt_h = @image.height * @tileh

    # y方向で拡大縮小して描画縦横幅に合わせる
    @rt_scaley = @h.quo(@rt_h)

    # Rendertarget生成
    @rt = RenderTarget.new(@rt_w, @rt_h)
  end

  #
  # 描画処理
  #
  # @param [int] x 描画位置x
  # @param [int] y 描画位置y
  # @param [float] u 横方向スクロール位置
  # @param [float] v 縦方向スクロール位置
  # @param [float] ang sinの開始角度。単位は度
  # @param [float] lvl sinの振幅幅
  # @param [float] spd sinの角度変化量
  # @param [Hash] opts オプション
  # @option opts [int] z 描画奥行情報
  # @option opts [float] alpha 透明度。0.0で透明。1.0で不透明
  # @option opts [Object] blend 合成方法。:add や :alpha が使えるはず
  #
  def draw(x, y, u, v, ang, lvl, spd, opts={})
    alpha = (opts.has_key?(:alpha))? opts[:alpha] : 1.0
    blend = (opts.has_key?(:blend))? opts[:blend] : :alpha
    z = (opts.has_key?(:z))? opts[:z] : 0

    self.shader.ang = ang
    self.shader.lvl = lvl
    self.shader.spd = spd
    self.shader.alpha = alpha

    # Shader が参照する画像を RenderTarget に作成
    @rt.draw_tile(0, 0, [[0]], [@image], u, v, @tilew, @tileh).update

    # Shader を使って描画
    Window.drawEx(x, y, @rt, :shader=>self.shader,
                  :scaley=>@rt_scaley, :z=>z, :blend=>blend)
  end

  #
  # 動作確認用。グリッド画像を生成して返す
  #
  # @param [int] w 画像横幅
  # @param [int] h 画像縦幅
  # @return [Image] 生成した画像
  #
  def self.get_grid_image(w, h)
    image = Image.new(w, h, [0, 255, 0])
    image.box_fill(0, 0, w / 2 - 1, h / 2 - 1, [150,250,150])
    image.box_fill(w / 2, 0, w - 1, h / 2 - 1, [100,250,100])
    image.box_fill(0, h / 2, w / 2 - 1, h - 1, [200,250,200])
    image.box_fill(w / 2, h / 2, w - 1, h - 1, [0,220,0])
    return image
  end
end

# ----------------------------------------
# 以下は使用例

if __FILE__ == $0

  image = Image.load("logo.png")
  bg = MbRaster.new(image)

  x = 0
  y = 0
  u = 0
  v = 0
  ang = 360
  lvldef = 0.35
  lvl = lvldef
  spd = 1.0
  cnt = 0

  font = Font.new(14)

  Window.loop do
    break if Input.keyPush?(K_ESCAPE)

    # マウスカーソル座標でパラメータを変えてみる
    ang = Input.mousePosX * 360 / 640.0
    lvl = Input.mousePosY / 240.0  - 1.0
    # spd = Input.mousePosY / 480.0

    bg.draw(x, y, u, v, ang, lvl, spd)
    # u += 1
    # v += 2

    Window.drawFont(4, 4, "ang=#{ang} , lvl=#{lvl} , spd=#{spd}", font)
    Window.drawFont(4, 20, "Please move mouse cursor", font)
    cnt += 1
  end
end
テスト用の画像も置いときます。

_logo.png

スクリプト単体でも実行できるけど、他のスクリプトから呼び出して使うこともできるはず。

スクリプトも画像も、Public Domain / CC0 ってことで。

#2 [dxruby] DXRubyでSTGサンプルを作成

とりあえず、ゲームとしての全体の流れはできたような気がするので、一応公開。

ソースは、Ruby初心者が書いたソレだから、もうグチャグチャで全然参考にならないと思いますが、でもまあ、「DXRubyを使えばこういう感じのゲームも作れそうですよ」という一つの事例・サンプルにはなるかもしれない、と。

ScreenShot0

ScreenShot1

ScreenShot2

ScreenShot3

Windows、かつ、 _Ruby_DXRuby_tmxライブラリ がインストール済みの環境なら、解凍して、ruby main.rb で実行できる…はずです。

_stg_sample01.zip (ソースと画像一式。2.1MB)

Ruby等をインストールしてない環境でも動かせるように、exe化した版も置いときます。(※ 2014/02/02、ドキュメントも同梱)

_stg_sample01_exe_20140202.zip (実行形式。6.4MB)

解凍して、main.exe をダブルクリックすれば、実行できるかなと。一応、RubyをインストールしてないサブPC上でも、動作することは確認しました。

ソースも画像も、Public Domain / CC0 ということで。

ただ、exe版のほうは、tmxライブラリその他を含んでしまってるので、ライセンスはどうなるのか分かりません…。まあ、DLして動作確認する程度なら怒られないのでは、てな気もするのですけど。MITライセンス等のライブラリを実行形式に含む場合は、どういう条件を満たしておけばいいんだろう?

結局、サウンドはつけませんでした。手持ちの素材の中にソレっぽいデータが既にあれば鳴らしてたところなのだけど、どれもなんだかSTG向けのデータじゃなくて…。

画像とBGマップについて。 :

画像とBGマップは、 _OpenGameArt.org にもアップしておきました。ライセンスは CC0 にしておきましたので、自由に使ってください。マップデータの tmx ファイルも入ってますので、 _Tiled Map Editor を使って、マップや敵発生位置を変更・調整したら、そこそこ遊べるゲームになったり…するのかな。どうなんだろう。うーん。

_STG Object Image | OpenGameArt.org
_Side scroll STG BG tile map | OpenGameArt.org

所詮、プログラマーが作った画像なので、どれも正直、出来はイマイチですけど。それでもまあ、サンプルで何かのソースを書く際、テスト用画像としては一応使えるんじゃないのかなと。でも、やっぱり自機が、あまりにもダサイ…。モブ子さんを飛ばしておいた方が良かったのだろうか…?

DXRuby開発版について。 :

※ 2014/02/02追記。 _DXRuby プロジェクトWiki - ファイル置き場 で、過去のDXRuby開発版も公開していただけるようになってました。作者様、対応ありがとうございます。なので、以下は古い情報です。


手元の環境では、Ruby 1.9.3 + DXRuby 1.5.8dev で動かしてたのですけど。

今現在、DXRuby の Wiki から入手できる DXRuby開発版は、Ruby 2.0対応版のみなので…。念のため、自分がDLして手元に残してた、Ruby 1.9対応のDXRuby開発版を、Dropbox にも(勝手に)置いときます。コレ、何か問題があったら、言ってもらえれば消しときますので…。

_dxruby158dev-test-mswin32-ruby19.zip
_dxruby157dev-mswin32-ruby19.zip
_dxruby156dev-mswin32-ruby19.zip

1.5.8dev は .so ファイルしか入ってないので…。1.5.7dev 版もDL・解凍して、.so だけを 1.5.8dev のソレで上書きしてから、ruby install.rb すればインストールできた…ような気がするけど記憶が怪しいです。

ちなみに上記は、Ruby 1.9.x を使ってる場合の話で…。Ruby 2.0 環境なら、 _DXRuby プロジェクトWiki - ファイル置き場 から開発版が入手できますので、そちらを導入してみてもらえればと。

余談。DXRuby開発版って、バージョンによってはバグがあったりするものの、機能がかなり強化されてるので、できれば過去の版もどこかしらで公開しておいてくれると大変ありがたいのですけど。

ていうか、過去版が入手できないと、バグを見つけたとしても、どの版でエンバグしたのかエンドユーザレベルで検証作業ができないことに今頃気づいたりもして。…いや待て。もしかして、SourceForge のソースを眺めて云々、とかすればいいのだろうか。しかしソレ、ちょっとレベル高過ぎて、自分如きでは無理っぽい予感。

tmxライブラリについて。 :

tmxライブラリは、ネットに繋がってる環境で、
gem install tmx
をすれば導入できるはず、です。

2014/02/02追記。 :

コメント欄で、「MITライセンスのライブラリも含める場合は、ドキュメントのどこかに『○○ライブラリ使ってるよ』と書いてけば、まあ大体の場合はヨサゲ」と教えていただいたので、exe版にドキュメントを同梱して、そこに明記しておきました。たぶんこれで問題無い…はず。アドバイス、ありがとうございます。

2017/03/19追記。 :

Dorpboxのpublicフォルダが死んだのでファイルの置き場所を変更。

この記事へのツッコミ

Re: DXRubyでSTGサンプルを作成 by mirichi    2014/01/30 22:36
mirichiです。
tmxはMITライセンスですが、これを含む場合の条件については人によって色々な感じです。
例えばmrubyはMITライセンスで、使う場合にはライセンス表記も特にいらないという立場です。
DXRubyは元MITライセンスで、俺個人の解釈では文面からするとライセンス表記が必要なはずだということで、それが必要無いzlib/libpngライセンスに移行しました。http://ja.wikipedia.org/wiki/MIT_License
これの1に該当するという話ですね。
たぶん、MITライセンスを選択するような人はライセンス表記うんぬんで怒ることは無いと思います。tmxを含むバイナリを配布する場合は「tmx使ってまーす」って書いておけばよいぐらいじゃないでしょうか。

2014/01/28(火) [n年前の日記]

#1 [anime] マギ、魔法学園の仕組みの回を視聴

なかなかイイ感じの設定だなと感心。パンピーは権威欲だの名誉欲だの征服欲だのを優先するが、魔術師? 魔導士? は知識欲を優先する、故にパンピーからまんまと利用され、しかも虐げられてしまうのだ、云々のあたり、なんだか技術者の属性を連想してしまってモヤモヤと。

最初から悪いことをしようとして悪の組織を作ってしまうより、世の中を良くしようとして試行錯誤してるうちに悪の組織になるほうが説得力あるよなと…。 ラスボス?に全然悪気が無くて、むしろ善行をしてるつもりなあたりも…。ていうか現実世界にもそういう人がたくさん居るわけだけど…。

しかし、なんでまたここに来て、2話連続放送をしたんだろう。何か放送スケジュールが崩れるようなことあったっけ? でも、内容的に、2話連続が良い方向に働いていたような気も。

金髪美少年魔術師が泣くところの演技に感心したのだけど。気になって調べてみたら、SAO(ソードアートオンライン)の主役をやってた方が担当してたらしくて。SAOの時も、慟哭する演技が上手いのではと思った記憶が。この声優さん、こういう演技をさせたらさすが、なのではないのかなあと個人的には思えてきました。

#2 [anime][neta] 「エビバデカモッ!」ってどういう意味なんだろう

「スペース☆ダンディ」には公式吹替え版が存在するという話を見かけて、すると岡村靖幸氏が歌うあのOP曲も各国語版が存在するのであろうか、あるいは別の曲と差し替えられてたりするのだろうか、みたいなことを妄想したのですけど。

そこで、ふと、OP曲中で叫んでる「エビバデカモッ!」って、どういう風に訳されるのかなと疑問を持ったわけで。

そもそも、「エビバデカモッ!」って、英語ではなんて言ってるんだろう? さらに、ソレを日本語に訳すと、どういうフレーズになるんだろう? …日本語の歌詞に英語の合いの手が入っているのだから、英語の歌詞にした時は、日本語の合いの手が代わりに入らないといかんよなと。

ググってみたら、「Everybody Come On」と叫んでるっぽい。Everybody は、「皆さん」「皆」とかそういう意味? Come on は、「来いよ!」「さあ行こう!」とかそういう意味なのかな? だとしたら、日本語に置き換えると…。できれば、「エビバデカモッ!」と同じようなリズムのフレーズがヨサゲだけど、ちょっと思いつかない。

たぶん、日本のミュージシャンの誰かしらが、このあたりのクールな訳、カッコよくてテンポがいいフレーズを、既に発明・発見してそうな予感もあるのですが。

全然意味を変えちゃって、「エビテンドンッ!」とか「エビテンプラソバッ!」とか合いの手を入れるのも面白いかな、てなことを思ったりもしたけど。坂本九の _「SUKIYAKI」 みたいな感じで。…ちと無理があるか。

「ビバ」ってどういう意味なんだろう。 :

「ビバナミダ」も、英語版等を作るなら、日本語+英語その他にしないといかん気がする。しかし、「ビバ」ってどういう意味なんだろう。

気になってググってみたら、そもそも「ビバ」はイタリア語だったらしい…。

_「ビバ」とはどういう意味か?。 - 外国語 - 教えて!goo
すでに回答そのものは出ていますが、イタリア語でviva、喜びを表すバンザイ!です。文字として書く時は、よくWと書きます(正確には、真ん中の部分を交わらせて)。イタリアに行くと、壁の落書きに
W LA ROMA!
などと書いてあります。サッカーのローマサポータが書いたものですね。

イタリア人も日本語の「バンザイ」という言葉をけっこう知っています。が、イタリアに限らず欧米では「Banzai」=「降参」だと思っている人が多数。両手を上げて投降してくる姿と勘違いしているみたいです。

「ビバ」とはどういう意味か?。 - 外国語 - 教えて!goo より

なるほど、「万歳」という意味だったのか…。

すると、「ビバナミダ」の英語版があるとしたら、「バンザイ _ラクリマ 」と歌ったりするのだろうか? それはそれでなんだか面白そう。でもないか。

#3 [unity] Unity2D勉強中

Unity 4.3 以降は2Dゲームを作るための機能が強化されていて、それらの機能がUnity2Dと呼ばれてる、らしいのだけど。せっかくだからこの際ちょっと勉強しようかなと。

以下の解説ページを見ながら試してるところ。

_[Unity]Unity4.3の2Dツールを使ってみる -Sprite編- | クスールブログ
_[Unity]Unity4.3の2Dツールを使ってみる -コマアニメ編- | クスールブログ
_Unity2D入門 スクロールアクションゲームを作る マップ作成 - とあるプログラマの備忘録
_Unity2D入門 スクロールアクションゲームを作る キャラクター作成 - とあるプログラマの備忘録

グリッド上に配置されたスプライトシートについて。 :

プロジェクトフォルダに、 _2013/12/23に作ったプレイヤー画像 をコピーして使おうとしたのだけど。Sprite Editor上の左上の「Slice」で分割したら、ガタガタしたアニメになってしまって。

分割する時のタイプを、Automatic にしてたのがマズかったらしい。Grid を選んで、グリッドの横幅と縦幅を指定したら、64x64で綺麗に分割してくれた。…もっともコレだと、テクスチャの無駄な領域がたくさん含まれてしまっているような気もするのだけど。

2014/01/29(水) [n年前の日記]

#1 [unity] Unityでタイルマップってできるのだろうか

Unity2Dの勉強中だけど、背景が寂しいので、BGを表示してみたいなと。で、せっかく2Dゲームのソレをやるのであれば、タイルマップを表示してみたいものだなと。しかし、そんなことできるんかいなと。

ググってみたら、いくつか選択肢があるようで。

_Unity3D - Unity 途別2Dアセット - Qiita [キータ]
_Asset Store - 2D Toolkit

_Unity3Dでお金をかけずに2Dゲーム開発 | めがみん.JP
_Asset Store - Orthello 2D Pro

_Asset Store - Tiler
_Asset Store - Rotorz Tile System
_Unity Tiled Tilemaps | Karnak Games
_Asset Store - Tiled Tilemaps
_Asset Store - UniTile

ただし、どれもえてして有償のAssetsの模様。

と思ったら、ちょっと気になるソレも見かけたり。

_UNITMX, A Tiled Map editor TMX Importer for Unity [RELEASED]
_[RELEASED] X-UniTMX, A Tiled Map TMX Importer for Unity3D
_Chaoseiro / X-UniTMX Bitbucket
_PolCPP / UniTMX Bitbucket
_Importing Tiled maps in Unity3D 4.3 using X-UniTMX - YouTube

Tiled Map Editor の .tmx ファイルを読んで変換してくれる何か、らしい。オープンソース云々、と書いてあるようにも見えるのだけど。しかし自分は英語が読めない+Unityはまだ詳しくないので、導入の仕方すら分からず…。

#2 [cg_tools][unity] 仮画像作成が地味に面倒臭い

DXRubyにしろUnityにしろ、仮画像を作成する作業が地味に面倒臭いなと。もっとサクサク手軽に作れないものか…。

MZ-700のキャラグラを再活用。 :

そこでふと思いついた。MZ-700のキャラグラエディタを使えば、絵心が無い自分のような人間でも、サクサクと仮画像を作れるのではあるまいか。

ということで、 _We Love MZ-700 のページから、キャラグラエディタ CGE7.exe を感謝しつつDL・解凍。フォントデータが必要らしいので、MZ700WIN のインストールフォルダから、mz700fon.jp を CGE7.exe と同じフォルダにコピー。かつ、mz700fon.dat にリネームしたら、CGE7.exe が実行できた。

CGE7スクリーンショット

フォントをいくつか置いてそれっぽいのができたら、Print Screenキーを押して、スクリーンショットを撮って。EDGE2 で新ウインドウに貼り付けて、別の色で塗り直して保存。

色を塗り直し


うむ。これならサクサク作れそう。

おそらく絵描きさんからは、「そんな画像でいいなら、最初からフツーのCGツールでテキトーに描けばいいだろ」と笑われそう。でも、絵描きさんじゃない側としては、「どうやって『テキトー』に描けばいいのか」、それすら分からないわけで…。が、しかし、「枠」「制限」が与えられると、状況がちょっと違ってくる。「ここにある文字だけを使って、それらしいモノを描け」と制約が提示されると、「だったら…こんな形が描けそうかな?」と、案がスラスラ湧いてくる。誰だって積み木遊びはできるけど、積み木の削り出しから始めることができる人は少数派なわけで。ということで、もしかするとこういうやり方は有効かもしれないと思えてきたり。

さておき、原型さえできてしまえば…。後はGIMPで、ちょっと化粧を…

化粧した画像

あまり変わらないか…。

Office系ソフトその他でもいいような気がしてきた。 :

単に制約をつければ描きやすくなるのだ、という話なら、例えば LibreOffice Draw(OpenOffice Draw) の基本図形だけを並べて作るとか、Inkscapeでグリッドにスナップさせて作るとか、そういう制約でもいいような気も。

LibreOffice Drawの基本図形のみで作画

Inkscapeで32x32グリッドを強制して作画


うむ…。これでも全然OKだったかも…。

#3 [cg_tools] LibreOffice DrawのSVGエクスポートにはバグがあるっぽい

LibreOffice Draw で作成した前述の画像をSVGエクスポートしてみたら、GIMP で開いても、Inkscape で開いても、Firefox で開いても、オブジェクトの位置が頓珍漢な状態になった。

どうも、LibreOffice Draw 4.1.4.2 のSVGエクスポートには、バグがあるっぽい。さすがに、GIMP、Inkscape、Firefox の3つで正常に描画されないSVGなのだから、LibreOffice Draw に問題があるんだろうと。

色々試してみた。
そもそも、LibreOfiice Draw は、アルファチャンネルつき画像をエクスポートできないのではないかと思えてきた。だとしたら、ゲームの仮画像作成には、ちょっと使えないかも。

一応、pngでエクスポートした時はアルファチャンネルがつけられるのだけど。ただし、ジャギが目立ってしまうわけで。なんだか背景色がくっついていて…。どうやら、RGBチャンネルはアンチエイリアスがかかってる・背景色まで含んでいるけど、アルファチャンネルだけは2値化状態で出力されてしまってるようで。

LibreOffice Draw は、この手の画像作成に使えない、と思っておいた方がいいのかもしれない。あくまで印刷用文書を作るためのソフトだから、仕方ないのだろうな…。

2014/01/30(木) [n年前の日記]

#1 [anime][game][dxruby] とある飛行士の恋歌、1〜3話を視聴

爆発エフェクトが参考になるアニメだなと思いながら眺めたり。十字型の透過光、円と直線、爆発時の画面フラッシュ等々。コマ送りして、白フレーム前の黒フレームの入れ方について勉強を ―― 爆発箇所を事前に透過光で光らせておくのが基本技だったのか、みたいな。詳しい人達には、「今頃知ったのか…」と呆れられそうだけど。

なんだかトムスっぽいアニメだなと思ったら、やっぱりトムスエンターテイメントの名前があって。実際作ってるのは別スタジオだったりするのではと想像しつつ、しかし、自分は一体、どこらへんにトムスらしさを感じているのだろう、てな疑問が湧いてきたりもして。…そのへん、自分でもよく分からず。IGっぽいとか、サンライズっぽいとか、ボンズっぽいとか、ガイナっぽいとか、ディーンっぽいとか。どういう部分で、それらを感じるのだろう…。

60コマ/秒だとまたちょっと違う。 :

フラッシュの入れ方って、ゲームの60コマ/秒でやると、一瞬過ぎてなんか違う…。なもんで、 _先日作ったサンプル では、わざわざ24コマ/秒前後のフラッシュになるように調整していたのですけど。

アニメに比べると、ゲームはえてしてフレームレートが高いのだから、ゲームなりの凝ったフラッシュができそうな予感もあるのだけど。情けないけど、自分は未だに「これぞゲーム映像」なフラッシュは思いついてなくて。

まあ、ポケモンショック以降、一般の市販ゲームはフラッシュさせる際の規定が出来上がっていて、その枠の中でやるしかないはずだから、「そもそもフラッシュ入れるなよ」と言われる場面もありそうですけど。同人なら制限はないから工夫し放題、だったりするのかしら?

#2 [windows][prog][unity] VisualStudio Express 2013 for WIndows Desktopをインストールしてみた

VS2012 って行の移動はできないのかな、と思ってググってたら、「VS2013から行の移動ができるようになった」てな話を見かけたわけで。だったら試しに触ってみようかな、と。

VS2012をインストールした際は、何か色々とトラブルが起きてしまったような記憶もあるのだけど。VS2013は、それらしい不具合には遭遇せず。

起動してみたら、「Visual Studioにサインインせよ」と言ってきた。Microsoftアカウントでサインインすればいいのかな…?

毎回起動時にちょっと待たされるような感じはするのだけど、それ以外は問題無し…というわけでもなかった。,cs 等がVS2012に関連付けされたままで。仕方ないので、VS2013、VS2012をアンインストールしてから、VS2013だけ再インストール。

とりあえず、これから触ってみる予定。

#3 [unity] Unity2Dで多重スクロールを実験

Unityを使って2Dゲームを作るなら、多重スクロール ―― Parallax とやらは必須だろうと思えてきたり。なので、そのあたりを実験。

巨大なポリゴンをカメラ位置の変化に合わせて動かしていく方法。 :

以下のチュートリアル動画が参考になりました。ありがたや。

_Unity 2D Game Development 16 : Creating a Parallax Background - YouTube
_Unity 2D Game Development 17 : Flexible Parallax Scripting - YouTube

で、参考にして、一応多重スクロールはできたわけですけど。
チュートリアル動画の手法

_Parallax.cs
using UnityEngine;
using System.Collections;

public class Parallax : MonoBehaviour {
    public bool followcamera;
    public float offset;
    private Vector3 oldpos;
    private Vector3 oldcampos;

    // Use this for initialization
    void Start () {
        oldpos = transform.position;
        oldcampos = Camera.main.transform.position;
    }
    
    // Update is called once per frame
    void Update () {
        if (followcamera) {
            Vector3 v = new Vector3((Camera.main.transform.position.x - oldcampos.x) / offset, 0, 0);
            transform.localPosition = oldpos + v;
        }
        else {
            Vector3 v = new Vector3((oldcampos.x - Camera.main.transform.position.x) / offset, 0, 0);
            transform.localPosition = oldpos + v;
        }
    }
}
  • 背景に使う画像は1024x1024、かつシームレスにした。その画像を Material に割り当て。
  • 巨大な Plane を作って該当 Material を指定。タイル回数?を弄って見た目を調整。
  • 該当 Plane に Parallax.cs を割り当てて、offset の値を各Plane毎に変えておけば、スクロール速度が変わる。

しかし、巨大なポリゴンが ―― というか巨大な Plane が、ドデーンと置いてあるのはどうなんだろうと。なんだか環境によっては激しく遅くなったりしそうだなあ、と。まあ、Plane だから、巨大な1枚に見えても、実際は細かくポリゴンで分割されているわけで、最小限のポリゴン数だけ描画されたりするのではないかと楽観視していたりもするのですが。

テクスチャのuvオフセット値を変更して動かしていく方法。 :

ということで、テクスチャのuvオフセットを ―― というか Material のオフセット値を弄ってスクロールする手法も試してみたり。

以下の記事が参考になりました。ありがたや。

_Unityで多重スクロール - kamemo -Unityはじめました-
_UNITYで2Dゲーム開発とかC#とか。: UNITY 2D ゲーム開発: 背景の多重スクロール
_テクスチャの擬似スクロールと小技

で、参考にして、こんな感じに。
uvオフセット値を変更する手法

_Parallax2.cs
using UnityEngine;
using System.Collections;

// カメラ追従しながらテクスチャ(マテリアル)のオフセットを変更して多重スクロール
public class Parallax2 : MonoBehaviour
{
    public float scroll_speed = 1.0f;
    public float div_v = 20.0f;
    private Vector3 oldcampos;
    private Vector3 oldpos;

    // Use this for initialization
    void Start()
    {
        // 初期位置を覚えておく
        oldpos = transform.position;
        oldcampos = Camera.main.transform.position;
    }

    // Update is called once per frame
    void Update()
    {
        // カメラ座標と同じにして追従させる
        Camera camera = Camera.main;
        transform.localPosition = new Vector3(camera.transform.position.x, oldpos.y, oldpos.z);

        // テクスチャオフセットをずらす量を算出
        float u = (camera.transform.position.x - oldcampos.x) / div_v;
        renderer.material.mainTextureOffset = new Vector2(u * scroll_speed, 0);
    }
}
Plane自体は、常にカメラのx,y座標と同じ値にして、uvオフセット値のずらし具合を、各 Plane 毎に変える、みたいな。

コレ、Plane を使ってしまっているけど。Quad でも良さそうな。

コレを Sprite でやれないかな…。

1024x1024の巨大なシームレス画像を用意してる点も、なんだかちょっと引っ掛かる。もっと小さい画像で実現できないかしら。まあ、今後の課題ってことで。

動作デモ。 :

一応 Dropbox に置いてみたり。

_動作デモ (2016/09/04,2017/03/19 置き場所を変更) ( _Unity - Web Player が必要)

←、→、↑キーで移動しかできませんが、BGが多重スクロールしてることぐらいは確認できるかなと…。

背景を3Dにしたほうが早いんじゃないかと言う気もする。 :

どうしてBGを多重スクロールさせるかと言うと、せめてそこだけでも立体的に見せたいから、なわけですけど。

しかし、だったら、カメラを平行投影 ―― Orthographic にするのではなくて、透視投影 ―― Perspective にすれば済むんじゃないの、という気もしていたり。3Dモデルを背景として置いといて透視投影したほうが、「立体的な見た目が欲しい」という目的を、間違いなく果たせるのではないかしらと。

ただ、それはそれで何か問題があったりするのかなと。あるいは、映像表現として、あえて平面らしさを出したいと要求される場面もあるのだろうか。実はそこらへん、自分の中では把握できてないのですけど。

2017/03/19追記。 :

Dropboxのpublicフォルダが死んだのでファイルの置き場所を変更。

2017/03/19現在、Firefox、GoogleChromeの最新版では Unity Web Player がそもそも動かないので、この日記で公開してるUnity関係のアレコレも動かないです。Windows10 + IE11 なら動作することを確認しましたが、いつまで動かせるのやら…。

#4 [game][cg_tools] VertexなるPDFを眺めて興奮してきた

_VERTEX 2 - 300ページの大ボリューム!有名ゲームアーティストが勢揃いのリアルタイムアート技術解説「無料PDFマガジン」第2弾が遂にリリース! | 3D人 -3dnchu- 毎日更新CG系情報サイト という記事を目にして興味が湧いて、 _Vertex なる無料PDFをDLして眺めてみたのだけど。(公式サイトには、「寄付してくれれば次号も出せるよ!」と書いてあるようにも見える。)

英語が読めないから何が書いてあるのか全然分かんないけど、画像見てるだけで興奮してきました。ムッハーッ。オラ、ワクワクしてきたぞ。ていうか、やっぱり絵描きさんはスゴイ。どうしてこんなにカッコイイメカが描ける・作れるのだろう…。

#5 [zatta] 英語、分かりません

先日、 _OpenGameArt.orgに画像をアップロード したら、英語で何かコメントをつけてもらえたようで。

しかし、自分、英語が読めない人間なので、何が書いてあるのか分からなくて。パレットについて言及してるらしい、ぐらいのことしか分からず…。もしかすると要望が書かれてるのかもしれないけど…。うう…ゴメンナサイ…。英語、分かりません…。Sorry. I can't understand English. と書いておけばいいのだろうか。

まあ、せっかくCC0で公開してるのだから自由に変更して使ってください、としか言いようがなく。

前述の、VertexなるPDFもそうだけど、英語が読めないと色々と損をしますな。と言っても、今から勉強したって、読めるようになるとは思えないし…。どうして自分は、こんなにも英語がダメなんだろう…。プログラムを書く時は、アルファベットで書いてるのになあ…。

2014/01/31(金) [n年前の日記]

#1 [anime] プリキュア最終回1話前と最終回を視聴

「正義の味方なんだから隠す必要はない!」てな台詞に、「おお…」と感心してしまいました。考えてみれば、その通りだよな…。なんか 隠し続けなきゃいけない事情があるわけでなし、悪事を働いてるわけでもないのだし。警察官や自衛隊が 素顔隠しながら仕事をするかと言えば別にそういうわけでもないのだから、プリキュアだって、ねえ。

更にラストで、 国家から要請を受けて活動するプリキュアという光景を目にして、「その設定で丸々通しちゃっても、面白かったかも…」と思ってしまったりもして。誰かが言ってたけど、アメコミヒーローってそういう感じだったりするし、実は結構アリな設定なのだろうか。

何にせよ、スタッフの方々、1年間お疲れ様でした。

次作の予告で「十周年」云々と言っていて、もうそんなに経ったのかと、なんだかクラクラ。

#2 [anime] ヴァルヴレイヴ2期、虐殺回の次の回ぐらいまで視聴

虐殺シーンが出てきて、「この黒さ…。これはきっと大河内脚本に違いない! 相変わらず黒い! さすがだ!」と予想してたら熊谷脚本だったのでビックリでした。このアニメ、黒い脚本家さんばかり集まってる…! いや、考えてみれば一期の熊谷脚本も相当黒かったっけ。 今になって驚くことでもないですわな。

気になってググってみたら、「ガリレイドンナ」の虐殺回も熊谷脚本だったそうで。また、当人曰く、「戦争描写等はトラウマレベルであるべき」「皆で火垂るの墓を見習おう」的信念をお持ちだそうで。なるほど、それもたしかに一理あるなー、と。

考えてみれば、世の中の娯楽映像作品においては、人が死なない戦争・戦闘描写ってのもあって。明るく楽しくハラハラドキドキでキャッキャウフフな戦争・戦闘描写のほうが、実は本質的によほど怖いような気もしてきたり。見ていて「うわ、酷い」と思う映像のほうが、むしろ健全な映像と言えるのかもしれないと。

また、殺人を行う敵側の非道さを、しっかり伝えることを目的としているシーンであれば、目を覆うような描写をそこに盛り込まないと意味が無いよな、とも。敵が非道だからこそ、敵をやっつけた時に視聴者側もスッキリするのだろうし。であれば、そういうシーンでブレーキ踏んじゃいかんよなと。

何にせよ、「酷いシーンにするぞ」と意識して、見事に酷いシーンが出来上がったのだから、これは良い仕事ぶりではないのかな、と思いました。

残虐行為の見せ方についてなんだか考えてしまったり。 :

「戦争・戦闘シーンはトラウマレベルであるべき」という考え方に同意するところもあるのだけれど。しかし、個人的には、「見せ方は工夫したほうが」とも思ったりして。その手の直接描写ってどうなんだろう、みたいな。イデオン発動編が好きな自分ではあるのだけど。おじさんになった今現在は、そういう見せ方は好みではないというか。 *1

と言うのも、視聴者一人一人は、その手の描写に対する耐久力が異なるからで。その視聴者の、耐久力のしきい値を超えた直接描写を提示してしまうと、下手すると、一シーンが原因で、作品全体の視聴すら拒否される展開に。それは娯楽『商品』として、よろしくないだろうなと。

なので、直接見せず、間接的にその周辺を描いて、視聴者の想像力を引き出す見せ方が可能なら、そっちのほうが美味しいのではないか、てなことを最近は思っているのでした。

視聴者に想像させるタイプの見せ方なら、各視聴者が、自分の想像できる範囲・しきい値を超えない範囲で、その人にとって一番キツイ残虐行為を思い浮かべてくれる。つまり、各視聴者のレベルに合わせて、作品がカスタマイズされるも同然。これは、直接描写では、けして得られない効果のはず。

以前、「PSYCHO-PASS」で、「獣の奏者エリン」の監督さんのコンテ担当回を見たのですけど。「エリン」と同様、直接描写は最低限にして、間接的描写を多用していて。「なるほど、これなら誰にでも見せられるなあ…。それでいて、犯罪者の非道ぶりが、しっかり伝わってくる。これは上手い」と感心した記憶が。…おそらく、公共の電波に乗せる作品ってのは、そういうところも意識できるか、工夫してるかが問われたりするのかなと。そこまで考えて作る人が、プロなのだろう、と。 *2

とは言うものの。
  • 直接見せず、しかし、直接見せるより強烈な、トラウマレベルのシーンを作れてしまう人なんてそれほど居ないだろう、難易度高過ぎてフツーはできないだろう、とも思うし。
  • 間接的なソレって、やり過ぎちゃうとギャグになってしまう時もあるし。
  • 視聴者の中には、想像力が全く働かない人もたまに混ざっていて面倒だし。
そのへん考えると、直接描写をポンと出すほうが、お手軽で確実、とも思えるわけで。でも、ソレ、やっぱり芸が無いよなあ…。

このあたり、脚本より、コンテを描く人の意識・技の数が重要では、とも思うので、もしかすると脚本レベルで考えることではないのかもしれないのですが。脚本で工夫してもコンテで無視される場面もありそうだし、脚本で考えてなくてもコンテで付加してくれる場面もありそうだし…。

とりあえず、そんなことをぼんやり考えてしまったのでメモ。

*1: たとえ自分の好みではなくても、バッチリな出来・作り手の狙い通りだろうと思えるシーンは評価されるべき、とも思ってますけど。
*2: しかし、「PSYCHO-PASS」は、他のコンテ・演出の方が直接描写を好む方ばかりだったので…。作品全体としてはとにかくグロイ印象を持たれるアニメだったような…。

以上、31 日分です。

過去ログ表示

Prev - 2014/01 - Next
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project