2006-12-01
■ [Ruby/SDL] C言語のenumの代わりには、StringじゃなくてSymbolを使おう
要するに、
case key when "UP" then @y -= 1 when "DOWN" then @y += 1 when "RIGHT" then @x += 1 when "LEFT" then @x -= 1 end
じゃなくて
case key when :up then @y -= 1 when :down then @y += 1 when :right then @x += 1 when :left then @x -= 1 end
のように書こう、って話を少し。
この手の「プログラム内部でしか使わない文字列」は、以下のような理由でSymbolを使ったほうが良いです。
Symbolとは (概念的に)
シンボルはLisp由来の機能で、 リファレンスでは「任意の文字列と一対一に対応するオブジェクトです」と定義されています。
概念的には、何かを「指し示す」ためのものがSymbolだと思えば良さそうです。 例えば 「attr_accessor :x」の「:x」 のように「インスタンス変数@xを指し示す」とか、上の例のように「上下左右の方向を指し示す」とか。 ゲームプログラミングでは、C言語のenumのように「何かの種類」を指し示す時に使うことが多いと思います。
Symbolとは (実際的に)
実装上は、シンボルの実体は数値になっています。 また、同じ文字列のシンボルは実体を共有します。 つまり、文字列一つに対し一つの数値が対応します。*1
シンボルは、to_sを呼ぶことで文字列に変換することができます(:hoge.to_s は "hoge" を返す)。
シンボル名には大文字も小文字も使えますが、小文字が使われることが多いようです(また、:hoge と :HOGE は区別される)。
Symbolを使うメリットを以下に挙げます。
(1) シンボルの方が軽い
Symbolは数値で実装されているので、
- オブジェクト生成が速い
- 文字列よりも高速に比較できる
といった特徴があります。特に、シンボルは最初の :hoge のときのみインスタンスが作られるので非常に高速です。 (文字列はリテラルが出てくるごとにString.newが呼ばれる)。
(2) コードが読みやすくなる
シンボルは文字列に比べて使い道がはっきりしているので、可読性が向上します。
(3) 無用なバグを避けることができる
シンボルはimmutableなので、Stringのように内容があとから書き換えられる心配がありません。
(4) Symbolの方がタイプ数が少ない
文字列だと「"」が2個要る(しかもシフトも必要)のに対し、Symbolは「:」1個なので打つのが楽です(^^;
参考
*1 といってもmd5みたいなものではなく、使われたシンボルから順に番号を振っていく感じ
■ [ruby] immutableについての補足
immutableとは、「内容が不変である」という意味です。
例えばStringはimmutableではないので、
# 銭形警部 keibu = "Zenigata Keibu" p keibu #=> "Zenigata Keibu" # だと思ったら…(べりべり) keibu.gsub!(/Zenigata/, "Lupin") keibu.gsub!(/Keibu/, "the 3rd") # ばかもーん、そいつがルパンだ!追えーっ! p keibu #=> "Lupin the 3rd"
という感じで、「同じインスタンスなのにいつの間にか内容が入れ替わっている」ということが起こり得ます。
一方Symbolにはgsub!のような内容を変えてしまうメソッド(「破壊的なメソッド」と呼ぶ)は存在しないので、 このようなことは起こりません。
Rubyの組み込みクラスの中では、シンボルや数値がimmutableです。配列やハッシュはmutableです。
2006-12-02
■ [softs] SVKのチュートリアルを書いた
http://mono.kmc.gr.jp/~yhara/w/?SvkTutorial
超適当ですが。
学園祭の間は部室のサーバが止まるので、その間はSVKを利用してローカルのリポジトリにコミットしてました。 普通にTortoiseSVNのdiffとか「元に戻す」が使えるので便利でした。
途中でローカルブランチを増やしたりややこしいことをしたせいで、元のリポジトリへの反映に結構苦労しましたが、 なんとかコミットできたようです。(^^;
今日知ったこと:
- svk smerge --baseless を使うと、祖先が共通してなくてもむりやりマージできる
- svkの本がある (Free Bookなので、出版されたわけではない?)
2006-12-04
■ [ruby] define_methodで変なメソッド名が作れるんだが
Hpricotのソースを見てたら 意味不明な記述に遭遇。
module Container::Trav def self.filter(tok, &blk) define_method("filter[#{tok.is_a?(String) ? tok : tok.inspect}]", &blk) end 中略 filter '@^=' do |attr,val,i| get_attribute(attr).to_s.index(val) == 0 end
メソッド名が filter[@^=] とか、ありえないですよね。
irb(main):011:0> class Object; define_method("filter[@^=]"){ "asdffdsa" }; end => #<Proc:0x00002aaaabb5db68@(irb):11> irb(main):017:0> 1.methods.find{|m| m=~/filter/} => "filter[@^=]"
と思いきや、普通に定義できたwww
irb(main):013:0> 1.filter[@^=] SyntaxError: compile error (irb):13: syntax error 1.filter[@^=] ^ from (irb):13
呼び出しはもちろんsyntax error。
irb(main):014:0> 1.__send__("filter[@^=]") => "asdffdsa" irb(main):015:0>
と思いきや、__send__使えば呼べるらしい。
define_methodで普通許されない名前のメソッドが定義できるのって仕様なんですかね?
2006-12-06
■ [ruby] やたーSubversionのログビューアできたよー
ViewVCがうまくインストールできなかったので自分で作った。後悔はしていない。
実装はRubyのCGI + Subversion付属のRubyバインディングです。svnコマンドは使っていません。
機能的には大したことないんですが(というかまだログ見るしかできない)、 「コミットログがリビジョンとリビジョンの間に表示される」というのが特徴かも知れません。 (この方が時系列から考えると自然な気がしませんか?)
「部内のプロジェクトの進行状況を適当に眺めたい」というのが開発動機なんで、今後の方針としては 実用よりも変な機能をいっぱい搭載して遊びたいです(笑)。「各プロジェクトのコミット時刻グラフ」とか。
ちなみに「(diff)」って書いてあるところを押すとdiffが見れる予定なんですが、まだ動きません。 というかワーキングコピー作らずにdiff取る方法がわかんね。ファイル2つ取ってきて Algorithm::Diffでも使うかなぁ。
年末の忙しさによってはこのまま更新しない可能性もあるので、 ソースをこっそり上げておきます。
あと調査メモがRuby/SVNに。
2006-12-10
■ [ruby][memo] Hpricot気になることメモ
- "E F" と "E > F"が逆じゃね? > http://code.whytheluckystiff.net/hpricot/wiki/SupportedCssSelectors
- inner_textってないんだっけ => HEADでは実装されてる
- nth-child(n) って0 originでいいのか
- 「spanタグとaタグの間」みたいのはうまく書けないなぁ
- Elem#children ってユーザが触っていいのかな
しかしHpricotって、それ自身がオリジナルの言語っぽい感じだな。正規表現リテラルがそうであるように。 複雑なことをやろうとするとどうしても泥臭くなるのも似てる。
2006-12-11
■ [Plagger] WindowsにPlaggerを入れる手順 (2006年年末版)
Windows版のPerl(ActivePerl)にはppmというパッケージマネージャがあって、非常に簡単にインストールできます (パッケージを提供してくれているppm.tcool.orgの中の人に感謝)。
下記の情報は2002年12月(Plagger 0.7.17)時点のものなので、最新版では事情が変わっているかも知れません。 まぁ何かあったらきっと誰かが記事(もしくは公式Wikiとか?)に書いてくれるでしょう。きっと。
必要な知識
- MS-DOSプロンプトが使える
- tar.gzが解凍できる
ActivePerlを入れる
最新版のはppm4が入っていてややこしいらしいので、ppm3の入っている5.8.7.815をインストールします。
ppmでPlaggerを入れる
C:\>ppm ppm> rep add tcool http://ppm.tcool.org/server/ppmserver.cgi?urn:PPMServer Repositories: [1] ActiveState Package Repository [2] tcool ppm> rep up tcool Repositories: [1] tcool [2] ActiveState Package Repository ppm> install Plagger ... ppm> q
数十分かかるので気長に待ちましょう。途中でdllをダウンロードするか?みたいに聞かれたらEnterを押してください。
assetsを入れる
現時点ではppm経由ではassetsが入らないので、 Plagger-0.7.17.tar.gz をダウンロードし、Plagger-0.7.17\assets を適当な場所(C:\Perl\site\lib\Plagger\ とか)にコピーしてください。
使ってみる
以下の内容を sample.yaml という名前で保存します。(c:/perl 等はインストールしたフォルダに合わせて書き換えてください)
global: plugin_path: - c:/perl/site/lib/Plagger/Plugin/ assets_path: c:/perl/site/lib/Plagger/assets timezone: Asia/Tokyo log: level: info plugins: - module: Subscription::Config config: feed: - url: http://d.hatena.ne.jp/keyworddiary/Plagger?mode=rss #一度送信した記事を除く場合 # - module: Filter::Rule # rule: # module: Deduped #本文も取ってくる場合 # - module: Filter::EntryFullText? - module: Publish::Gmail config: mailto: yhara@example.jp mailroute: via: smtp host: (プロバイダのメールサーバのアドレス)
MS-DOSプロンプトを開いて、Plaggerを実行してみます。
C:\> plagger -c sample.yaml Plagger [info] plugin Plagger::Plugin::Subscription::Config loaded. Plagger [info] plugin Plagger::Plugin::Publish::Gmail loaded. (略) Plagger::Plugin::Aggregator::Simple [info] Fetch http://d.hatena.ne.jp/keyworddi ary/Plagger?mode=rss Plagger::Plugin::Aggregator::Simple [info] Aggregate http://d.hatena.ne.jp/keywo rddiary/Plagger?mode=rss success: 21 entries. Plagger::Plugin::Publish::Gmail [info] Sending はてなダイアリー - 「Plagger」を 含む日記 to yhara@example.jp
のようになったらOKです。指定したメールアドレスにはてなの新着日記が送信されたことと思います。
注:
- 以上の手順でインストールできるのはPlaggerの標準機能のみです(プラグインによっては追加のライブラリを必要とするものがあります)。
参考:
■ [Plagger] Filter::Pipeがうまく動かない
なんかタイムアウトとか言われる。
調査したのは
- Win32 + Plagger 0.7.17
- Linux + Plagger 0.7.15
で、エラーメッセージは
- Windows
IPC::Run: timeout on timer #1 at C:/Perl/site/lib/IPC/Run.pm line 2932
- Unix
Plagger::Plugin::Filter::Pipe [error] filter timeout
という感じ。
パイプ先のプログラムは、タイムアウトエラーになるのが
- ruby -e "print $stdin.read"
- nkf --unix
- /usr/bin/nkf
で、OKなのが
- cat
- tee /tmp/asdf
とか。むー。Publish::Pipeの方はrubyでもnkfでもうまく行くんだけど。
2006-12-20
■ [Ruby/SDL] キーボード情報の扱い(Ver.2)
danさんのところで話がでてたので、思い出したように書いてみる。
前回から紆余曲折を経て、現在はこんな感じになってます。 最初にキーと通称(symbol)の対応を定義しておいて、以降のコードでは通称のみを使います。 こうすると、ゲーム内では「:up」というシンボル一つで「上キーとKキー*1とジョイパッドの上方向」を まとめて扱うことが出来て便利です。
class Input #ユーザ用 define_key SDL::Key::ESCAPE, :exit define_key SDL::Key::UP , :up define_key SDL::Key::DOWN , :down define_key SDL::Key::LEFT , :left define_key SDL::Key::RIGHT , :right define_key SDL::Key::K , :up define_key SDL::Key::J , :down define_key SDL::Key::H , :left define_key SDL::Key::L , :right define_key SDL::Key::RETURN, :ok define_key SDL::Key::Z , :ok define_key SDL::Key::X , :cancel define_pad 1, :ok # ○ define_pad 2, :cancel # × #NF用 define_key SDL::Key::F1, :reset define_key SDL::Key::F10, :goal if DEBUG_MODE #デバッグ用(そのうち消す) define_key SDL::Key::W , :toggle_window define_key SDL::Key::K0 , :fast_move define_key SDL::Key::V , :event_sample #製作補助用 define_key SDL::Key::R , :reload_images define_key SDL::Key::I , :inspect define_key SDL::Key::A , :auto_reload define_key SDL::Key::Y , :inspect_events #マップエディタ用 define_pad 3, :put_chip # □ define_pad 0, :erase_chip # △ define_pad 9, :yuutai # select define_pad 6, :prev_chip # L1 define_pad 7, :next_chip # R1 define_key_shift SDL::Key::K1, :force_encount #! define_key SDL::Key::B , :set_base_chip define_key SDL::Key::P , :put_chip define_key SDL::Key::G , :get_chip define_key SDL::Key::E , :erase_chip define_key SDL::Key::COMMA , :prev_chip, :repeats define_key SDL::Key::PERIOD, :next_chip, :repeats define_key SDL::Key::S , :save_map define_key SDL::Key::U , :yuutai define_key SDL::Key::TAB , :next_capital end end
定義用メソッド(Rails風?)には以下のような種類があります。
- define_key キー定数, シンボル
- キーを定義する
- define_key キー定数, シンボル, :repeats
- キーを定義する(擬似キーリピートあり)
- define_key_shift キー定数, シンボル
- シフト+キーを定義する(ctrl,alt,metaもあるよ!)
- define_pad ボタン番号, シンボル
- パッドのボタンを定義する
パッドの上下左右は、自動的に :up, :down, :left, :right というシンボルに対応づけられます。固定です。
使い方は
input = Input.new loop do input.clear #12/23追記:これを忘れてました。すいません input.poll input[:up] #=>「上が押されていたか」 input.pushed? :ok #=>「OKボタンが押されたか」 .. end
という感じで、実装はinput.rbに。
*1 roguelike風
2006-12-25
■ [Ruby/SDL] Ruby/SDLスターターキット : Rubyはbetter HSPに成り得るか
面倒なインストールは一切不要、ダウンロードして解凍するだけでゲーム開発が始められる「Ruby/SDLスターターキット」をリリースしました。
http://mono.kmc.gr.jp/~yhara/w/?RubySDLStarterKit
今まで「Ruby/SDLは面白そうだけどインストールが面倒だなぁ…」と思っていた人はぜひ試してみてください。 簡単*1 なサンプルゲームも入っています。
最近まつもとさんの日記でHSPの話が出ていましたが、 そこでHSPのメリットとして挙げられていた条件は
- フリーウェアである → OK
- セットアップが簡単 → 解凍するだけでOK
- ウィンドウ、画像の操作が簡単 → ゲームについてはOK*2
- 文法が単純で習得が容易 → たぶんOK (チュートリアル次第?)
- 自作EXEの配布が可能 → OK
のように、これでほぼ満たすことができたと思います。これでRubyも、HSPの「手軽さ」にだいぶ近づけたのではないでしょうか。
が、本当にHSPくらい普及するためには、
- チュートリアル
- 豊富なサンプル
- ユーザー掲示板
- 1キーでヘルプが見られたり、デバッグを補助してくれたりする開発環境(RDEが使えるか?)
- 入門書(書きたいなぁ)
など、道のりはまだまだ長そうです。*3
ともあれ、まずは実際にゲームを作ってみる人が増えないことにはどうしようもありません。 ということで、Ruby初心者な人も、Rubyistな人も、正月休みにゲーム製作にチャレンジしてみませんか? 楽しいゲームをお待ちしています。 (あと、apple catcherに見栄えのいい絵を付けてくれる方も募集しています^^;)
2006-12-27
■ [Ruby/SDL] RSKit(2)
(まさか、 i386-mswin32/ 内はライブラリサーチの対象外とか!?)。
[http://d.hatena.ne.jp/cyross/20061226#p1より引用]
_|‾|○ごめんなさい、その通りです…。年内に修正版をリリースします。とりあえず、lib/rskit/on_*.rbを修正すれば直ります。
■ [Ruby/SDL][memo] exe生成器
よく考えると、Exerb使ったexe生成器を同梱すればいいんだよな。 「pack.exeをダブルクリックすると、必要なリソースを全てまとめたexeファイルを生成します」みたいな。 vrubyでGUI作ってアイコンとか指定できるようにすると良さそう。
exeに入れるもの:
- *.rb
- *.so
- *.dll
あと音声や画像もexeに入るといいんだけど…exeに入れてしまうとUnixで動かせないんだよなぁ。 せっかくRuby/SDLなんだから「WindowsでもUnixでも遊べるゲームができます!」って方が格好いいんだが。
まぁとりあえず音声・画像は後回しか。
2006-12-28
■ [ruby] tempdir.rbが欲しい
Tempfileのディレクトリ版みたいなやつ。tmpdirはシステムのテンポラリディレクトリのパスを教えてくれるだけだ し*1。
いつもは
tmpdir = Dir.tmpdir k = 0 k += 1 while File.exist?(File.join(tmpdir, "hoge#{k}")) dir = File.join(tmpdir, "hoge#{k}") Dir.mkdir(dir)
みたいな感じで作ってるんだが。dir = Tempdir.new("hoge") でディレクトリ作ってパス名返してくれると嬉しい。
あ、Tempdir.new("hoge") do |dir| ... end とかだともっといいかも(ブロック抜けるときにディレクトリをrm_fする)。
*1 あと片方がtempで片方がtmpなのは気持ち悪い(笑)
■ [prog] HSPの凄さ
HSPってのは多分、「必要最低限」で成り立ってるんだな。
HSPのサイトを見てもおにたま氏の名前しかないから、多分ほとんどを一人で開発しているのだろう。 GUIアプリからゲームやネットワークまで、さまざまなソフトが作れる環境を開発し、なおかつそれが ここまで広く使われるってのは並みのことじゃない。ていうか、普通は一人でできるってレベルじゃない(笑)。
一人で開発を進める欠点は、どうしても作業量が限られてしまうってことだ。少ない作業量で 言語とライブラリとエディタとドキュメントとウェブサイトを開発しようと思ったら、 どうしたってそれぞれは「広く浅く」にならざるを得ないだろう。 そういう視点で見れば、HSPの言語仕様ってのは限りなく「パーズが簡単なように」できてることに気づく *1。
おにたま氏の凄さは「どこまで実装すれば使ってもらえるのに十分か」という「必要最低限」を 見切る能力にあるんじゃないか。
*1 たとえば、最近まで戻り値の構文が無かったりとか、最近まで演算子が右から順に結合してたとか
■ [ruby] .と..がうざい
Dir.entriesとかDir.foreachとかDir#eachが.と..を渡してくるのがうざい!デフォルトで削っといてくれよ!
まぁDir.glob("foo/bar/*") とかやればいいんだけどさ。
つーかRubyのFileとDirって、他の標準ライブラリに比べて全体的にわかりにくい気がする。 抽象度が低い(ファイルシステム寄りな)APIだからか?
Ruby初心者だったときに結構はまった。 ファイルの移動がmoveじゃなくてrenameだったりとか。ディレクトリの移動もFile.rename使うとか。 Unixに慣れてる人なら当たり前なんだろうけどなぁ。
(追記:そうか、Pathname使えばいいのか。)
Before...
□ yhara [r1911にて、 * ruby -e 'print ARGF.read' * nkf --unix いずれも正しく実..]
□ yhara [っと、Windowsだとまだ動かないみたい? 家に帰ったら追試してみます。 あと、0.7.17だとnkf -uでもt..]
□ yhara [Windowsでも正しく動くのを確認しました。ありがとうございました。]