[BeOS | Blender | COLLADA | NPR | OpenGL | PeerCastStation | Riko | Rub | Ruby | Silverlight | TRPG | XNA | mono | monotone]
土日になると体調崩す。 かといって無駄に休日出勤するわけにも……つっても忙しいのでそれ程無駄でもないんだが。
最近は仕事とmkv読み込みしかしてないので特に日記書くこともない。あ、ゲームマーケットにちょこっと行ったくらい。
PeerCastStation自体は手を入れてないんだが。Matroska(.mkv)読み込みをずっとやってた。
MatroskaはEBMLというバイナリ化XMLみたいなもので構成されてるのでまずはそれを読み込み。 要素のIDやらサイズが可変長整数なのが面倒だが難しくはなかった。
一点だけ、整数は可変長整数なんだーと思って要素のデータがuintやintの物も可変長整数としてパースしてたら間違っていた。こいつらも可変長であることは違いないが、サイズは事前にわかるので普通に1〜8バイトのビッグエンディアン固定長整数として読んでやればいいだけだった。ちゃんと仕様を読んでなかったのが悪いが紛らわしい。
書けはしたのでじゃあいざテストファイルを読み込みましょうとやってみたら上手くいかん。 単純なファイルtest1.mkvは大丈夫だったんだが、当初の目的であるライブストリーミング用ファイルtest4.mkvを読むと途中でおかしなデータにぶちあたって止まってしまう。バイナリエディタで見るとSegmentタグのあとに0xA0が130バイトちょい連続していてファイル壊れてるっぽく見えるんだが、壊れてるテストファイルはtest7.mkvだと言うし、検証アプリのmkvvalidateでも文句言われないしじゃあこれは正当なのかと。
しばらく悩んでると、同じこと言ってるForumのスレを教えてもらったので読んでみた。確かにぶっこわれてるけどストリーミングなら壊れるもんなんだから頑張れ、的なことが書いてあった。このくらいのゴミデータはぶっこわれてるうちに入らないので検証器も普通に通るようだ。あと普通にGStreamerから吐かれたファイルらしい。いつのか知らんがGStreamerぶっこわれてますよそれ……。
壊れてるファイルを読むのはまあいいんだけど、復帰方法が具体的に書かれてるところないんだよね……。一応Streaming方法のページにも上記スレにも次のClusterタグを探せよと書かれているんだが、Segmentの直後なのでここからClusterまで飛ばしてしまうとTrackとかのコーデック情報が入ってるタグもすっとばすので再生できないっすよ。
仕方ないのでmkv読み込むCとかC++のライブラリソースを眺めてみよう。 するとすっごいめんどくさいのがわかった。 不正な要素が来たと判断するのに必要な項目がたくさんある。
上記の場合は問答無用でおかしい。ただしIDもサイズも最大長はファイル先頭のEBMLタグ内に書かれてるので本当はそれを参照すべき。
上記の場合は一個親のタグにくっつかないかなーというのを再帰的に調べる必要がある。というわけで親の読取り開始位置と親のサイズと親のIDとそれに仕様的にくっつくと思われるID一覧を知ってないといけない。
上記の場合の処理がよくわかってない。特に後者。知らないタグなのかぶっこわれたデータなのか判別できないんだが、どうもタグによって子に知らないタグを許可するかどうか切り替えてるっぽい?やっぱりよくわかってないが。
というわけでなかなか難しいことはわかった。OGGやMPEG-2 TSなんかと比べると明らかにストリーミング向けじゃないのな。階層構造とストリーミングは相性悪いわ。mp4でライブストリーミングできない理由がわかった気がする。まああっちは仕様上mkvみたいに不定サイズのタグを持てないからってのもあるんだけど、やったとしても破壊に弱いから対応しないんだろうな。
さてはて困ったな、と言いたいところだが、PeerCastで扱う限りはそんなに難しいことしなくていい。TCPでしか通信できないんでパケット破壊とかそうそう起きるものではない1。 さらに言うならPeerCastでコンテナのパースをしないといけないのは配信開始の部分だし、そこもヘッダとそのあとのボディに分けるだけ。 というわけで 判別が必要そうなところだけ見てあとは出来る限りすっとばそう。
これだけだ。ちゃんとパースして再生するのはプレイヤーの役目なので、途中で受け渡しするだけのPeerCastではあまり構造については深く考えないことにして、ごく少数の知ってるデータ以外は何も見ずに右から左に流すだけにした。 ただしどこからがヘッダ部でどこまでがボディ部、というかどこからまた新しいヘッダが流れてきたのかを判別はする必要はあるのである程度同期はとってる感じ。 同期をとるワードが状態によって変わるけどね。
あとはこれC#で書くだけか。うーん、まあすごい大変ってほどではないかな……。
生成されたストリームが既にぶっこわれてるのは仕方ないんで対処しないといけないんですけどね。↩
4月末でイベント終わった。なにもできなかったな……。
一応PeerCastStationの配布はできたけど、新機能入れられなかったしなぁ。 ともあれ、これでまたちょっと落ち着けるようになった。新機能に向けて頑張りたい。あと配布用のサイトをちゃんと作りたい。
GWは29日と5日・6日だけ休んであと出勤してた。5日は出ようかとも思ったけどコミティアとかトレフェスとか行けたので休んでよかた。あんまり会社に引きこもっててもやる気が持続しないからなー。
最近LOOX Uの調子がいまいちでちょっとこわい。起動後がいつもよりもっさり気味だったりエクスプローラがたまに固まってたりする。これが普通のデスクトップやノートPCならまあ本格的に壊れたら新しく買うかーで心配もないんだけど、こいつは今は亡き手乗りサイズのPCなので新しく買うのができん。タブレットとかいう文字入力も満足にできない物を濫造してるなら、ちょっとでいいからHWキーボード付きの小型PCを作ってくれよ!!携帯ゲーム機はもう十分だよ!!
イベントには間にあわなかったが、新たなコンテナに対応したい。
まずはMPEG-2 TSの仕様書読みかなぁと思ったんだけど、VLCでのエンコードちゃんと動くのかなー?と試してみたらなんかMKVとかWebMの出力もできるようになってるし。あれー、ちょっと前に見たときには無かったんですけどー。ってMac版だとGUIに出てこないのか。相変わらずだな。
Windows版でエンコをやってみたら落ちる。落ちまくる。原因を絞っていくとどうもH.264にエンコードしようとすると落ちるようだ。あらら。 WebMやmkvでTheora/Vorbisなんかは通ったのでmkv対応を先にやってしまうか。Matroskaは仕様も無料で読めるしね。
VLCはflv出力もできるのでこれ対応してみてもいいかもしれない。flvは仕様公開されてるし作りも超簡単だからな。flvに入るコーデックを調べてたらH.264とかAACも入るけど制限有りってことになってる。制限ってなんだろう。まあ使えないって言うならそれはそれでもべつにいいんだが。
flvのエンコーダはFlash Media Encoderとか使えないのかしらとRTMPについても調べてみたけど、これも仕様公開されてるし意外に簡単だったのでサーバ作ってみるのもいいかもしんない。だがしかし、Flash Media EncoderはFlash Media Serverにしか使っちゃいけないというライセンスのようだ。……ま、まあWirecastとかもRTMP対応みたいですしね。Wirecastは結構素敵な値段するけどDesktopPresenter使えると2PC配信に便利ですよね。あと画質に目を瞑ればFlashPlayer自体が簡易エンコ機能持ってるのでそれも使えるっぽい。
希望としてはPeerCastStationにRTMPやRTSPのサーバ内蔵して配信できるようにしてみたいんだけど、今のところ配信はどこからかPullしてきて開始になってるので、Pushされて配信開始とかそんな設計になってないんだよな。あとそうすると必然的にMuxerも内蔵する必要がある。DemuxerとMuxerはあるといろいろ出来て便利なんだがちょっと大変なんだよなぁ。まあ妄想レベルなんでぼちぼち考えておくくらいにしよう。本気でやるにしてもやるならまず外部プロセスでだな。
なんとも体調が落ち着かん。起きるの遅いのが悪い気がするんだが寝るのも遅いからなぁ。
今週末にイベントなんだが出すものさっぱり出来てない。出来てないどころか何も作ってもないのでもう無理だろうまじで。 今回のイベントは何か出すのは諦めるしかなさそう。仕事の忙しい時期と被ってしまったのが敗因だ。
Monoで動かない部分があったと思ってたんだが、いろいろ試したらやっぱり動いた。以前動かなかったのはなんだろうな。もっと前のバージョンのMonoだと動かなかったりするんだろうか。Monoの2.6系でも動くことを目標にしてるのでそっちでも試したほうがいいかもしんない。
MonoTools for VisualStudioとか入れてみたが、VisualStudioごと落ちまくってあまり使えなかった。Macではデバッグ開始した瞬間Mac側のサーバが落ちてた。MoMAがさくっと使えるのは便利だがそれだけか……。
PeerCastStationでmkv対応してイベントに出そうと思ったんだが、VLCでmkvのストリーミング配信ができないのに気付いたのでやる気が失せた。配信だけ対応してもエンコーダがないのではな。HTTP Live Streamingに行く行くは対応したいと思うのでMPEG-2 TSには対応せねばなるまい。調べたらWindows7のWindows Media PlayerはMPEG-2 TSをそのまま再生できるようになってるということだしVLCで配信もできるようなので対応するならMPEG-2 TSが先か。
MPEG-2 TSについて調べたが、あまり詳しい仕様が見つからない。ISO規格になってるようなのでこれを読めということか。しかし買うと7000円もするのがちょっと悩ましい。値段はともかく規格書なので読み辛いだろうというところがちょっとな……。 なんにしろ平日4日で仕様読んで実装するのは難しいだろう。
イベントに出すならWindows版はHTMLベースUIも追加したしこのまま出すこともできるだろう。MonoではGUIが全く動かなくなっちゃってるのでなんとかしないといけない。Mono版はGUI外部化しないとなぁ。
GUIといっても起動したらPeerCastStation本体を同時に立ち上げて、タスクトレイ的なところに常駐してクリックしたらメニュー表示してHTMLベースUIを開くか終了ができるだけでいい。MacならMacRubyで作ればいいだろうか。MacRubyももうしばらくいじってないのですっかり忘れてそうだぜ。Linux等ではどうすりゃいいんだろう。Ruby/Gtk+とか使えばいいんだろうか。よくわからんので一旦パスでもいいかもしんない。
Mac版程度ならなんとかできるかもなぁ。イベントまでには無理でもイベント中にならなんとか。
オンラインアップデートとか対応したかったんだけど今からでは無理だなぁ。うーん、残念……。
メインPCのメインドライブであるSSDが見えなくなってしまった。
突然Operaが固まって落ちるのかなーと思ったが落ちないが操作できず、しかし他のアプリへの切り替えもできないのであれーと放っておいたら1分くらいで再起動してた。そしてBOOTMGR is missingとか言って起動しないでやんの!わーいディスク死んだ! BIOS(つかUEFIだけど)で見るとSSDが見えなくなってた。あばばば。
一旦電源をちゃんと切っておいてから再起動するとBIOSからは見えるようになったが相変わらずOSが無いとかいって起動できない。見えてはいるってことはデータなりMBRなりが壊れたのかなぁと。Crucialのm4だとファームウェアアップグレードで治るかもよ!と教えてもらったが、残念ながらうちのはOCZのSolid3だ。一応ファームを探してみたがリリースノートを見る限りOSが起動しなくなるほどのバグ修正はないようだな。
仕方ないのでとにかくDVDなりから起動してディスクの状態を見るかなーとWindowsのインスコディスクを出してきて起動優先度を変えようとしたら……あ、SSDじゃなくてデータ用HDDが優先度トップになってるじゃないですか。一旦見えなくなった時に変わっちゃったんだな。SSDから起動してやるように直したら無事復活した。よかった。ディスクのチェックとかSMART情報をチェックしたけど問題なく、少なくとも死にかけてる状態ではないようなので様子見だな。
その後一応SSDのファームアップデートはしておいたけど、それよりバックアップだなぁ。用意しなきゃとは思いつつ普通にバックアップするだけじゃつまらんからといろいろと夢見すぎていつまでも環境用意できていない。作業リポジトリはいろんなところに分散してsyncしてあるので大丈夫だし、あとはDropboxに入ってたりするのでメインPCローカルにしか無くて消えたら困るようなものってのはそんなにないはずなんだが、いざ壊れたら環境の復旧が大変だものね。
主にGUI修正とかの続き。GUI自体はまあまあ整理できた。リレーツリーとかまだあまり試してないけど大丈夫かな。
あと何やるんだけっかな。HTML UIでリレーツリー表示できるようにしないといけないか。めんどいなぁ。
JSON-RPCにmonoで動かない部分があるのが分かっていたので修正しようとしたが、WindowsのMonoDevelopが動かなかったりMacではGUIが全く動かなくなったりしてて手を出せない状態に。
Windows版のmonoとMonoDevelopを入れてデバッグしようとしたんだが、デバッグがさっぱり開始できなくてなんともならなかった。あとで気付いたんだが64bitOSなのが悪いんだろうか。
OSXではWindows.Forms製のGUIがさっぱり起動しなくなってしまった。ウィンドウの表示まではされるんだが、そのあとApplication.Run内で止まってしまうようだ。GUIをメインスレッドとは別なスレッドで起動してるのが悪いっぽいんだが、そういう作りにしちゃったしどうしたものか。以前は動いてた気もするんだが気のせいだろうか。 とはいえ動いたとしてもOSX版のWindows.Formsは出来が悪く、日本語は文字化けしまくるしスクロールすると描画がはみだすしとあまり使いものにならない。 OSX用にMonoMacでGUIを作ってみようとしたが、MonoMacのdllは署名されてないし.NET4用しかないので署名付きで.NET3.5対象にビルドしてるPeerCastStation.exeでは参照ができなかった。それはまだビルド設定変更でなんとかしたとしても、MonoDevelop落ちまくるし出来た実行ファイルはぶっこわれてて起動できないしで全く使えなかった。MonoMacよくわからん。どうもビルド時にObj-CのソースをごにょごにょするようなのでdllでGUI起動とか変なことはできないのかもしれない。
どうしたものかと考えたが結局HTML UIをメインにするか、GUIもJSON-RPC経由で通信する外部プロセスにするしかないな。どちらにしてもプロセス内GUIはやめてサービス的な作りにして操作はプロセス外からやるしかない。となるとWindows.Forms版のGUIもやめて外部プロセスにしちゃってもいいかもねー。まあいまやめる必要もないのでOSX以外では残すけど。
JSON-RPCでの操作で足りないものとしてはプロセス終了ができないくらいか。そんなものRPCに入れるかどうか迷うところではあるが、今でも設定いじったりとかやりたい放題なので終了くらい今更どうということもない。でもSIG_KILL受けたら終了の方がいいかな。サービス的に起動したものだしそれ自体で終了したいこともあるまい。
UIいじりに一段落ついたらコンテントパーサ増やしてPCPプロトコルちょっといじるのやりたい。mkvに対応したいんだが、仕事も忙しいし今月末までにそこまで入れるのは難しい気がしてきたわ。てか無理じゃね。
新しいGeForceが出たけどなんか新しいGL拡張追加されてないんかなーと思ったら早速公開されてたので読んでみた。まああんまり派手な機能は無かったんだけれども。あとついでに最近追加されてた拡張も読んでみた。言う程最近じゃないけど気になったものもちゃんと読んでみた。
最近はシェーダ内でテクスチャやバッファオブジェクトを直接読み書きしちゃったりできるわけですが、この拡張はその際に浮動小数点数に対して不可分操作をできる拡張です。今までは不可分操作は整数しかなかったんだね。そんなん使うかよとか思ったけど加算とかあればあったで使うか。
描画時にテクスチャを読む場合は
という流れになってたんだけど、テクスチャユニットはコンテキストグローバルなので沢山の描画を流したいときも一気にGPUに流せずいちいち途中でテクスチャのバインドが挟まるのが残念だった。そこでシェーダパラメータとしてテクスチャ(とサンプラ)を渡してテクスチャユニット関係なく読み込めるようにするぜ!という拡張です。
シェーダで使うテクスチャ設定するときにどこのテクスチャユニットに割り当てようかなぁとか、その番号をあらためてシェーダに渡したりとかいまいちめんどうだと思ってたので便利そうな拡張なんだが、思ったよりはめんどうだった。
NV_bindless_textureを使ったときの流れは
という感じ。テクスチャオブジェクトとかサンプラオブジェクトの番号をそのままUniformとして渡せれば簡単だったんだがなぁ。一度ハンドルを取得するようになっているのはハンドルが64bitだからのようだ。テクスチャオブジェクトとかサンプラオブジェクトは基本32bitだし、最近のOpenGLでないと好き勝手な番号使えることになっちゃってるからなぁ。あとサンプラとテクスチャを関連付けないといけないというのも理由かもね。
ハンドルを取得する必要があるのはともかく、テクスチャハンドル常駐化ってのがいまいちわからん。 テクスチャを常駐化する(ビデオメモリに載っける)っぽいんだが、失敗しないんだよなこいつ。 ビデオメモリが足りなければ他のテクスチャが非常駐化するから常に成功でもいいのかな。 そもそも1枚でもビデオメモリに載らないテクスチャなんか作れないし。 関係ないけど非常駐化って日本語としておかしいかも。
これであとはsampler2Dとかのuniform変数にハンドルを渡してやるとシェーダでいつもどおりサンプルできるってわけだ。
ついでにこの拡張ではsampler変数を64bit符号無し整数としても扱えるようになる。今までできなかったsampler変数をuniformブロックにつっこんだり、頂点属性として渡したりとかもやりほうだいのようだ。値としてはテクスチャハンドルを指定しておく。
そのうちARB拡張やGL 4.xに入ってくれるとよさそうな拡張だけど、ハンドルとかどうだろうな。
AMD_vertex_shader_layer / AMD_vertex_shader_viewport_index
プリミティブをレイヤードテクスチャのレイヤーやキューブマップの面、はたまた複数定義したビューポートのどれかにシェーダ中で振り分けて描画する機能ってのが最近はあるんですが、こいつがジオメトリシェーダ必須なわけですよ。しかし振り分けるだけのジオメトリシェーダを挟むのはめんどくさーいということで頂点シェーダだけで振り分けができるようにするのがこの拡張。
特筆することはなにもないですねー、と思ったがよく考えたらおかしい。ジオメトリシェーダはプリミティブ毎に実行なんで振り分けもわかるんだが、頂点シェーダで三角形のプリミティブを頂点単位で別なレイヤーに振り分けちゃったらどうなるんですか!よく読んだら、どうなるかは実装依存なのでひどいことになりたくなかったらちゃんとプリミティブ単位で同じところに振り分けろや、って書いてあった。インスタンス化描画してインスタンス毎に振り分けみたいなのを想定してるようだ。なるほどたしかにそうするし、ジオメトリシェーダでプリミティブ複製するより楽でもしかしたら速いかもな。ジオメトリシェーダさん涙目。
WGL_EXT_swap_control_tear / GLX_EXT_swap_control_tear
WGL_EXT_swap_control拡張にあるwglSwapIntervalEXT関数では何VSync待ってバッファのスワップをするか決められた。たいていは垂直同期を普通に待つ1か、ティアリング気にせずスワップしちゃう0を指定するわけですな。
常に待つとティアリングが発生しないのが嬉しいんだけど、60fpsをちょっと下回ったときに急に30fpsになってしまい(垂直同期周波数が60Hzだとして)、さらに次の垂直同期までしばらーく無駄に待ってしまう。
全く待たないようにすると一時的に処理負荷が上がっても垂直同期を無駄に待ったりしないので時間を有効に使えるんだけどティアリングが発生してしまう。処理落ちしてるときにティアリングが発生するのはフレーム数が急に1/2になるよりかはマシだろうが、困ったことに60fpsを越えてる場合も垂直同期に関係なくスワップするおかげでティアリングが発生してしまう。
さてそこでこの拡張、両者のいいとこ取りをしてくれちゃう。wglSwapIntervalEXTに負の値を渡せるようになるんだけど、たとえば-1を指定すると処理落ちしてる場合は即スワップ、処理落ちしてない場合は1VSync待ってくれる。処理落ちってのは前のスワップから指定した値の絶対値回VSyncを通過してるかどうかで判断ね。なので処理落ちしてる場合はティアリングは発生するけど急にフレームレートが下がりまくったり無駄な時間を待ったりはせず、間に合ってる場合は同期を取ってくれるのでティアリングが発生しなくて嬉しい。
なんかNVIDIAがGeForce GTX 680の発表でさも新機能のように発表してたけど、Rageが出たころにドライバにそういう機能入れてもらったよーとか言ってたし、RadeonはもちろんIntel GPUですら対応しちゃった普通の機能です。NVIDIAの場合はコントロールパネルから垂直同期強制上書きでこの設定できるみたいだけどね。
単純だけど嬉しい機能。使い方も簡単なので是非使ってほしい。
普通にmallocとかで割り当てたメモリ領域をそのままバッファオブジェクトとして使えるようにする拡張。 どうもGPUが直接システムメモリにアクセスできる場合に素敵なことになるよ!というのを想定してるようなんだが、仕様としてそれを要求してるわけではない。Radeonの5000系でも使えちゃうのでやっぱりGPUがシステムメモリにアクセスするなんてのは必須ではないようだ。
使い方は簡単でBufferData時にターゲットとしてEXTERNAL_VIRTUAL_MEMORY_BUFFER_AMDを指定すると、以降そのバッファオブジェクトはBufferDataに渡したメモリ領域をそのまま使うようになる。もちろんバッファオブジェクトとして使ってる間はメモリ領域解放しちゃだめだよー。
なんつーか普通にバッファオブジェクトを割り当ててMapBufferするのと大差なくて微妙かなぁと思ったんだけど、よく考えるとそうでもなかった。 OpenGLでシステムメモリ上にあるデータをシステムメモリ上への無駄なコピーをせずに非同期でGPUメモリにコピりたいなと思うと意外にできないんだが、AMD_pinned_memoryを使うとやっと実現できる。
ファイルから読み込んできた頂点データがメモリ上にある時に、これを描画のために頂点バッファオブジェクトにつっこみたい。 いくらか方法はあるが、
このどれかだろう。
BufferDataでバッファ確保と同時にコピーするのは単純だが、この方法はでは非同期にコピーができない。 というのもBufferDataを呼んだ直後にメモリ上の頂点データは解放してかまわないことになっているので、ドライバはBufferData内で同期的にGPUにデータ転送する(まあありえないだろう)か、ドライバ内部でシステムメモリ上の領域に同期コピーしてあとでゆっくりGPUに転送しておくかだ。後者は非同期にGPUへ転送してるかもしれないけど、システムメモリ上へのコピーが一回余計に入ってるしやっぱり同期コピーだ。メモリコピーもでかいデータだと結構馬鹿にならん時間がかかったりする。
MapBufferはもう明らかに同期コピーだわな。手動でコピーだし。まあ別スレッドでコピーしておけばいいじゃないと言う意見もあるが、結局マップされた領域ってのはシステムメモリ上にあるわけでUnmapされるとそこからドライバ内部でGPUにゆっくり転送ということになるので、やっぱりシステムメモリ上に一回無駄にコピーしてるなぁ。
ファイルからメモリに読んであるデータをバッファオブジェクトにコピるんじゃなくて、MapBufferした領域を対象に直接ファイル読み込みをすると無駄なコピーは無くていい感じではある。ただこれはめんどい作りである。普通やらんだろう。
といった感じでなかなか悩ましいところなんだが、AMD_pinned_memoryを使うと解決してしまうわけだ。
CopyBufferは非同期でやってくれるので、はい、これでアプリで読み込んだデータをシステムメモリからGPUメモリに無駄なく非同期コピー完了です。まあ気をつけなきゃいけないのは頂点データのある領域はコピーが終わるまで解放したらまずいってことですかね。
つかシステムメモリ上の領域をバッファオブジェクトにした時点で頂点バッファとして描画でもなんでもできるじゃないですか!という意見もあると思いますが、たぶん速度的にあまり嬉しいことにならないんじゃないかと思うのでどうなんだろうね。本当にGPUがシステムメモリを直接見れるシステムなら問題ないだろうけど、今はまだ出来ないよね?Radeonの7970とかならある程度できるんだっけ?
あとこれは思いついただけで試してもないので本当に出来るかどうかわからんが、メモリマップドファイルのメモリ領域を直接バッファオブジェクトにできるとちょっと面白いかも。2GBとかの巨大頂点配列をDrawArraysするだけでファイルからストリーミング描画していけるかもしんない。頂点バッファに限らずテクスチャバッファとかUniformバッファでもいいので出来れば使い出はいくらでもありそうだね。
この拡張はNVIDIAにも是非実装してもらいたいところ。つかGPUから直アクセスである必然は無さそうだし、標準に入んねーかな。
いろいろ忙しい気がする。そうでもない気もする。
体調が悪くてなんかふわふわする。一回休みにして一日寝てたけどあんまりかわらず。微妙な風邪っぽいが。
この前の週末にPeerCastハッカソンというのがあったのでそこでUnityであそんだ。
最初はちょっと前に買っておいたUnityではじめるゲームづくりという本を参考に進めてたんだが、半分くらいまでいって飽きたのでぶん投げて適当にゲーム作り始めた。飽きた原因はいつまでも見た目ばっかり作っててなかなかゲームに入らなかったからだろう。
何作ろうかと悩んでたらボンバーマンと言われたのでそれを……っていつもボンバーマン作ってないか?べつにいいんだけど。
見た目は適当でいいので箱並べて適当にテクスチャを設定しただけ。三人称視点にしようとしたんだが、FPS Character Controllerを置いたあとに一回消してTPS Character Controllerを置いたらなぜか上手く動かなくなってしまったので諦めた。FPS Character Controllerを置いてFPS操作にしておいた。カメラはちょっといじったのでFPS操作のTPSみたいなかんじだったが。というか360ボンバーマンのFPBモード(とてもクソゲー)みたいなの。
あとは爆弾置いたり。64のボンバーマンは爆風が球形だよねとか言われてなるほどとひよったり、爆風がデフォでどんなブロックでも貫通するけど気にしなかったり、パーティクルが上手くつくれなかったので見た目が悪かったりしたがまあできたよ。
敵も作ってやった。高速で自機めがけてつっこんでくるだけという手抜きな敵だが、いい感じにいやらしすぎてひどいクソゲー感が出たので満足した。
最初の爆風があまりにもでかくて死にまくり、さらにちょっと行動範囲が広がると敵が倒せなくてやばいので火力UPのアイテムも作ってやった。ブロックを壊すと1/2の確率で出て上限無しで取るごとに火力が上がるおそろしいアイテムだ。爆風はブロックで止まらないので取りすぎると死ぬ。たいていはその前に凶悪な敵に殺されるがな。
残機とか敵全部倒したらクリアとかあってもいいかと思ったんだが時間もなかったし、ゲームオーバーとかクリア画面を作るのがめんどくさかったのでやめた。満足したら終わりということで。
てな感じでちょこっとゲームの残骸らしきものがUnityいじりはじめてから約2日でできたわけだ。もうちょい頑張ればもっとゲームらしくなったかもしれんが、まあこんなところ。
使ってみて思ったのがやっぱりUnityすげーなという感じ。
何がすげーって丁度よい。いじる前はいやーゲームエンジンってアクションゲームツクールみたいなもんなんでしょ?詳しくいじろうとするとしんどいんでしょ?と思ってたんだが少なくともUnityは違ってた。カメラ動かすにもなんにもプログラムを書く必要がある。ちょっと試すのにあると便利なものは標準のコンポーネントとして用意されているが、それにしたって自前で書いたコンポーネントとの差異はない。 じゃあ結局プログラム書かないといけなくてめんどいじゃんということになるわけだが、標準のコンポーネントがなかなか使い勝手が良いのでそれを使えばそれほどめんどくもない。
コンポーネントの仕組みもすごくよくできてるよねー。プログラムの単位はコンポーネントということになっている。コンポーネントはシーン中に配置するゲームオブジェクトにくっつけるプログラム。ゲームオブジェクトにはコンポーネントが複数設置できて、あるコンポーネント内で別のコンポーネントも参照できる。コンポーネントはモデルとかテクスチャと同じようにプロジェクト中のアセットとして保存されていて、いつでも適当なオブジェクトにくっつけることが可能。
コンポーネントクラスのpublicフィールドはGUI上で編集可能だし、VisualStudioでいじってUnityに帰ってくると即座に(ちょっと固まるけど)変更が反映されるあたりはとても素敵。他のオブジェクトを参照したいわーと思うとGameObjectのフィールドを作っておいてGUIでそこにドラッグ&ドロップするだけでできるしね。この辺の便利さはRubyCocoaとInterfaceBuilderの連携に驚愕した思い出が蘇えるが、UnityはMac発祥だからInterfaceBuilderを参考にした部分もあるんだろうか。
コンポーネントのいいところは、プログラムの再利用化が自然に行なわれるところ。まあもちろんプレイヤー用のコンポーネントとかいう再利用のしようもない物も普通に作るけど、HP管理して無くなったら死ぬだけというコンポーネントを作って、プレイヤーとか敵とか破壊可能ブロックとかにくっつけようというのを自然にやりたくなる。
最初はHP管理コンポーネントを継承して敵コンポーネントを作ろうとしたんだが、継承は上手く動かないようなのでやめた。継承じゃなくて必要なコンポーネントは全部ちゃんとくっつけとけよということらしい。確かに継承より再利用しやすいからな。ただし必要なコンポーネントをくっつけてないと取得しようとしてもnullがかえってきて終わるのは注意な。
見た目の方も自動でこれ生成して欲しいなーという物が簡単に作れるのがよい。地形の編集はめんどいが。ライトマップの焼き込みとか簡単にやってくれるし、法線マップ作ってほしいなーと思ったらインポート設定でできる。それでいてちゃんとした編集は結局外部エディタでおねがいしますとなっており無駄がない。 見た目に関してはBasic版だと水がしょぼいんだけど!とか影くらい出してほしいんだけど!とかポストエフェクトはないのかな!とかいう無くてもいいんだけどあるともうちょい派手になる辺りの機能がPro版買ってねとなってて、ちょっと欲しくなる絶妙な辺りを突いてくるのが憎い。 ゲームロジック作る分にはプロファイラがないくらいでProとBasicで違いはないみたいね。
一方でプログラムを書くにはおかしなところも散見される。まずJavaScriptで書けますといってるわりには静的型付け可能なJavaScriptじゃねーだろそれという言語だった。JScript.NETとかActionScriptをJavaScriptといいはるのは無理があるだろJK。
まあ俺はC#で書いたんですけどね。 しかしC#もなんかおかしい。まずこいつを見てくれ
コンポーネントはこんな感じで実装される。一見普通なんだが、よくみるとなんかおかしい。StartとかUpdateがプライベートメソッドである。オーバーライドも何もしていない。なのになぜか実行されるのである。ど、どういうことなの…?
最初オーバーライドしないといけないのかーと思ったんだが、そもそもMonoBehaviorにStartもUpdateもなかったりした。あれー?おそらくはリフレクションでUpdateとかのメソッドを取り出して(ついでにリストにつっこんで?)実行してんじゃないかと思う。なんでそんなことするかというとvirtualにしてオーバーライドするようにすると派生元のクラスでは空メソッドを用意しておかないといけないが、ゲーム中大量に発生するコンポーネントの大半で空メソッドを呼ぶのはそれだけで結構無駄な時間になることがあるのでvirtualですらないんだろう。全くの想像だけどね。
速度の問題ってのは理解しても気持ちわるいなぁとは思う。ていうかさらに気持ちわるいのはプライベートメソッドなのに呼ばれることだよ!せめてpublic必須にしておけよ!!べつにいいんですけどねー。
もしかしたら速度関係なく、単にoverrideとかpublicとか書き忘れんだろおめーら、ということで書かなくてもいいようにしてるのかもしれない。そんなことはないと思うが。
もう一個気持ち悪い点がある。
Updateの中にこんなのを書いたとする。MyComponentがくっついてるゲームオブジェクトからCharacterControllerコンポーネントを拾ってきてあったらそれを使ってキャラクタを移動させようとしてる。移動の中身は今関係ないので省略してるよ。GetComponentは対象のコンポーネントが無かったらnullを返してくるんだが、C#ではnull判定はcharacter!=nullと書かないといけないのでif (character)とか書けないはずである。でもなぜか書ける。これはちょっと調べたんだがどうやってるかわからなかった。たぶんどこかにboolへの暗黙的変換があるんだと思うけど……きもちわるっ。だがこの書き方は好きだけどな!
気持ちわるい点ではないけどちょっと書いてみてだめだった点も挙げておこう。上にも書いたけどコンポーネントの継承は思ったように動作してくれない。GetComponentで指定した型のコンポーネントを取ってくるときに派生元の型を指定しても派生先のコンポーネントはとってきてくれない。完全一致じゃないとだめなようだ。これはコンポーネントは継承しないで多数くっつけろということだろうから納得できる。
プログラムで変だと思ったのはこんなくらいかなぁ。そんなにいじってはないけど。あと途中まで参考にしてた本でカメラ操作のコードが意味不明すぎて混乱させられたくらいだが、これは本の問題だしな。どう見てもコードがバグってる。
ちょっと最後に気をつけたい点。Unityは簡単だけど、誰でも簡単にゲームが作れます的なものじゃない。キーを押したらキャラが動くってだけでもプログラムを書く必要はある。それにしても3Dのベクトル演算とかしないといけないので全くわからんと厳しいだろう。つってもそんなに難しいプログラムを書く必要があるわけじゃないのでちょっと勉強すりゃ誰でも作れるってのは間違いないけどね。プログラムなんて無理だわーと思いこんでる人はアクションゲームツクールなりRPGツクールなりでも買ってきましょう。
逆にある程度3Dの知識があるんだけどゲーム作ろうとするとあれこれ一から作らないといけなくて大変すぎるよ!という人にはとてもおすすめできる。まあゲームエンジンってそういうものだよね……。
あー、あと2Dゲーム作るのには全く向いてないと思うのでやめておこう。3D描画の2Dゲーならいけると思う。物理エンジンは素直に2Dいけるんだろうか?詳しい向き不向きは俺もまだわからん。
最後に、座標系が左手系だ。死ぬがよい。
最近GUIいじってただけだなぁ。Windows.Formsはもうしょぼくて死にたい。
設定保存がGUIに依存してたんでPeerCastStation.exeに移行させた。GUI側といえば設定保存にやけに依存したコードになっていたのでそれをやめて、さらにHTML UIに合わせて構成を変更。リレーツリー以外はたぶんそれなりに直せたと思う。
あとリレーツリーが問題だな。ツリー作る自体はたぶんできるんだが、更新がとてもやりづらい。更新前と後のデータが取れるだけだと、あれが消えてこれが移動してそれが追加されたなんかの情報を自前で作らないといけない。とても大変。一旦アイテム全部消して追加しなおすとかやるとWindows.Formsのツリービューではスクロールもリセットされるし更新も遅いとなんとも使いづらい。しかしどうしても高速に更新は難しそう。HTML UI側での実装も高速な方法はないような気が……。
と思ってたがそんなに頻繁に更新する必要ねえな、これ。今だと変更がある度に更新してたけど、よく見る物でもないし手動更新でも十分だ。そうするか。手動更新ならちょっと遅くてもいいだろう。遅いっつてもせいぜい1秒程度だろうしな。
早起きはまあまあできるようになってきた。最近あったかいしね。
アサシンクリード2を買ってきたんだけどやる暇あんまないな。
Unityをいじろうとして本を探してみた。さすがに人気なだけあっていくらかみつかるんだが、 本屋でぱらぱらとめくってみるに使える本はあまり無いようだ。
良さそうだった本はいずれも訳書だった。日本人が書いた本はいずれも「○○ということをやりたいと思います。」→「こういう操作をしてください。」→「できました。」というのが連続しているだけのもの。この手の本はあまり参考にならん。
訳書は2種類あって上下巻構成の物が良さそうだったけど2700円×2と高かったので諦めて3400円×1冊の方を買った。ポイントで買ったんだが実は6000ポイント以上貯まっていたようで2700円×2の方でも買えたようだ。ちゃんと確認しておけばよかった。まあそこまで本気でいじるつもりはないのでいいんだけど。
本は買っただけでまだ読んでない。23~25日にPeerCastハッカソンとかやるようなのでそこでいじろうかなぁと。いざとなったら別な物作ってたりするかもしれないけどなー。
HTMLベースのUIがだいたい出来た。やっとだよ。
KnockoutJSを使うようにしてからはそんなに難しいところもなくできた。難しそうなところは諦めるようにしたしな。
UI自体はできたのでPeerCastStation本体のAPI部分をちょこっと修正して組み込んだ。 しかし設定が保存されないなーと思ったらそうだった、設定の保存はGUIでやってるからされないんだわ。 設定の保存は整理しないといけないと前々から思ってはいたが、こりゃ早くやらないとだめだな。
あとGUIもHTMLから設定した値を見てくれなかったりしていろいろとめんどい。 これはGUI側直さないといけないな。
そんなこんなでUIをいじるのは一段落したので、今度はまたコア部分いじっていきたい。まずは設定保存だな。
久々に早起きできた。続けられるといいが、そのためには早く帰るのも徹底しないといけない。まだしばらく忙しいし難しいなぁ。
360のアンデフHDは結局近くのヨドバシで買った。WiiとかPSPのゲームが全然売ってないのに360が充実してる不思議な店である。助かる。
JavaScripter日記。
KnockoutJSでなかなかいい感じに進められるようになった。 最近仕事でWPFとさんざん戦ってたのでMVVMはまあまあ理解できることもあってとても便利だわこれ。 もちろん外れた使い方をしない限りにおいてだが。
相変わらずレイアウトというか見た目をなんとかするのがめんどくせぇな。 bootstrapを使うと手軽にそれっぽくはなるんだが、やっぱり気がつくとなんでもかんでもテーブルにしてしまう。 テーブル使わずに頑張ろうとするとどんどんdivがネストしていってわけわからなくなる。 WPFのGridはすげーよな。あれも最終的にはどんどんネストすることにはなるが、Grid.RowとかGrid.Columnの指定でネストを減らしてるのが上手い。
長々とやってたHTMLベースのUIももうちょっとかな。リレーツリーという大物がまだ手付かずだけどどうしても必要ってものでもないので後回しでもいい気がしてる。 リレーツリーはともかくチャンネル毎の出力接続一覧と、切断を実装しないといけないな。 作るのはそう難しくないんだがどう画面に収めるかが難しい。
HTMLのUIが出来たらUIのプラグイン化、ContentReader等のプラグイン化、ContentReaderの追加辺りをやりたいかな。いやUIのプラグイン化はもできてた気もするな。ContentReaderの追加にあたってはちょっとばかりプロトコルの拡張をしないといけないのでそれも検討したい。
まあ先のことはともかく今やってることを完成させないとな……。エラー処理さっぱりやってないんだけど大丈夫かなぁ。
仕事帰りに川崎のヨドバシ寄ったが360のアンデフHDが見当たらないんで店員に聞いたら限定版しかないっすっていうからそれを買ってきたんだが、帰ってみたらPS3版だった。だから360版のってわざわざ聞いたじゃねぇかよ……。店員はそもそも360版があるなんて知らなかったのかもしれん。もー、今時店舗でゲーム買うなんてやっちゃだめだなー。DL販売か通販しかだめだわー。
チャンネル一覧からチャンネル情報表示・編集は一通りできるようになったかなぁ。
チャンネル情報編集はダイアログで出してその中で編集するようにしたんだが、ダイアログ出すのにjQuery UIを使ったら見た目をどう合わせればいいのか悩ましいことになった。 まだページにスタイル設定してないんだけどYAMLとかいうCSSフレームワーク?というかデザインテンプレート?を使おうかと思ってた。しかしこれとjQuery UIの標準のテーマが全然合わねぇんだわこれが。 jQuery UIのテーマは自分でいじれるんだけど、ちゃんと合わせるには手間かかりそう。 じゃあもう諦めてjQuery UIのテーマで全部統一してしまおうかと思ったんだが、jQuery UI全体で定義されてるスタイル自体はほんと少なくて各プラグイン専用のスタイルが細かく定義されてた。 これを使いまわして自分で作ったページに適用しようってのは一瞬でそれっぽくできましたというわけにもいかねぇな。
jQuery UIのダイアログは使わないようにするかまで含めてしばらく悩んでみたが、見た目のことはまたあとで考えようということにして見なかったことにした。
HTMLベースのUIとして一番大事なところはできた。あとめんどくさいのはリレーツリーなんだが。リレーツリーの組み立てはめんどくさいので、サーバ側でツリー状態にまで組み立てたデータを受け取ってきてクライアント側は描画するだけにしたほうがいいな。いやめんどいんだよなこれー。折り畳みとかしなければそうでもないんだろうか。
設定部分もけっこうめんどくさい。ポート設定増やせたりするとな。まあこれはGUI版でも2つしか設定できないようになってるから、同様に2つに固定でもいいや。YPの設定はたくさんできなきゃいけないのがめんどくさいが、なんとかするしかねぇか。
配信開始用の画面どうしようか。従来のPeerCastだと個別の配信開始ページになってるけど、チャンネル一覧に配信開始ボタン→ダイアログ出て配信情報作って配信開始という方がUIとして自然かな。ページ分けるのめんどくさいってのもある。
ちょっとJavaScriptも慣れてきた。あまり難しいことは考えないのがよさそうだ。作るのがめんどくさいところは諦めてUIの仕様を変えるくらいでいい。あとやっぱりJsRenderなんかのテンプレートエンジンは便利だし必須だ。
と思ったがうそうそ。全然慣れてなかった。画面更新するのにページほぼ全部作り直してたんだが、リストにチャンネル2つくらいの状態でべつにパフォーマンスは問題ないもんだなーと思ってたら項目が3つになったら構築される様子が見えるくらい遅くなってしまった。うわやっぱりだめっすよねそうすね……。
はてさてこれはどうしたものか。やっぱりページ全部作っちゃだめで値だけ書き換えないとだめか。値だけ書き換えればいいのかっていうとあやしくて、たぶん更新された値だけ書き換えないといけないんだろうな。えーと、それやるんだった自分で書くとかばからしいからデータバインディングできるライブラリ使いたいっすね……。
JsViewsはすげー意味わかんねーしと思ってたらKnockoutJSというものを知った。少なくともドキュメントはしっかりしてるのでわかりやすい。あとdata-bind属性のところにどの属性へのバインディングですよーというのを書くのでJsViewsよりは動作が想像しやすい。JsViewsはなんか文字列を返してるように見えるんだがなぜか更新される!という不思議なものに見えるし。
しかしJavaScriptに慣れてきてわかったのが、ライブラリてんこ盛りにしないと全く使いものにならねーという事実とは。勉強するってそういうことか。最初っからjQueryつかってたから本当のJavaScriptは知らないわけだが、クロスブラウザとか考えると知りたくもないな。
朝全然起きれねー!
そりゃまあ帰ってきて気がついたら0時過ぎてててふとんに入ってからなかなか寝れずにふと時計を見ると1時半とかやってれば起きれるわけもない。強い意思を持って帰ってきてから何もしないをしないといけない。
JavaScripter日記。
JsRenderは便利だった。
いきなり話はかわるが、HTML埋め込みでひたすらJavaScriptを書いてる時にvimではなぜか埋め込みのスクリプトがオレンジ色というか特殊文字扱いの色で表示されてこまる。JavaScriptのシンタックスハイライト自体はされてるんだが、そのうえで通常の色で表示されるべきところが特殊文字扱いになってしまいとてもうざい。あとscriptタグ内は全部JavaScriptになってるけど、JsRenderのテンプレートは普通にHTML扱いして欲しいなぁ。
ということでなんとか作ってみたhtml.vimがこちら
source $VIMRUNTIME/syntax/html.vim
syn region javaScript start=+<script [^>]*type *=[^>]*x-jquery-tmpl[^>]*>+ keepend end=+</script>+me=s-1 contains=@htmlTop,htmlScriptTag,@htmlPreproc
hi def link javaScript NONE
特殊文字扱いになるのは、標準のhtml.vim内でなぜか特殊文字扱いにされてたのでNONEにしてそんなへんなことしないように回避。テンプレートを判別するのは標準のhtml.vimにVBScript対応なんかが入ってたのを参考に普通にHTML扱いにするようにした。テンプレート対応はもしかしたらもっと上手いこといけるかもしれないけど十分それっぽく動いてるのでこれでいいや。
こいつを~/vimfiles/syntaxなり~/.vim/syntaxにhtml.vimとしてぶちこんでおけばおk。
さて、JsRenderは便利だなーとやってたんだが、こいつは文字列を組み立てるだけの機能しかない。一旦作ったDOMの中身というかテーブルの中身を、あとから再取得して更新されたデータで書き換えましょう!ということをやろうとしたら大変だった。バインディング的なことをやりたいんだが、そう簡単にはいかなそうだなぁ。
こういう時に使うんだなJsViewsって奴は!ということで見てみたけど便利そう。便利そうだけどやばそうな臭いもする。そもそも何やってるかサンプルコード見てもよくわからん。何をやりたいコードなのかはなんとなくわかるんだが、一体どういう仕組みで動くんだかすぐ想像できなくて、上手く動かないときにひどいことになりそうだ。中で何かすごいことが起きてんじゃないかなにこれこわい的な。
サンプルをさらに見ていくとできるだけバインディングでなんとかする方向が自然になるようにできてるっぽい。そりゃまあそうだろうけど、やばそうだな。たぶんJsViewsを知らない人が見たら何がおきてるのかさっぱりわからんコードが完成するだろう。JsRenderは文字列置き換えしかしないからわかりやすいんだけどね。 ちょっとJsViewsに手を出すのはやめてめんどいところもせっせと書いていくことにしよう……。
また話はかわるが、jQueryのappendでどっかのノードにHTML文字列をくっつけることができるけど、これで作ったノードの参照を名前とかつけないで取得したかった。appendの戻り値はくっつけた親のノードだし無理っぽいなーと諦めてたんだが。jQueryだと$()にHTML文字列渡すと新しいノード作ってその参照を返してくれるらしい。それをそのままappendに渡せばおっけーと。おいおい$()ってセレクタ渡すとマッチするノードを検索して返してくれるだけだと思ってたがそんな機能もあったのかよ。他にも$()に関数渡すと$.ready()のかわりになるらしい。なにこれ万能すぎてきもちわるいだろ。そのうち$(‘なんかいい感じの動作’)とかやればもう全部出来るようになるんじゃねーかな。