Jewel-mmo開発日記

RubyでMMORPGを作る過程を記録する日記。 Yokohama.rb 発起人。
2006-02-28

[tDiary]いろいろ整理したい

他のブログツール(サービス)に移行する気はなくなったので、いろいろ整理したりカスタマイズしたりしたい。

2006-02-28

[Easy Rocket]子タスクの生成

require 'er'

class SampleImage < ER::ImageTask
end

ER::init 640, 480, 30.0
image = SampleImage.new(:image => 'sample.bmp')
image.create_sample_image(:image => 'sample.bmp', :pos => [100, 100, 0])
ER::loop do
  image.x += 1
  image.sample_image.x += 1
  image._draw
end

SampleImage を image の子タスクとして生成する場合は、

image.create_sample_image

とします。これで SampleImage のインスタンスが image の子タスクとして作られます。 image に対して sample_image というアクセサも同時に定義されます。

ローカル座標とワールド座標

子タスクは親タスクのローカル座標に配置されます(上記サンプルでは (100, 100) )。つまり親タスクを移動させると子タスクも同じ量だけ同時に移動します。上のサンプルでは親タスク (image) と子タスク (image.sample_image) の両方の x を加算しているので子タスクは親タスクの2倍の速度で右へ移動することになります。

子タスクのワールド座標は wpos で取り出すことができます。

image.create_sample_image.wpos

draw と _draw

draw を毎回呼ぶことで画面への描画を行います。

またループ内で image.draw ではなく image._draw を呼び出しています。 _draw は内部で自分および子タスクの draw を実行します。

デフォルトオプション

ちなみに SampleImage のデフォルトの画像をこのように設定しておけば、

class SampleImage < ER::ImageTask
  image 'sample.bmp'
end

次のように書くだけで同じ画像タスクを生成出来ます。

image = SampleImage.new
image.create_sample_image :pos => [100, 100, 0]
2006-02-28

[Easy Rocket]画像表示のサンプル

試行錯誤中の ER ですが、なんとなく決まってきた基本インターフェースの紹介です。

以下はウィンドウを生成して画面に sample.bmp を表示するだけのコードです。

require 'er'

ER::init 640, 480, 30.0
image = ER::ImageTask.new(:image => 'sample.bmp')
ER::loop do
  image.draw
end

ER::init の引数は (screen_x, screen_y, fps) です。

ループ中にこの1行を加えれば右に移動させることができます。

image.x += 1
2006-02-27

C 言語ユーザーが Ruby を使う

C 言語を知っていれば何となくRubyを書くことができます。 C++ の class を知っていれば Ruby の class を使ったオブジェクト思考プログラミングも可能です。

C で書いていたような書式の多くは Ruby にも通用します。(とくに)型がないぶんだけより書式がシンプルになりますが、ちょっとしたコツを覚えればすぐに Ruby で動作するプログラムを書けるようになります。

次のプログラムは C 言語の hello world です。

#include <stdio.h>

int main()
{
    printf("hello world\n");
    return 0;
}

これを Ruby で書くとこうなります。 Ruby では C 言語の main 関数のようなものは不要です。

puts("hello world")

puts は自動的に改行を行うメソッド(メソッドとは簡単に言えば C 言語の関数にあたるもの)です。これは次のように書く事もできます。

(追記: あとで気がつきましたが C 言語にも puts がありましたね。忘れていました(汗))

print("hello world\n")

print メソッドは改行の出力を行わないので文字列の最後に"\n"を追加してあります。また Ruby にも C 言語と同じように動作する printf があります。また通常は省略しますが文の最後にセミコロンを置くこともできます。

printf("hello world\n");

main メソッドと ruturn を書いてより C 言語ライクに記述することも可能です。

def main()
  printf("hello world\n");
  return 0;
end

main();

これはあくまで上記の C 言語のサンプルに無理矢理合わせて書いただけなので 実際は Ruby の hello world には main メソッドも return もセミコロンも不要です。また main メソッドが自動的に呼び出されるわけではないので最後で明示的に main メソッドを呼び出しています。

メソッドは次のように定義します。

def メソッド名(引数)
  処理
end

C 言語の関数定義には型が必要ですが Ruby のメソッド定義に型はありません。ちなみに引数はこんな風に記述します。

def main(str)
  print(str)
end

main("hello world\n")

いかがでしょうか? C 言語の知識があれば Ruby のプログラムを記述することは難しくありませんし、 Ruby の方がよりシンプルに同じ処理を記述できます。 Ruby には C 言語にはない便利な概念がたくさんありますが、さしあたってはそんなことを知らなくても Ruby の便利さを教授することはできます。

2006-02-25

[Ruby]「オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル」を読もう2

「Ruby の起動」はさしあたって興味がないので飛ばしました。本当は読んだほうがいいんでしょうけど……。

オブジェクトのページ

書いてあることがなかなか理解できません。ひとつずつじっくり見てみます。

  • Ruby で扱える全ての値はオブジェクトです。

これはそのままですね。

  • Ruby のオブジェクトに対して可能な操作はメソッド呼び出しのみです。

なるほど。

  • あるオブジェクトが反応できるメソッドは、そのオブジェクトが所属するクラスによって一意に決定します。

あれ得意メソッドは?

  • 所属するクラスはオブジェクト生成時に決まり、その後は特異クラスの導入以外、所属クラスが変わることはありません。

得意クラスってなんだろう。「特異クラスとは何ですか」を読んでわかりました。でもこれで所属クラスが変わるってどういうことだろう。

む、難しい……挫折しそうですががんばります。

2006-02-25

[Ruby]「オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル」を読もう

  • ドキュメントがないとゆっている人がいるという噂
  • このリファレンスマニュアルは良いらしい

この両極の話をよく聞きます。オレ自身「リファレンスマニュアル」をちゃんと読んだことはなく、すでに知っている機能の検索くらいにしか使っていません。最近は書いてあることが理解できるだけの基礎力がついてきた気がするのでちゃんと読んでみたいと思います。これは今年の目標にしたいですね。

リファレンスマニュアルの入手

まずはリファレンスマニュアルの入手から。「Windows HTML Help版」が好きなんですが、過去に自分でダウンロードしてこようとするとなかなか見つけることが出来なかった覚えがあります。最近は「Ruby の歩き方」があるので大変便利になりました(あれタイトルが「FirstStepRuby」に変わってる(追記:一時的な障害だったようです)。「Ruby の歩き方」って覚えやすくて気に入ってたのに)。

リファレンスマニュアルのデザイン

デザインの印象について述べておきます。このデザインはあまり好きではありません。はじめてみたときとても安っぽいものに見えて(そもそもリファレンスというのはプロによってしっかりデザインされるはずであると思い込んでいた。それまでOSS界隈に縁がなかったのもそういう思い込みを持つ原因だったかと。)、内容を含めたドキュメント全体が質の低いものに感じました。これは印象の問題でドキュメントの質なんて読む前だから知らないのに、とにかくそういうマイナス印象を持ったことを記憶しています。表面にとらわれずちゃんと物事の本質を見極められる人にはわからないことだと思います。Rubyistには本質の見極めがしっかりした人が多い気がするので、なはかなか理解されにくい部分かもしれません。私のように表面にとらわれやすい人に対して損をしているのではないでしょうか。

(しかし字が小さかったりコントラストが低かったりするだけでよく見えるなんてなんたることでしょう。)

はじめに

冒頭の文章(とくに「Rubyは単純で、分かりやすく」の部分)に疑問を感じますが、書いてあることの意味は全部理解できました。やっぱり私にとってはここはRubyをはじめたころ読むべきものじゃなくて今読むべきものですね。今読むと大変面白いです。私にとっての「はじめに」としては難しすぎましたが。

ちなみにクロージャを理解したのはつい昨日で、最近読み返している「ハッカーと画家ハッカーと画家 コンピュータ時代の創造者たち(ポール グレアム) 」に

def foo(n)
  lambda {|i| n += i } end

というコードが出てきてそれをたまたま調べていたためです。

前提として必要とされる知識が多い

ちょっと読むとすぐに感じることですがわからない単語が多いですね。

たとえば「awk の RS 変数のように働きます。」といわれてもわかりません。このリファレンスの対象者は決まっていたり明示されていたりしてるんでしょうか。

2006-02-24

[EesyRocket]タスククラスの未実装部分

  • 当たり判定
  • 感性(vメソッド、aメソッド、動摩擦係数、静止摩擦整数)

衝突

ちょっと考えて思いついた当たり判定の案としては、オブジェクトとオブジェクトの関連を保持するクラス(タスクかな)を設けるとどうか。

Collision.new :gtasks => [gtask0, gtask1, gtask2]

こんな感じで2つ以上のGameTaskのインスタンスを与えて初期化とか。

衝突したときの処理を当たり判定クラスに書くべきか、衝突するタスクそのものに書くべきかは不明。

--

同じオブジェクトでも衝突する相手によって振る舞いが変わるから(敵、敵の弾、パワーアップアイテムそれぞれで違う)、衝突処理はCollisionが持つべきだ。

時機とパワーアップアイテムが接触した場合に実行しなければならないのはこの2つだ。

  • 時機#powerup
  • パワーアップアイテム#destroy

そうか、Collision初期化時にこのメソッドも渡せばいいんだ。

Collision.new :gtasks => [[ship, :powerup], [item, :destroy]]

とかかな。

2006-02-23

[Easy Rocket]タスク

画像の説明

処理のプリミティブな単位を汎用的なタスククラスとして抽象化して、アプリケーションレベル(シーン生成とかキャラクタの生成・動作の部分)のコードを出来るだけシンプルにしようという試み。

Atsushi's Homepage 〜 タスクとはを参考に過去にも実装を試みた記録があるが、そのときはうまくいかなかったらしい。が今回はかねがねイメージしていた理想に近い形でタスクを使用したサンプルコードを書くことが出来た。

Taskの基本機能として実装したものは以下。

  • 木構造
  • 毎フレーム呼ばれるrunメソッドとdrawメソッド

Taskを継承したGameTaskには位置や向きの情報を持たせた。ImageTaskには2D画像イメージを描画する機能がある。

今回書いてみたアプリケーションのコードはこれ。えぐぜりにゃ〜にまだなっていないもので、キャラが武器を持っていてそれを動かせるだけ。SPACEキーで武器が伸びる。コードについてはフルスクラッチしているのでもはやえぐぜりにゃ〜の改造……なのか?(いちおうこのコードのライセンスは修正BSDということにしておきます)。

require 'er'
include ER::Key

class Font < ER::FontTask
  image 'images/font.bmp'
end

class Bg < ER::ImageTask
  image 'images/bg.bmp'
  transparent false
end

class Ring < ER::ImageTask
  image 'images/ring.bmp'
  center true
end

class Hand < ER::ImageTask
  images ['images/hand0.bmp', 'images/hand1.bmp']
  center true
  transform true
end

class Weapon < ER::GameTask
  attr_accessor :extend

  def after_create
    @l = 0
    7.times { create_ring }
    create_hand
  end

  def run
    @l += (extend ? 12 : 0) + (0 - @l) / 4
    @l = 0 if @l < 0
    (rings + hands).each_with_index {|e, i| e.pos.x = @l * i}
  end
end

class Ship < ER::ImageTask
  images ['images/ship0.bmp', 'images/ship1.bmp']
  center true

  def after_create
    create_weapon :pos => [64, 0, 0]
  end

  def run
    angle.z += 1
    weapon.extend = key.press?(SPACE)
    pos.x += (key.press?(LEFT) ? -8 : 0) + (key.press?(RIGHT) ? 8 : 0)
    pos.y += (key.press?(UP) ? -8 : 0) + (key.press?(DOWN) ? 8 : 0)
  end
end

class Scene < ER::GameTask
  def after_create
    create_bg
    create_ship :pos => [100, 100, 0]
    [
      ['',               [0,  0, 0]],
      ['',               [0, 16, 0]],
      ['MOVE=>[CURSOR]', [0, screen.h - 48, 0]],
      ['SHOT=>[SPACE]',  [0, screen.h - 32, 0]],
      ['WAIT=>[W]',      [0, screen.h - 16, 0]],
    ].each {|str, pos| create_font :str => str, :pos => pos }
  end

  def run
    fonts[0].str = "WAIT=%s" % (ER::sync? ? "ON" : "OFF")
    fonts[1].str = "%02dFPS" % ER::real_fps
  end
end

if $0 == __FILE__
  ER::do_init 640, 480, 30.0
  scene = Scene.new(:screen => ER::screen)
  ER::do_loop do
    scene._run
    scene._draw
    puts ER::real_fps
  end
end

以下でポイントだけざっと解説。

画像タスク

BgはImageTaskを継承した画像タスククラス。2つのクラスメソッドが定義されている。 image "file name" と書くとその画像タスククラスが使用するイメージファイルを指定出来る。配列を渡すと複数の画像でアニメーションを行う。transparent false で抜け色処理をOFFに。デフォルトは抜け色処理がON。

このBgクラスを使用しているのはSceneクラス。after_createメソッドの中で実行されているcreate_bgによって生成され、sceneの子タスクとして登録される。

タスクにはrunメソッドとdwawメソッドがあり、これらが毎フレーム呼ばれることになる。例えば次のように書けば毎フレーム1度ずつZ軸に対して回転する。

def run
  angle.z += 1
end

タスク生成と木構造

Weaponは時機が持つ武器となるクラス。武器は複数のRingと一番先にあるHandで構成する。Weapon#after_create でRingとHandを子タスクとして生成している。

7.times { create_ring }
create_hand

次のように書けば、RingとHandの間接構造をタスクの階層構造で実現することができる。

pa = self
7.times { pa = pa.create_ring }
pa.create_hand

scene以下全てのタスクは木構造になっているので、例えばトップレベルからWeaponのワールド座標を取得する場合は次のようになる。

scene.ship.weapon.wpos

posはローカル座標、wposは親タスクの階層をたどって計算されるワールド座標を示すVector3Dクラス

言語内DSL

クラスメソッドとして呼び出せるimage等のデフォルトオプションの定義やcreate_task_nameによる子タスクの生成のような記述方法はRailsに影響を受けている。ActiveRecordではhas_one, has_many 等のクラスメソッドでモデル間の関連を作るが、ER::Taskではいきなりcreate_task_nameメソッドでTaskNameを子タスクとして生成し同時にアクセサも作ってしまうので、こっちの方が凶悪かも。

これまでDSLって言われてもピンと来なかったんだけど、これがまさに言語内DSLってやつかな?

er.rb の実装

Ruby/SDL でシューティングのコードを参考にしつつ Ruby/SDL を使用している(ので上のコードは擬似コードじゃなくてちゃんと実行出来る)。

そしてevalを初めて使った。evalを使っていると頭がこんがらがってくる。Rubyのスコープの仕組みとか知らないことばかりで大変だった。が大変勉強になった。もっともっとRubyを勉強しないと。

速度的にはVector3Dクラスとそれを使った演算部分がネックだと思うので、その辺りを拡張ライブラリにしてしまえば問題ないと楽観視している。

EesyRocket ver 0.0

この間の勉強会で、適当に作ったソースコードを日記に張ったりすると、いい加減なコードでもそれがその人の実力と見なされて……という話があったけどそういうのぜんぜん気にしないので張っておく。長いけど、張っておかないとこういうのすぐなくしちゃうので。張っておくと後で検索出来て便利だし。

(補足: eval("#{name}s << task") の部分と parent= で #{name}s を変更をしていない部分が手抜き)

er.rb

require "optparse"
require "sdl"

class Numeric
  def to_rad
    Math::PI * self / 180 
  end
end

class Array
  def merge_hashs
    inject({}) {|opt, e| e.merge(opt) }
  end
end

module ER
  class Vector3D < Array
     def x ; self[0] ; end
     def y ; self[1] ; end
     def z ; self[2] ; end
     def x=v ; self[0] = v ; end
     def y=v ; self[1] = v ; end
     def z=v ; self[2] = v ; end
     def +dpos
       Vector3D[self[0] + dpos[0], self[1] + dpos[1], self[2] + dpos[2]]
     end
  end

  class Task
    def self.default_options *args
      @default_options = args.flatten
      class_eval args.flatten.map {|e| <<TEXT
        attr_accessor :#{e}
        def self.#{e} v ; @default_#{e}= v ; end
        def self.default_#{e} ; @default_#{e} ; end
        def self.default_#{e}_defined? ; defined? @default_#{e} ; end
TEXT
      }.join
    end

    def self._default_options
      @default_options ||= []
      @default_options + (defined?(superclass._default_options) ? superclass._default_options : [])
    end

    attr_reader :tasks, :boot_counter, :parent

    def initialize *args
      @parent = nil
      @tasks = []
      @boot_counter = 0
      before_create
      _create *args
      after_create
    end

    def before_create
    end

    def after_create
    end

    def _create *args
      self.class::_default_options.each do |e|
        eval "self.#{e} = self.class.default_#{e} if self.class.default_#{e}_defined?"
      end
      args.merge_hashs.each do |key, value|
        self.send "#{key}=", value
      end
    end

    def inspect
      "#{self.class} #{@boot_counter}"
    end

    def parent=v
      @parent.tasks.delete self if @parent
      @parent = v
    end

    def children
      @tasks.map {|e| [e, e.children] }
    end

    def _run
      run
      @tasks.each {|e| e._run }
      @boot_counter += 1
    end

    def _draw
      draw
      @tasks.each {|e| e._draw }
    end

    def run
    end

    def draw
    end

    def method_missing(meth_name, *args)
      if /^create_(\w+)\z/.match(meth_name.to_s) && name = $1
        klass = Kernel.const_get(name.split('_').map {|e| e.capitalize }.join)
        task = klass.new(*args)
        task.kind_of?(Task) || raise("`#{klass}.new.kind_of?(Task) => false'")
        task.parent = self
        @tasks << task
        unless eval("defined? #{name}s")
          self.class.class_eval("def #{name}s ; @#{name}s ||= [] ; end")
	  self.class.class_eval("attr_accessor :#{name}")
          eval("self.send '#{name}=', task")
        else
          self.class.class_eval %Q|def #{name} ; raise "Use `#{name}s' method!" ; end|
        end
        eval("#{name}s << task")
        return task
      end
      super
    end
  end

  class GameTask < Task
    default_options :pos, :angle, :scale

    def screen=(v); @@screen = v; end
    def screen; @@screen; end
    def self.screen=(v); @@screen = v; end
    def self.screen; @@screen; end

    def pos ; @pos ; end
    def pos=args ; @pos = Vector3D[*args] ; end
    def angle ; @angle ; end
    def angle=args ; @angle = Vector3D[*args] ; end
    def scale ; @scale ; end
    def scale=args ; @scale = Vector3D[*args] ; end

    def _create *args
      options = args.merge_hashs
      %w(screen).each do |e|
        next unless options.has_key? e.to_sym
        self::send "#{e}=", options[e.to_sym]
        options.delete e.to_sym
      end
      super options
      self.pos ||= [0,0,0]
      self.angle ||= [0,0,0]
      self.scale ||= [1,1,1]
    end

    def wpos
      if parent 
        if (r = parent.wangle.z) != 0
          r = r * Math::PI / 180
          x = pos.x * Math.cos(r) - pos.y * Math.sin(r)
          y = pos.x * Math.sin(r) + pos.y * Math.cos(r)
          parent.wpos + [x, y, pos.z]
        else
          parent.wpos + pos
        end
      else
        pos
      end
    end

    def wangle
      parent ? wangle = angle + parent.wangle : angle
    end
  end

  class ImageTask < GameTask
    default_options %w(transparent transform center image images)
    attr_reader :w, :h
    def cx ; center ? w / 2 : 0 ; end
    def cy ; center ? h / 2 : 0 ; end

    def initialize *args
      @transparent = true
      @transform = false
      @center = false
      super
    end

    def load_image(fname)
      image = SDL::Surface.load(fname)
      image.set_color_key(SDL::SRCCOLORKEY, image[0,0]) if transparent
      image.display_format
    end

    def images=v
      @images = v.to_a
      @image = @images.first
      @loaded_images = v.map {|image| load_image(image) }
      @w, @h = @loaded_images.first.w, @loaded_images.first.h
      @loaded_images
    end
    def image=v ; self.images = v ; end

    def draw
      return unless @loaded_images
      image = @loaded_images[boot_counter / 8 % @loaded_images.size]
      if !transform || (r = wangle.z) == 0 && scale.x == 1
        SDL.blit_surface(image, 0, 0, 0, 0, screen, wpos.x - cx, wpos.y - cy)
      else
        SDL.transform_blit(image, screen, r, scale.x, scale.y, cx, cy, wpos.x, wpos.y, 0)
      end
    end
  end

  class FontTask < GameTask
    default_options :str

    @@bm_fonts= {}
    def self.image v
      class_eval "def image ; '#{v}' ; end"
    end

    def _create *args
      unless @@bm_fonts.has_key?(image)
        @@bm_fonts[image] = SDL::BMFont.open(image, SDL::BMFont::TRANSPARENT)
      end
      @font = @@bm_fonts[image]
      super
    end

    def image
      raise
    end

    def draw
      return if str.empty?
      @font.textout(screen, str, wpos.x, wpos.y)
    end
  end

  module Key
    include SDL::Key
  end

  def self.screen; @screen; end
  def self.real_fps; @real_fps; end
  def self.sync?; @do_sync; end

  def self.do_init screen_w, screen_h, fps
    @do_full = false
    ARGV.options do |opt|
      opt.on("-f") {|v| @do_full = true }
      opt.parse!
    end
    SDL.init(SDL::INIT_VIDEO | SDL::INIT_AUDIO)
    flag = SDL::HWSURFACE | SDL::DOUBLEBUF
    flag |= SDL::FULLSCREEN if @do_full
    @screen = SDL.set_video_mode(screen_w, screen_h, 0,  flag)
    SDL::Mixer.open(22050 * 4)
    @do_sync = true
    @real_fps = 0
    @fps = fps
    screen
  end

  def self.do_loop
    count = 0
    tm_start = tm1 = SDL.get_ticks
    while true
      while event = SDL::Event2.poll
        case event
      when SDL::Event2::Quit
          exit
        when SDL::Event2::KeyDown
          case event.sym
          when SDL::Key::ESCAPE
            exit
          when SDL::Key::W
            @do_sync = !sync?
          end
        end
      end
      screen.fillRect(0, 0, screen.w, screen.h, [0, 0, 0])
      SDL::Key.scan
      yield
      screen.flip
      # FPSを固定
      if sync?
        tm2 = SDL.get_ticks
        diff = tm1 + (1000 / @fps) - tm2
        SDL.delay(diff) if diff > 0
      end
      tm1 = SDL.get_ticks
      # 実際のFSPを計算
      count += 1
      if count >= 30
        count = 0
        @real_fps = 30 * 1000 / (tm1 - tm_start)
        tm_start = tm1
      end
    end
  end

  class GameTask
    def key
      SDL::Key
    end
  end
end
2006-02-22

またえぐぜりにゃ〜

先日も書いたこのゲーム実は結構気になっていて、その後の動向もチェックしてたり。

面白いのはそれなりに派手なゲーム画面であるのに絵の量が少ないこと。同梱されているall.bmpを見るとわかるのだけど時機だって2パタンの絵しかない。キャラクタの向きを普通ならキャラクタ自体を回転させて表現したくなるが、それを武器の向きで表現しているのもうまい。

そして改造や再配布OKとのことなのでただいま改造中。もちろんRuby使ってます。

2006-02-21

[Ruby]mapって便利

いままでよくこんなコード書いていたけど、

@items = []
5.each { @items << create_item }

map使えばこうやって書けるじゃん。

@items = (0...5).map { create_item }

追記:さらにこうも書けるそうです。

@items = Array.new(5){ create_item } 
2006-02-21

お金がない。

どんどん厳しくなってくる。最近は技術書を買うのもやめてるし、それ以外にものを買うこともない。主な出費は以下。

  1. 職場の飲み会
  2. フットサル
  3. 勉強会の交通費、懇親会

フットサルは他の人みたいにウェアーとか余分なものは一切買わないので交通費とコート代だけ。

やっぱり出費が大きいのは職場の飲み会と懇親会。つまり交際費。

2006-02-21

[るびま]Rubyist Magazine 0013 号 リリース

今回も何もやっていません。編集の方々はすごいと思います。お疲れ様でした。

2006-02-20

[Ruby]えぐぜりにゃ〜

これHSPで実装されているのだけど、こういうのがRubyで簡単に書けるといいなあ。2Dだけじゃなくて3Dも。

2006-02-18

[Rails]さくらのレンタルサーバでRuby on Railsを動かしてみる

とりあえずDBを使う前の状態で動かしてみた。

RubyOnRails(さくらサーバ編)さくらのレンタルサーバにRuby on Railsを入れる手順 を参考にやったら意外にあっさりと動いた。

まず ruby 1.8.4 をインストール。

wget ftp://ftp.ruby-lang.org/pub/ruby/ruby-1.8.4.tar.gz
tar xzvf ruby-1.8.4.tar.gz
cd ruby-1.8.4
./configure --prefix=/home/xxx/ruby
make
make install

インストールした ruby にパスを通す。

手元の環境はzshなので次のようにした。ここは各シェルにあわせて。

#.zshrc
PATH=/home/xxx/ruby/bin:$PATH
export PATH

rubygems のインストール。

wget http://rubyforge.org/frs/download.php/5207/rubygems-0.8.11.tgz
tar xzvf rubygems-0.8.11.tgz
cd ../rubygems-0.8.11 
ruby setup.rb 
gem install rails  --include-dependencies

あとはさくらのレンタルサーバにRuby on Railsを入れる手順 にあるとおり。

rails プロジェクトとして例えば hoge を作成。

rails hoge

hoge/publicの.htaccessを書き換え

下の3行をコメントアウト。

AddHandler fastcgi-script .fcgi
AddHandler cgi-script .cgi
Options +FollowSymLinks +ExecCGI

config/environment.rbを書き換え

ファイルの先頭に下の2行を追加。

$LOAD_PATH.push("/home/xxx/ruby/lib/ruby/site_ruby/1.8")
$LOAD_PATH.push("/home/xxx/ruby/lib/ruby")

hoge/publicにシンボリックリンクを張る

/home/xxx/hoge

/home/xxx/www/hoge

で公開する場合は次のように。

cd /home/xxx/www/
ln -s /home/xxx/hoge/public hoge

動作確認

http://xxx.sakura.ne.jp/hoge/ にアクセスしてみる。「Welcome aboard」が表示されるはず。

ドメインを使っている場合「ドメインの使用方法を選択してください」ところが「エイリアスとして使用する」になっているとダメ。「リダイレクトとして使用する」はOK。

Hello world

./script/generate controller hello

app/controllers/hello_controller.rb を編集。

class HelloController < ApplicationController
  def index
  end
end

vim app/views/hello/index.rhtml を作成。

Hello world!

http://xxx.sakura.ne.jp/hoge/hello にアクセスしてみる。「Hello world!」と表示されるはず。

2006-02-18

ここの Ruby のバージョンを ruby 1.8.4 にしてみた。

ruby 1.8.4 (2005-12-24) [i386-freebsd4.10]
2006-02-18

はてなダイアリーはじめました

<URL:http://d.hatena.ne.jp/kibidan5/>

今まではてなダイアリーを使ったことがなかった(なぜか他人のはてなダイアリーにも縁がなかった)のですが、最近増えた巡回先のはてな率がやけに高いので気になってたんです。

ブログサービスはmixiしか使ったことなかったんですが、他のブログを使うとtDiaryが客観的に見えてきて面白いですね。

いくつのも場所に日記を分散させるのは嫌なんですけど、いろんなサービスを使ってそれぞれの特徴とか違いを知ることには価値があるなと思いました。

2006-02-17

[Ruby]private とかは最近は書きません。

Rubyをはじめたばかりの頃はそれまでC++ばかりを使っていたせいか、privateであるべきものにprivateがついていないのが気持ち悪かったんですが、なんでもpublicにするという人の話を聞いて何となくそうしているうちに違和感がなくなりました。何も考えなくなったし記述量も減ったので楽です。これまで特にはまったり不都合に感じたこともありません。あんなに神経質にprivateとかprotectedとか書いていたのに書かなくても別に不都合がないなんて不思議な話です。

Railsのコントローラークラスではhide_actionでなくてprivateを使ってますけど。

2006-02-17

日記

獲物にされてた。

ポジションペーパー書かないとなあ。

2006-02-16

[開発ログ]戦闘システムの実装

もうここ3週間くらい戦闘システムをやっています。だいたい形になったのですが、Rails、Rubyプロセスのバトルサーバー、Ajaxなインターフェースという3つのシステムが絡み合うので思いのほか苦労し時間がかかりました。

いまだにJavaScriptがほとんどわかっていません。

2006-02-15

ライセンスについて

ライセンスについて迷い続けています。このRailsで実装しているMMORPGのコードのライセンスをどうするか。そもそもソースを公開すべきか非公開にすべきか。

実現しようとしているものはツールでなくてサービスです。ソースを公開しても実際にそのソースを使う人はほんのごく一部です。ひとつのサービス運営に対して多数のユーザーがつくという構造です。この構造ではオープンソースのメリットがほとんど得られないように思います。

更にソースを公開した結果複数の運営者によるサービスの乱立が起るとユーザーの分散を招くので、多くのユーザー獲得を目標とするMMORPGというサービスにはデメリットのように思います。

ソースは公開するけどそれを使ったサービス運営は禁止する、みたいなライセンスってないですかね。コードの断片を再利用されることについてはなんら問題ないし、公開することによってセキュリティーホールの発見や対策が迅速に進んだり、バグの発見やパフォーマンスの改善案が得られる可能性はあるかもしれません。

上記の話はサーバー部分に限った話で、クライアントで動作するプログラム(Webベースだとほとんどないんですが)については修正BSDライセンスでいいと思っています。

2006-02-07

[Rails] 開発中のJewel-mmoのソースの行数を数えてみる

app ディレクトリの中のソースの行数を数えてみると、

total    2185 lines
.rb      1104
.rhtml   1081

.rb の部分が1104。書き始めてからおよそ2ヶ月。思ったより少ないな。 2000行以内で公開したいところ。

ちなみにtestディレクトリは以下。テストの方が行数が多いようだ。

total    1777 lines
.rb      1777

app内のソースコード別に見ると以下の通り。まだコントローラーとモデルが半分くらいしかできてないかなあ。

111    app/controllers/application.rb
24     app/controllers/area_controller.rb
2      app/controllers/cardshop_controller.rb
88     app/controllers/character_controller.rb
16     app/controllers/cron_controller.rb
34     app/controllers/doll_controller.rb
75     app/controllers/event_controller.rb
66     app/controllers/ghost_controller.rb
59     app/controllers/shop_controller.rb
45     app/controllers/user_controller.rb
55     app/controllers/world_controller.rb
30     app/controllers/party_controller.rb
59     app/controllers/battle_controller.rb
14     app/helpers/application_helper.rb
2      app/helpers/user_helper.rb
2      app/helpers/area_helper.rb
2      app/helpers/cardshop_helper.rb
2      app/helpers/character_helper.rb
2      app/helpers/cron_helper.rb
2      app/helpers/doll_helper.rb
2      app/helpers/event_helper.rb
2      app/helpers/ghost_helper.rb
2      app/helpers/shop_helper.rb
2      app/helpers/world_helper.rb
2      app/helpers/party_helper.rb
2      app/helpers/battle_helper.rb
5      app/models/area.rb
55     app/models/character.rb
123    app/models/doll.rb
50     app/models/doll_card.rb
3      app/models/doll_level.rb
4      app/models/event.rb
28     app/models/ghost.rb
6      app/models/good.rb
3      app/models/job_level.rb
4      app/models/map.rb
4      app/models/shop.rb
78     app/models/user.rb
7      app/models/user_character.rb
17     app/models/party.rb
8      app/models/battle.rb
7      app/models/battle_command.rb
1104   .rb total

行数を数えるのがなぜか好き。以前はソースの行数が多いとその分仕事をした気がして嬉しかったんだけど、今はサービス内容に対してソースの量が少ないことに喜びとか優越感を感じるようになった。

2006-02-06

ワンダと巨像日記

プレー初日にmixi書いた感想をここにも張っておく。

amazon 視点で注目すべき所は2つ。

ひとつ目はテキスト表現が最小限であること。神殿の中で女の子が横たわって眠っている。剣をかざすと飛行石のそれのように倒すべき巨像の方向に光が伸びる。それ以上のストーリーはわからないがそれで十分だ。(追記:オープニングムービーを面倒で飛ばしていた)

ふたつ目は小さな人間が巨人にしがみついてよじ登って脳天に剣を突き刺して倒すというのをアクションゲームとして見事に体現したこと。まるでレゴラスがトロルやオリファントをよじ登ってしとめるそれだ。

2006-02-06

キャッチマインド(IE限定)というゲーム

数人でひとつの部屋に入り、一人がお題を絵に描いて他のプレイヤーがお題を当てるというゲーム。クイズ番組でよくあるそれを体感出来。要登録だがおすすめ。