トップ «前の日記(2006-10-22) 最新 次の日記(2006-10-24)» 編集

Route 477



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(..) みたいな感じで書くと思う。 プレイヤーはゲーム中いろんなところから参照される。例えば「自機の方に歩いてくる敵キャラ」を実装しようと思ったら、 敵キャラがプレイヤーの位置を知ってないといけないわけで。

で、どうするか。

  1. グローバルに持つ($player)
  2. グローバルに持つ(Singleton)
  3. 引数で渡す
  4. 引数で渡す(構造体)

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] PluginListdocs.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から自動的にリンクするような仕組みがあれば良いのだが。

[tDiary] plugin.tdiary.org?

もしくはRAAみたいに、プラグイン集のサイトがあれば良いのかも。

vim.orgのscriptsのコーナーみたいな。

[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
  #ボムを撃つ

のようにする。

本日のツッコミ(全4件) [ツッコミを入れる]
mts (2006-10-28 11:27)

5.グローバルアクセサ関数を用意する<br>てのはどうなのかな?GetPlayer(int nIndex);みたいな。<br>これだと2人プレイも可能だし、デコイ的効果の実装も簡単にできるかな、と。<br>ちなみに<br>6.(プレーヤ情報以外も含む)グローバル環境を用意してそれへのアクセサを用意する<br>は簡単に作れて簡単に使えるんだけれどグローバル変数とほとんど同じだしちょっと危険。

yhara (2006-10-31 03:54)

おお、詳しい人からのコメントが!ありがとうございます。<br>5>なるほど、それは思いつきませんでした。オブジェクト指向にこだわりすぎてたかも。

ohai (2006-11-01 14:59)

インスタンス変数経由で渡す、とか

yhara (2006-11-04 02:52)

newするときに渡しておくってことすか。いろんな方法があるなぁ。