StarRubyでFC風文字表示(とキーボードからの文字入力)を試すでござるの巻

簡単な作業ツールをRfmで作り始めたが、カッとなってUIをFC風にすることにした。

DXRubyでやろうと思いましたが、Rfmと一緒に使うとnet/protocolあたりでtimeoutのエラーが出てしまう*1のでStar Rubyに変更。

表示はともかく、キーボードからの文字入力は大変。ゲーム用ライブラリだからしょうがないけど。

require 'starruby'
include StarRuby

#画面表示系のサポート処理
module StarRuby::Texture::Support
  FCWIN_FONT = Font.new("MS Gothic", 12)
  FCWIN_FORECOLOR = Color.new(255, 255, 255, 255)
  FCWIN_BACKCOLOR = Color.new(0, 0, 0, 255)

  #----------------------------------------------------------------------
  #FC風枠の描画
  def self.draw_fc_frame(aScreen, aLeft, aTop, aWidth, aHeight)
    aScreen.fill_rect(aLeft, aTop, aWidth, aHeight, FCWIN_BACKCOLOR)
    aScreen.fill_rect(aLeft + 1, aTop + 1, aWidth - 2, aHeight - 2, FCWIN_FORECOLOR)
    aScreen.fill_rect(aLeft + 3, aTop + 3, aWidth - 6, aHeight - 6, FCWIN_BACKCOLOR)
    aScreen[aLeft + 1, aTop + 1] = FCWIN_BACKCOLOR
    aScreen[aLeft + aWidth - 2, aTop + 1] = FCWIN_BACKCOLOR
    aScreen[aLeft + 1, aTop + aHeight - 2] = FCWIN_BACKCOLOR
    aScreen[aLeft + aWidth - 2, aTop + aHeight - 2] = FCWIN_BACKCOLOR
  end

  #----------------------------------------------------------------------
  #FC風ウィンドウの描画
  def self.draw_fc_window(aScreen, aLeft, aTop, aTexts)
    win_width = 0
    win_height = 0
    aTexts.each do |line|
      ts = FCWIN_FONT.get_size(line)
      win_width = ( win_width < ts[0] ) ? ts[0] : win_width
      win_height += ts[1]
    end
    win_width += 16
    win_height += 12
    draw_fc_frame(aScreen, aLeft, aTop, win_width, win_height)
    text_x = aLeft + 8
    text_y = aTop + 6
    aTexts.each do |line|
      aScreen.render_text(line, text_x, text_y, FCWIN_FONT, FCWIN_FORECOLOR)
      ts = FCWIN_FONT.get_size(line)
      text_y += ts[1]
    end
  end
end

#入力系のサポート処理
module StarRuby::Input::Support
  SHIFT_CHAR_MAP = {
    :a => ['a', 'A'], :b => ['b', 'B'], :c => ['c', 'C'], :d => ['d', 'D'],
    :e => ['e', 'E'], :f => ['f', 'F'], :g => ['g', 'G'], :h => ['h', 'H'],
    :i => ['i', 'I'], :j => ['j', 'J'], :k => ['k', 'K'], :l => ['l', 'L'],
    :m => ['m', 'M'], :n => ['n', 'N'], :o => ['o', 'O'], :p => ['p', 'P'],
    :q => ['q', 'Q'], :r => ['r', 'R'], :s => ['s', 'S'], :t => ['t', 'T'],
    :u => ['u', 'U'], :v => ['v', 'V'], :w => ['w', 'W'], :x => ['x', 'X'],
    :y => ['y', 'Y'], :z => ['z', 'Z'],
    :d0 => ['0', ''], :d1 => ['1', '!'], :d2 => ['2', '"'], :d3 => ['3', '#'],
    :d4 => ['4', '$'], :d5 => ['5', '%'], :d6 => ['6', '&'], :d7 => ['7', '\''],
    :d8 => ['8', '('], :d9 => ['9', ')'],
    :numpad0 => ['0', ''], :numpad1 => ['1', ''], :numpad2 => ['2', ''], :numpad3 => ['3', ''],
    :numpad4 => ['4', ''], :numpad5 => ['5', ''], :numpad6 => ['6', ''], :numpad7 => ['7', ''],
    :numpad8 => ['8', ''], :numpad9 => ['9', ''],
    :space => [' ', ' '], :add => ['+', ''],  :backslash => ['\\', '|'],
    :backquotes => ['@', '`'], :closebrackets => ['[', '{'],
    :comma => [',', '<'], :decimal => ['.', ''], :divide => ['/', ''],
    :equals	=> ['^', '~'], :minus => ['-', '='], :multiply => ['*', ''],
    :openbrackets => ['@', '`'], :period => ['.', '>'], :quotes => [':', '*'],
    :slash => ['/', '?'], :semicolon => [';', '+'],  :subtract => ['-', '']
  }

  @@last_keys = []

  #----------------------------------------------------------------------
  #文字列編集メソッド(引数は編集する文字列、戻り値は [編集結果文字列, [残りのキーコード]])
  def self.edit_text(text)
    #キーを取得
    keys = Input.keys(:keyboard).dup
    #SHIFT状態を作成
    shift_on = false
    if keys.include?(:capslock)
      shift_on = ! shift_on
      keys.delete(:capslock)
    end
    if keys.include?(:lshiftkey) || keys.include?(:rshiftkey)
      shift_on = ! shift_on
      keys.delete(:lshiftkey) if keys.include?(:lshiftkey)
      keys.delete(:rshiftkey) if keys.include?(:rshiftkey)
    end
    #前回と同じ状態ならなにもせず終了
    return text, keys if @@last_keys == keys

    #キー状態を保存
    last_keys = @@last_keys
    @@last_keys = keys.dup
    #前回と重複する文字を削除
    keys.delete_if {|x| last_keys.include?(x)}
    #バックスペース処理
    if keys.include?(:back)
      keys.delete(:back)
      text = text.slice(0, text.length - 1) if text.length > 0
    #文字入力処理
    else
      shift_idx = shift_on ? 1 : 0
      keys.each do |key|
        key_info = SHIFT_CHAR_MAP[key]
        text += key_info[shift_idx] if key_info
      end
    end
    #文字列全体を返す
    return text, keys
  end
end

英語キーボードには対応してませんのであしからず。


で、使ってみる。

$KCODE="u"

require 'starruby'
require 'support/starruby_support'
include StarRuby

account = ""
password = ""
dummy = ""
last_keys = nil
progress = :input_account
Game.run(320, 120, :title => "Login Prompt", :cursor => true, :window_scale => 2) do |game|
  game.screen.clear
  #キー入力
  keys = []
  case progress
  when :input_account
    account, keys = Input::Support.edit_text(account)
    progress = :input_password if keys.include?(:enter) and last_keys != keys
  when :input_password
    password, keys = Input::Support.edit_text(password)
    progress = :complete if keys.include?(:enter) and last_keys != keys
  else
    dummy, keys = Input::Support.edit_text(dummy)
  end
  break if keys.include?(:escape)
  last_keys = keys.dup

  #表示
  Texture::Support.draw_fc_window(game.screen, 20, 20,
    ["'あかうんと'をにゅうりょくせよ!:#{account}#{'_' if progress == :input_account }",
     "'ぱすわーど'をにゅうりょくせよ!:#{'*' * password.length}#{'_'  if progress == :input_password }"])
  if progress == :complete
    Texture::Support.draw_fc_window(game.screen, 100, 45, ["しょりはかんりょうした!"])
  end
end

もうこれでUI作ってリリースする!


...MacでやってみたらColor.new(0, 0, 0, 255)がブルーになりました。・・・これは見なかったことに。

*1:DXRubyが標準のtimeoutメソッドを書き換えてるのかしら?