2006-10-23
■ [ruby][python] いやなブログ:文字列操作の比較表: Ruby, Python, JavaScript, Perl, C++
expandtabsはそんなに使わないので s.gsub(/\t/," ") でいいけど、String#starts_with?(other) はRubyにも欲しいなぁ。
■ [Ruby/SDL] プレイヤー情報をどうやって共有するか
唐突にゲームプログラミングについて書く。
前提。各キャラクターのオブジェクトはactとrenderというメソッドを持っていて、actで1フレーム分の動きを実行し、 renderでキャラを画面に描画する。
Rubyはオブジェクト指向言語なんで、プレイヤーキャラは多分 @player = Player.new(..) みたいな感じで書くと思う。 プレイヤーはゲーム中いろんなところから参照される。例えば「自機の方に歩いてくる敵キャラ」を実装しようと思ったら、 敵キャラがプレイヤーの位置を知ってないといけないわけで。
で、どうするか。
- グローバルに持つ($player)
- グローバルに持つ(Singleton)
- 引数で渡す
- 引数で渡す(構造体)
1はグローバル変数を使うという方法だけど…、Ruby的にグローバル変数はあんまりおすすめできない。 まぁプレイヤーだけならグローバルでも何とかなるけど、キー情報とか、Screenとか、スコアとか、共有したい情報はたくさん あるので、それ全部グローバル変数にするとソースが読みにくくなってしまう(どこで変更され得るか全く分からないので)。
2は添付ライブラリのsingleton.rbを使う方法。「Player.instance.x」 みたいな感じで書ける。 ただちょっと長いし、原理的に二人プレイが実装できない(笑)。「インスタンスが一つしか生成できない」ってのがSingletonだからね。 長さについては
class Player include Singleton def self.x; self.instance.x; end #← ... attr_reader :x
とかしとくと 「Player.x」 でアクセスできるようになるけど、ちょっと気持ち悪いかなぁと思う。
3は、敵の動作のときにenemy.act(@player) みたいな感じで渡してやる方法。昔はこれを使ってた。
でも、プログラムが複雑になってくるとactに渡す引数がだんだん増えてくるので、最近は構造体を作ってそれを渡すことにしてる。これが4。
def enemy.act(info) → info.player, info.key, info.screen, etc.
actとrenderで別の構造体を用意するべきかについてはちょっと考え中。
■ [tDiary] PluginList と docs.tdiary.org
小人さんしてみた。
- toc_hereを追加(どういう分類があるか分かりにくかったので)
- 全ての「プラグイン集に収録」という文字列に、docs.tdiary.org側のドキュメントへのリンクを付加
- tDiaryのHEADで添付されていて、載っていなかったものを追加
- 「→配布サイト」をddじゃなくdtの側に移した(解説へのリンクが全て上の段に来るようにした)
編集履歴が残るようなので、不都合があったらrevertしてくださいませ。
tDiaryについては、そもそもdocs.tdiary.orgとtdiary-users.sourceforge.jpにドキュメントが分散してるってのが何とかならんかなぁという感じだが…。
それぞれの特徴は以下。
- 設定画面でプラグイン名をクリックするとdocs.tdiary.orgに飛ぶ
- googleで「tdiary plugin」を検索すると、tdiary-users.sourceforge.jpが一番最初に出てくる
現状での使い分けは、
- 公式プラグインはdocs.tdiary.org
- 野良プラグインはtdiary-users.sourceforge.jp (公式プラグインはdocs.tdiary.orgにリンク)
という説明が一番近いと思うが、docs.tdiary.orgにも野良プラグインが結構載っている。
設定画面から手軽に見られる、という点ではdocs.tdiary.orgに項目を作る意義はあると思うんだけど、全てのプラグインが docs.tdiary.orgとtdiary-users.sourceforge.jpに載っているってのはちょっと二度手間っぽくて嫌だなぁ。
tdiary-users.sourceforge.jpに載っている野良プラグインを、docs.tdiary.orgから自動的にリンクするような仕組みがあれば良いのだが。
■ [Ruby/SDL] キーボード情報の扱い
現プロジェクトで使ってるやり方がなかなか便利なので紹介。
キーボードから発生する情報は、
- あるキーが押された(push) → SDL::Event2::KeyDown
- あるキーが押されている(press) → SDL::Key.press?()
という2種類。
基本的にゲームではpressを扱うことが多いのだが(「ボタンを押している間だけ移動する」とか)、 たまにpushだけを区別したい場合もある(ネームエントリーとか)。以下の方法はその両方に対応している。
まず、「Ruby/SDLのキー」と「ボタンの種類」を対応付けるデータを用意する。
KEY_MAP = { SDL::Key::UP => :up, SDL::Key::DOWN => :down, SDL::Key::LEFT => :left, SDL::Key::RIGHT => :right, SDL::Key::Z => :shot, SDL::Key::X => :bomb SDL::Key::ESCAPE => :exit, }
次に、メインループで以下のようなコードを実行する。
loop do #event key = {} while event = SDL::Event2.poll case event when SDL::Event2::Quit key[:exit] = :push when SDL::Event2::KeyDown sym = KEY_MAP[event.sym] key[sym] = :push if sym end end break if key[:exit] #scan SDL::Key.scan KEY_MAP.each do |k,v| key[v] = :press if SDL::Key.press?(k) && key[v].nil? end ...
と、ハッシュkeyに {:shot => :press} とか {:bomb => :push} のような感じで情報が入る。
単に「押されていたかどうか」を確かめる場合は
if key[:shot] #ショットを撃つ
のようにすれば良い。pushだけを扱いたいときは、
if key[:bomb] == :push #ボムを撃つ
のようにする。
5.グローバルアクセサ関数を用意する<br>てのはどうなのかな?GetPlayer(int nIndex);みたいな。<br>これだと2人プレイも可能だし、デコイ的効果の実装も簡単にできるかな、と。<br>ちなみに<br>6.(プレーヤ情報以外も含む)グローバル環境を用意してそれへのアクセサを用意する<br>は簡単に作れて簡単に使えるんだけれどグローバル変数とほとんど同じだしちょっと危険。
おお、詳しい人からのコメントが!ありがとうございます。<br>5>なるほど、それは思いつきませんでした。オブジェクト指向にこだわりすぎてたかも。
インスタンス変数経由で渡す、とか
newするときに渡しておくってことすか。いろんな方法があるなぁ。