鳥小屋.txt

ゲーム作ったり、Web的なことしたり、ぐだぐだしたりしますねヽ(・x・)ノ

第9羽「青山ツクールマウンテン」

この投稿は「ごちうさ住民 Advent Calendar 2014」の9日目の記事です。

昨日はkazuheiさんの『私の所持金52円!!』でした。
雑コラメーカーのスマホ対応、心よりお待ちしております(チラッチラッ


ごちうさ1羽コメントスクリプトさん for RGSS3

概要

RPGツクールVX Ace(RGSS3)用のスクリプト素材です。
ゲーム画面にニコニコ動画の『ご注文はうさぎですか? 第1羽「ひと目で、尋常でないもふもふだと見抜いたよ」』のコメントを表示します。

f:id:ru_shalm:20141208005237p:plain

f:id:ru_shalm:20141208005245p:plain

f:id:ru_shalm:20141208235409p:plain

利用規約

いつも通りテキトーにどうぞ。許諾も表記も何もナシでOKです。
ただし、画面に流れるコメントにはそれぞれ権利があるのでご注意ください。

なお、本スクリプトに関しては完全に一切のサポートを行いません。よろしくお願いします(ぺこり)

スクリプト

# coding: utf-8
#===============================================================================
# ■ ごちうさ1羽コメントスクリプトさん for RGSS3
#-------------------------------------------------------------------------------
# 2014/12/09 Ru/むっくRu (@ru_shalm)
#-------------------------------------------------------------------------------
# ゲーム画面にニコニコ動画の
# 『ご注文はうさぎですか? 第1羽「ひと目で、尋常でないもふもふだと見抜いたよ」』の
# コメントを表示します。
#===============================================================================

module Torigoya
  module Base64
    def self.encode(str)
      [str].pack('m*')
    end

    def self.decode(str)
      str.unpack('m*').join
    end
  end
end

module Torigoya
  module Win32
    INTERNET_OPEN_TYPE_PRECONFIG = 0
    INTERNET_SERVICE_HTTP = 3

    def self.internet_open
      ::Win32API.new('wininet.dll', 'InternetOpen', %w(p l p p l), 'l')
    end

    def self.internet_connect
      ::Win32API.new('wininet.dll', 'InternetConnect', %w(p p l p p l l l), 'l')
    end

    def self.http_open_request
      ::Win32API.new('wininet.dll', 'HttpOpenRequest', %w(p p p p p p l l), 'l')
    end

    def self.http_send_request
      ::Win32API.new('wininet.dll', 'HttpSendRequest', %w(p p l p l), 'i')
    end

    def self.internet_read_file
      ::Win32API.new('wininet.dll', 'InternetReadFile', %w(l p l p), 'l')
    end

    def self.internet_close_handle
      ::Win32API.new('wininet.dll', 'InternetCloseHandle', %w(l), 'l')
    end
  end
end

module Torigoya
  class URI
    # URLエンコード
    # @param [String] str
    # @return [String]
    def self.encode(str)
      str.to_s.gsub(/([^A-Za-z0-9\-_\.!\~\*\'\(\);\/\?\:@&=\+\$,\[\]])/) do
        '%' + $1.unpack('H*').join('').scan(/.{2}/).join('%').upcase
      end
    end

    def self.parse(uri)
      self.new(uri)
    end

    def initialize(uri)
      /\A(?<scheme>[^:]+):\/\/(?<host>[^\/:]+)(?::(?<port>\d+))?(?<path>\/[^\?]*)?(?:\?(?<query>.*))?\z/ =~ uri
      @scheme = scheme.downcase
      @host = host
      @port = (port || default_port).to_i
      @path = path || '/'
      @query = query || ''
    end
    attr_reader :scheme
    attr_reader :host
    attr_reader :port
    attr_reader :path
    attr_reader :query

    private
    def default_port
      case self.scheme
      when 'http'
        80
      when 'https'
        443
      else
        raise 'unknown scheme'
      end
    end
  end

  module Http
    def self.client
      @client ||= Client.new
    end

    class Client
      SESSION_NAME = 'Torigoya RGSS'

      def get(uri, params = {}, headers = [])
        request('GET', uri, params, headers)
      end

      def post(uri, params = {}, headers = [])
        request('POST', uri, params, headers)
      end

      private
      def request(method, uri_str, params = {}, headers = [])
        uri = URI.parse(uri_str)
        payload = nil
        result_body = []

        headers.push "Host: #{uri.host}"
        if params.kind_of?(String)
          path = uri.path
          payload = params
        else
          if method == 'GET'
            # TODO: uri.queryとdeep_mergeしましょう
            query = convert_query(params)
            path = "#{uri.path}?#{query}"
          else
            query = convert_query(params)
            path = "#{uri.path}#{uri.query ? "?#{uri.query}" : ''}"
            payload = params
          end
        end

        begin
          handle = Torigoya::Win32.internet_open.call(SESSION_NAME, Torigoya::Win32::INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0)
          http_session = Torigoya::Win32.internet_connect.call(handle, uri.host, uri.port, nil, nil, Torigoya::Win32::INTERNET_SERVICE_HTTP, 0, 0)
          http_request = Torigoya::Win32.http_open_request.call(http_session, method, path, nil, nil, nil, 0, 0)
          Torigoya::Win32.http_send_request.call(http_request, headers.join("\n"), -1, payload, (payload ? payload.size : 0))

          while true
            buf = "\0" * 10240
            read_size = "\0" * 4 # DWORD
            break if Torigoya::Win32.internet_read_file.call(http_request, buf, buf.size, read_size) && (size = read_size.unpack('i*')[0]) == 0
            result_body.push buf[0, size]
          end
        rescue => e
          puts e.inspect
          puts $@
          result_body = []
        ensure
          Torigoya::Win32.internet_close_handle.call(http_request) if http_request
          Torigoya::Win32.internet_close_handle.call(http_session) if http_session
          Torigoya::Win32.internet_close_handle.call(handle) if handle
        end

        result_body.join
      end

      def convert_query(params = {})
        params.to_a.map { |n| n.map { |n| URI.encode(n) }.join('=') }.join('&')
      end
    end
  end
end

module Torigoya
  module PoorXml
    # 超簡易XMLパーサー
    # - UTF-8以外読み込ませちゃダメ
    # - コメント(<!-- -->)を含むやつもダメ
    # - というかいろいろ読めない
    class Parser
      # 初期化
      # @param [String] raw_doc XMLテキスト
      def initialize(raw_doc)
        @raw_doc = raw_doc
      end

      # パース処理
      # @return [Node] XMLのrootノード
      def parse
        buffer = ''
        str = @raw_doc.dup
        stacks = []
        root = Node.new('root')
        stacks.push root

        while (str.strip! || str.size > 0)
          case str
          when /^(<\?xml.+\?>)/ # XMLヘッダ(UTF-8以外読まないので無視)
            str.sub!($1, '')
          when /^(<([^>]+)\/>)/ # 単独タグ
            params = $2
            str.sub!($1, '')
            child = line_to_node(params)
            stacks.last.children.push child
          when /^(<\/([^>]+)>)/ # 閉じタグ
            name = $2
            str.sub!($1, '')
            stacks.pop
          when /^(<([^>]+)>)/ # 開始タグ
            params = $2
            str.sub!($1, '')
            child = line_to_node(params)
            stacks.last.children.push child
            stacks.push child
          when /^([^<]+)</
            text = $1
            str.sub!($1, '')
            child = TextNode.new('text')
            child.text = text
            stacks.last.children.push child
          else
            raise 'invalid xml'
          end
        end

        root
      end

      private
      def line_to_node(line)
        name, attr_str = line.split(/\s+/, 2)
        node = Node.new(name)

        if attr_str
          i = 0
          key = ''
          value = ''
          read_key_flag = true
          while i < attr_str.size
            if read_key_flag
              case attr_str[i]
              when /\s/
              when '='
                read_key_flag = false
                i += 1 # 次は「"」のはずなのでスキップ
              else
                key << attr_str[i]
              end
            else
              case attr_str[i]
              when "\\"
                i += 1
              when '"'
                read_key_flag = true
                node.attributes[key] = value
                key = ''
                value = ''
              else
                value << attr_str[i]
              end
            end
            i += 1
          end
        end

        node
      end
    end

    class Node
      # 初期化
      # @param [String] name ノード名
      def initialize(name)
        @name = name
        @attributes = {}
        @children = []
      end
      attr_reader :name
      attr_reader :attributes
      attr_reader :children

      # ノードの子の中から指定の名を持つものを取り出す
      # @param [String] name 検索するノードの名前
      # @return [Array] 配列
      def [](name)
        self.children.select { |child| child.name == name }
      end

      # 属性の取得
      # @param [String] name 属性名
      # @return [Object] 指定属性の値
      def attr(name)
        self.attributes[name]
      end

      # ノード内に含むテキストを返す
      # @return [String] 子ノードのテキストを全て連結した文字列
      def text
        self.children.map(&:text).join('')
      end

      # Hash化する
      # @return [Hash] Hash化した値
      def to_h
        {
          name: @name,
          attributes: @attributes,
          children: @children.map(&:to_h),
        }
      end
    end

    class TextNode < Node
      # 初期化
      # @param [String] name ノード名
      def initialize(name)
        super
        @text = ''
      end
      attr_accessor :text

      # Hash化する
      # @return [Hash] Hash化した値
      def to_h
        super.tap { |o| o['text'] = @text }
      end
    end
  end
end

module Torigoya
  module NicoVideo
    class Comment
      def initialize(no, user_id, vpos, mail, text)
        @no = no
        @user_id = user_id
        @vpos = vpos
        @mail = mail
        @text = text
      end
      attr_reader :no
      attr_reader :user_id
      attr_reader :vpos
      attr_reader :mail
      attr_reader :text
    end

    class CommentLoader
      def initialize(thread_id, message_url)
        @thread_id = thread_id
        @message_url = message_url
      end

      def fetch
        fetch_comment_xml(fetch_threadkey)
      end

      def fetch_threadkey
        resp = Torigoya::Http.client.get(
          "http://flapi.nicovideo.jp/api/getthreadkey",
          thread: @thread_id
        )
        response_set = {}
        resp.split('&').each do |n|
          set = n.split('=', 2)
          response_set[set[0].to_s] = set[1].to_s
        end
        response_set
      end

      def fetch_comment_xml(threadkey_data, size = 1000)
        thread_key = threadkey_data['threadkey']
        force_184 = threadkey_data['force_184']
        message_server_url = 'http://msg.nicovideo.jp/24/api/'
        data = "<thread res_from=\"-#{size}\" version=\"20061206\" thread=\"#{@thread_id}\" threadkey=\"#{thread_key}\" force_184=\"#{force_184}\" scores=\"1\" />"
        Torigoya::Http.client.post(
            message_server_url,
            data,
        )
      end
    end
  end
end

module Torigoya
  class RabbitComment
    # ごちうさ1羽のコメントサーバ情報
    # ログイン状態でgetflv API叩いて取得できるよ
    MESSAGE_SERVER_URL = 'http://msg.nicovideo.jp/24/api/'
    THREAD_ID = '1397552685'

    def self.load
      rc = RabbitComment.new
      rc.data['comments']
    end

    def initialize
      @data = nil
      load_comment
      unless @data['fetched_at'] and @data['fetched_at'] > Time.now.to_i - (60 * 60 * 3)
        @data['fetched_at'] = Time.now.to_i
        @data['comments'] = fetch_comment
        File.open('tmp/comment.dat', 'wb') do |f|
          Marshal.dump(@data, f)
        end
      end
    end
    attr_reader :data

    def load_comment
      Dir.mkdir('tmp') unless File.exist?('tmp')
      if File.exist?('tmp/comment.dat')
        begin
          File.open('tmp/comment.dat', 'rb') do |file|
            @data = Marshal.load(file)
          end
        rescue => e
          puts e.inspect
        end
      end
      @data ||= {}
    end

    def fetch_comment
      nico_comment = Torigoya::NicoVideo::CommentLoader.new(THREAD_ID, MESSAGE_SERVER_URL)
      parser = Torigoya::PoorXml::Parser.new(nico_comment.fetch)
      doc = parser.parse
      chat_elements = doc['packet'].first['chat'].map { |node|
        Torigoya::NicoVideo::Comment.new(
          node.attr('no').to_i,
          node.attr('user_id'),
          node.attr('vpos').to_i,
          (node.attr('mail') || '').split(/\s+/),
          node.text,
        )
      }.sort { |a, b| a.vpos <=> b.vpos }
    end
  end
end

module Torigoya
  module CommentPlayer
    module Manager
      def self.init(comments)
        @stage = Spriteset_Stage.new(comments)
      end

      def self.stage
        @stage
      end
    end

    class Sprite_Comment < Sprite
      def initialize(sprite_set, viewport = nil)
        super(viewport)
        @sprite_set = sprite_set
        @test_bitmap = Bitmap.new(1, 1)
        @speed = 1
        @life = -1
        self.z = 255
      end

      def dispose
        @test_bitmap.dispose
        super
      end

      def set(comment)
        unset
        @comment = comment

        self.bitmap.dispose if self.bitmap
        apply_font(@test_bitmap)
        lines = @comment.text.strip.split(/\r?\n/)
        rect = @test_bitmap.text_size(lines.max_by(&:size))
        if self.ue? or self.shita?
          rect.width = Graphics.width unless rect.width < Graphics.width
          align = 1
        else
          align = 0
        end

        self.bitmap = Bitmap.new(rect.width, rect.height * lines.size)
        apply_font(self.bitmap)
        lines.each do |line|
          self.bitmap.draw_text(rect, line, align)
          rect.y += rect.height
        end

        @speed = 0
        case
        when self.ue?
          self.x = (Graphics.width - self.bitmap.width) / 2
          self.y = @sprite_set.find_ue_position(self)
        when self.shita?
          self.x = (Graphics.width - self.bitmap.width) / 2
          self.y = @sprite_set.find_shita_position(self) - self.bitmap.height
        else
          self.x = Graphics.width
          @speed = (Graphics.width + self.bitmap.width) / (4.0 * Graphics.frame_rate)
          self.y = rand(Graphics.height - self.bitmap.height)
        end
        @life = Graphics.frame_rate * 4
      end

      def apply_font(bitmap)
        bitmap.font.size =
          case
          when @comment.mail.include?('small')
            24
          when @comment.mail.include?('big')
            48
          else
            32
          end

        # TODO: 16進数指定もあるよ
        bitmap.font.color =
          case
          when @comment.mail.include?('red')
            Color.new(255, 0, 0)
          when @comment.mail.include?('pink')
            Color.new(255, 128, 128)
          when @comment.mail.include?('orange')
            Color.new(255, 192, 0)
          when @comment.mail.include?('yellow')
            Color.new(255, 255, 0)
          when @comment.mail.include?('green')
            Color.new(0, 255, 0)
          when @comment.mail.include?('cyan')
            Color.new(0, 255, 255)
          when @comment.mail.include?('blue')
            Color.new(0, 0, 255)
          when @comment.mail.include?('purple')
            Color.new(192, 0, 255)
          when @comment.mail.include?('black')
            Color.new(0, 0, 0)
          when @comment.mail.include?('white2'), @comment.mail.include?('niconicowhite')
            Color.new(204, 204, 153)
          when @comment.mail.include?('red2'), @comment.mail.include?('truered')
            Color.new(204, 0, 51)
          when @comment.mail.include?('pink2')
            Color.new(255, 51, 204)
          when @comment.mail.include?('orange2'), @comment.mail.include?('passionorange')
            Color.new(255, 102, 0)
          when @comment.mail.include?('yellow2'), @comment.mail.include?('madyellow')
            Color.new(153, 153, 0)
          when @comment.mail.include?('green2'), @comment.mail.include?('elementalgreen')
            Color.new(0, 204, 102)
          when @comment.mail.include?('cyan2')
            Color.new(0, 204, 204)
          when @comment.mail.include?('blue2'), @comment.mail.include?('marineblue')
            Color.new(51, 153, 255)
          when @comment.mail.include?('purple2'), @comment.mail.include?('nobleviolet')
            Color.new(102, 51, 204)
          when @comment.mail.include?('black2')
            Color.new(102, 102, 102)
          else
            Color.new(255, 255, 255)
          end
      end

      def unset
        @comment = nil
        @life = 0
        self.bitmap.dispose if self.bitmap
      end

      def update
        if @comment
          self.x -= @speed
          @life -= 1
          if @life <= 0
            self.unset
          end
        end
        super
      end

      def living?
        @life > 0
      end

      def ue?
        @comment && @comment.mail.include?('ue')
      end

      def shita?
        @comment && @comment.mail.include?('shita')
      end
    end

    class Spriteset_Stage
      SPRITE_SIZE = 50

      def initialize(comments)
        @comments = comments
        @comment_index = 0
        @timer = 0
        @prev_frame_count = Graphics.frame_count 

        @viewport = Viewport.new
        @viewport.z = 65535
        @sprites = Array.new(SPRITE_SIZE).map { Sprite_Comment.new(self, @viewport) }
        @sprite_index = 0
      end

      def dispose
        @sprites.each do |sprite|
          sprite.bitmap.dispose if sprite.bitmap
          sprite.dispose
        end
        @viewport.dispose
      end

      def update
        # 時間を進める
        prev_timer = @timer
        @timer += ((Graphics.frame_count - @prev_frame_count).to_f / Graphics.frame_rate)
        @prev_frame_count = Graphics.frame_count

        # 経過時間中に登場するコメントを流す
        while @comments[@comment_index] && @comments[@comment_index].vpos <= (@timer * 100)
          @sprites[@sprite_index].set(@comments[@comment_index])
          @sprite_index = (@sprite_index + 1) % SPRITE_SIZE
          @comment_index += 1
          unless @comment_index < @comments.size
            @comment_index = 0
            @timer = 0
          end
        end

        @viewport.update
        @sprites.each(&:update)
      end

      # ue表示の表示位置を探して返す
      def find_ue_position(target_sprite)
        y = 0
        @sprites.select(&:living?).select(&:ue?).sort { |a, b| a.y <=> b.y }.each do |sprite|
          return y if y + target_sprite.bitmap.height < sprite.y
          y = sprite.y + sprite.bitmap.height
        end
        y
      end

      # shita表示の表示位置を探して返す
      def find_shita_position(target_sprite)
        y = Graphics.height
        @sprites.select(&:living?).select(&:shita?).sort { |a, b| b.y <=> a.y }.each do |sprite|
          return y if y - target_sprite.bitmap.height > sprite.y + sprite.bitmap.height
          y = sprite.y
        end
        y
      end
    end
  end
end

class << DataManager
  alias gochiusa_init init
  def init
    gochiusa_init
    comments = Torigoya::RabbitComment.load
    Torigoya::CommentPlayer::Manager.init(comments)
  end
end

class Scene_Base
  alias gochiusa_update_basic update_basic
  def update_basic
    gochiusa_update_basic
    Torigoya::CommentPlayer::Manager.stage.update
  end
end

一見、めっちゃ長いんですけど、RGSSからnet/httpとrexmlが使えないのが悪いんです…!XMLパーサー書くとか昭和かよ。

注意

  • コメント表示の再現度が絶望的にダメです
    • ニコ動のコメントシステム複雑すぎてつらいので誰かまとめてほしい
  • コメントの取得数が足りません(直近1000件しか取ってきてない)
  • よくコメント表示レイヤーが消失します
    • シーンをまたがったSprite大量に置くのよくないと思う
  • 間違ってもマジメなゲームに使ってはダメです

ごちうさの話

当時、タイトルの「うさぎ」に釣られて1羽を視聴しました。
「なるほどな」という感じだったので本来ならそのまま視聴継続になるはずだったのですが、4月当時、僕は完全に炎上案件の真っ只中で、仕事と睡眠を繰り返す日々で気づいたら夏になっており、僕の中でごちうさは1羽が最終回になってしまいました。
あのとき、リアルタイムに視聴していたら僕もどうなっていたかわかりませんね。。。

しかし、幸か不幸かはわかりませんが、弟がごちうさ民になっており、現在では家に原作もBlue-rayもある状態のため、ごちうさ分に関しては特に問題がない状態になっています。なので原作は3巻まで読みました。あと一応、電子版も買ったです。

シャロちゃんとリゼ先輩の関係がとても良いですね。
あの2人の関係は見てるだけでこう…グッとね。はい。


明日はゆっくりしないさんです٩(๑❛ᴗ❛๑)۶

VXAce(RGSS3)でTweenアニメーションするスクリプトつくった


実は僕、Flashなどではお馴染みのTweenアニメーションって
全然つかったことなかったのです……
で、こないだの4月バカのときに初めて使ってみて、
「やべー便利すぎるー!ツクールでも使いてー!」という衝動に。

ちょっとググってみたら外人さんが作ったやつとかもあったんですが、
僕の求めてるものとはちょっと違ったので、自分でつくってみました。
動機的にちょっとArctic.jsっぽいです。

スクリプト

gistにおいてあります 【Tweenアニメーションさん for RGSS3

使い方

こんな感じ。
「60フレーム(1秒間)かけて、X座標64、Y座標128に移動する」アニメーションです。

# 【補足】
# object : 動かしたいもの(SpriteとかWindowとか)

# アニメーションの準備
animation = HZM::Tween::Animation.new(object, {
  # 必須設定(省略しても勝手に設定して動きますが……)
  :time => 60,                   # アニメーション時間(フレーム数。60フレーム=1秒)
  :transition => :ease_in_quad,  # 動作方法。詳細は後述。
  # ここから先は変更するものを設定
  :x =>  64,  # X座標を64へ
  :y => 128,  # Y座標を128へ
})

# アニメーションの開始
animation.play


「:transition」の部分は毎回のフレームに移動させる量を計算するメソッドを指定する部分です。
要するに計算式を設定する部分です。
この辺りのサイト様を参考にしていくつか用意してあります↓↓↓

  • :linear
  • :ease_in_quad
  • :ease_out_quad
  • :ease_in_out_quad
  • :ease_in_cubic
  • :ease_out_cubic
  • :ease_in_out_cubic
  • :ease_in_quart
  • :ease_out_quart
  • :ease_in_out_quart
  • :ease_in_quint
  • :ease_out_quint
  • :ease_in_out_quint
  • :ease_in_sine
  • :ease_out_sine
  • :ease_in_out_sine
  • :ease_in_expo
  • :ease_out_expo
  • :ease_in_out_expo
  • :ease_in_circ
  • :ease_out_circ
  • :ease_in_out_circ


上の以外を使いたい場合は、HZM_VXA::Tween::Transitionの中に
新しくメソッドを増やすことで追加できます。

また、例ではx座標とy座標を指定していますが、別にほかのも指定できます。
「○○.hoge = 数値」で設定できるものなら、「:hoge => 数値」でOK。
ただし、計算結果を強制的に整数にしてしまっているので、
小数の結果が欲しい場合はうまく動かないです。

ついでに別にSpriteとかWindowじゃなくても、何でも使えます。
Spriteに限定せず何にでも使えるやつが欲しかったので自分でつくったのです。
まぁ、通常用途ではあんまり意味ないかもしれませんが。。。僕には必要。

わかりにくいから実際に試したい

実用性ゼロのサンプル。
メニューを開くときにアニメーションします。

# メニュー画面を開いたときにうぃんうぃんするスクリプト
class Scene_Menu < Scene_MenuBase
  ANIMATION_TIME = 15
  #-----------------------------------------------------------------------------
  # ● 開始処理
  #-----------------------------------------------------------------------------
  alias hzm_vxa_tween_sample_start start
  def start
    hzm_vxa_tween_sample_start
    unless Window_MenuCommand.last_command_symbol
      # 各アニメーションを設定
      anim_command = HZM_VXA::Tween::Animation.new(@command_window, {
        :time => ANIMATION_TIME,
        :transition => :ease_out_quad,
        :x => @command_window.x,
        :y => @command_window.y,
        })
      anim_gold = HZM_VXA::Tween::Animation.new(@gold_window, {
        :time => ANIMATION_TIME,
        :transition => :ease_out_quad,
        :x => @gold_window.x,
        :y => @gold_window.y,
        })
      anim_status = HZM_VXA::Tween::Animation.new(@status_window, {
        :time => ANIMATION_TIME,
        :transition => :ease_out_quad,
        :x => @status_window.x,
        :y => @status_window.y,
        })
      # 初期値を指定
      @command_window.x = 0 - @command_window.width
      @command_window.y = 0 - @command_window.width
      @gold_window.x = 0 - @gold_window.width
      @gold_window.y = Graphics.height + @gold_window.width
      @status_window.x = Graphics.width
      # アニメーション開始
      anim_command.play
      anim_gold.play
      anim_status.play
    end
  end
end
class Window_MenuCommand < Window_Command
  #-----------------------------------------------------------------------------
  # ● コマンド選択位置の取得(クラスメソッド)(独自)
  #-----------------------------------------------------------------------------
  def self.last_command_symbol
    @@last_command_symbol
  end
end


うわー、うぜー(

RPGツクールVX Ace Liteの思うところと、イケナイこと!


お久しぶりですRuたんです!
制作の類が吐きそうになるくらい進んでないので、死にそうです(

最近あったことと言えば、RPGツクールVX Ace Liteが公開されましたね。
ちょっと僕もインストールしてみたので、気になったところを書いてみます。
まぁ、スクリプトいじれないので、僕がやることはほとんどないですがw

起動時ロゴの表示


Lite版で作られたゲームは起動時にエンターブレインでツクールなロゴが入ります。
これは実行ファイルが特別なものになってる……わけではなく、
スクリプトに以下の処理が追加されています。

def show_logo
  sprite = Sprite.new
  Graphics.fadeout(0)
  sprite.bitmap = Cache.system('Logo1')
  Graphics.fadein(30)
  Graphics.wait(30)
  Graphics.fadeout(30)
  sprite.bitmap = Cache.system('Logo2')
  Graphics.fadein(30)
  Graphics.wait(30)
  Graphics.fadeout(30)
end

show_logo unless $TEST

製品版持ってる人で起動ロゴ入れたい人は、
コレそのまま使えばいいんじゃないかな!

起動時にしか表示されない(F12リセット時は出ない)のに、
わざわざキャッシュにするのもどうかとは思いますけど……
わかりやすくするって意味でこうなってるのかな?

あと、Graphics/Systemに入ってるロゴ画像表示してるだけなので、
この画像を差し替えたらLite版でも……(駄目です

コモンイベントは使えないわけじゃない


イベントコマンドの「コモンイベント」は機能制限がかかっているので、
使うことはできないですが、データベースのコモンイベントは普通に使えます。
(※もちろん、最大数は変更できないので10個まで)


なので、上みたいにスイッチONをトリガーに実行するようにすれば、
コモンイベントを使ってあげることが可能です。
使うとイベントが発生するアイテムも問題なく作れますね!

ちなみにコレやるときはちゃんと最後にスイッチ1をOFFにしないとダメですよ?
スイッチ1がONのままだと永遠におっさんと話し続けることになるので……!

イベントコマンドの「コモンイベント」も使える!?

用意するものは簡単! VX Ace LiteとVX Ace(製品版)です!(


まず、製品版でコモンイベントの呼出のイベントを作ります。


それをコピーします。



VX Ace Liteのほうを開いてペーストします。


おー


わーお


同じやり方で、スクリプトのイベントもコピーすることができます。
つまり製品版を買えばやりたい放題ってことですね!!!!……あれ?

ちなみに貼り付けたイベントコマンドは編集はできないけど、
のコピー&ペーストは問題なくできたりします。
なので、製品版持ってる人にコモンイベントの呼出1〜10を作ってもらって、
そのマップデータを読み込めb……おっと誰か来たようだ。
(※普通に駄目です)

製品版のプロジェクトを読み込める

製品版のプロジェクトファイルのGame.rvproj2を
Lite版で作ったプロジェクトファイルのGame.rvproj2で上書きすると
一応、開くことが出来ます。

が、エディタの画面(マップ)がおかしくなるっぽい?ので注意。
というか、別に意味ないですし……

むしろLite版のプロジェクトを製品版で読み込める

「Liteで作成したプロジェクト(ゲームデータ)を
 そのまま製品版で読み込むことができません」という公式の記述通り、
そのまま読み込むことはできないですが、
上に書いた逆、つまり製品版のGame.rvproj2で上書きしてあげれば、
Liteのプロジェクトを製品版に引き継げます。

なので、「Lite版で作ってたけどもっといろいろやりたい」とか、
鳥小屋.txtっていうサイトで公開されてるスクリプト使いたい」って人は、
製品版買えばいいんじゃないかな!! スクリプトいいよ!!(

というかGame.rvproj2って、ただのテキストファイルなので、
メモ帳かなんかで開いて編集するだけで、Lite版と製品版切り替えられます。

製品版は

RPGVXAce 1.01

Lite版は

RPGVXAce-Lite 1.01

になってます。
バージョン情報書いてあるだけー。

(製品版持ってる人)インストールすると関連付けに困る

製品版インストール済みなのにVX Ace Liteをインストールすると
Game.rvproj2をダブルクリックしたときに、
Lite版が起動するようになります。仕方ないね。

直すのだいぶ面倒くさいんで、
よくわからない人はインストールしないほうがいいかなー。
実行ファイル名が同じせいか、
Windowsの関連付けのほうからだとうまく設定できない感じ?
僕は関連付けいじるソフト使いました。

結論

スクリプトいじれないから、Ruたんやることない(
そもそも製品版持ってるのでやる必要もないですが……

純粋に保存できるようになった体験版って感じですね。
ちょっとした作品だったら十分作れるんじゃないかな?と思います。

ニコニコ自作ゲームフェスに応募する方は頑張ってください!!
たぶん期限内に完成させるのが一番大変w

「新」音量変更スクリプトさん for RGSS3

2013/05/25 タイプ設定を変更すると強制終了する不具合修正しました。使っている方は最新版に差し替えお願いします。

前にも1回載せてますが,あれはVXAceの体験版が出て飛びついて書いた記事なので,
今回,スクリプトをいろいろ直したついでに解説記事をちゃんと書きます.

「音量変更スクリプトさん for RGSS3」について

タイトル画面,メニュー画面にゲーム中の音量を設定するメニューを追加するスクリプトです.
ここで設定した音量はゲーム中のすべてのサウンドに適用されます.

HZM_VXA ベーススクリプトさん」を併用することで,
調整した音量を保存して,次回起動時に反映させることができるようになります.
便利なのでできれば一緒にいれてあげてください.
※ベーススクリプトは,音量スクリプトより上に設置してください

2012/12/17追記
ベーススクリプト無くても反映できるようにしました.やったね!!

敵バトラーに影を表示 for RGSS3

メモ欄シリーズ,スクリプト名に「さん」つけ忘れてて泣きたい.
そんなわけで,ノミノネに使ったスクリプトをどんどん素材にします!

敵バトラーの下に影を自動的に表示するスクリプトさんです.
まぁRTPのモンスターはみんな画像自体に影が付いてたりするので,
このスクリプト意味ないんですけどね.他の素材使う時などに><

[hzm]敵バトラーに影を表示 for RGSS3
※使用するためには,このスクリプトより上に[hzm]メモ欄拡張共通部分 for RGSS3を設置する必要があります.

機能は必要最低限です.
動いたり,敵の形の影になったりはしません.全部同じ形です.
ただ,大きさだけは半自動調整です.
でも実際に使うと手動調整しないと使い物にならないという……(体験談)

使う時は影用の画像が必要です.
Graphics/Battlersの中に入れてください.
上のサンプル画像はノミノネに使ってたやつですが,勝手に使ってもOKですよん.

実際に設定した例です.
「[hzm]影」までは共通で,その後に手動設定する場合は数値を入れます.
上の図だと,横方向はそのまま,縦方向は上に16ドット,拡大率は1.8倍です.
あ,位置だけ手動設定したい場合は,拡大率は書かなくてもいけます.
「[hzm]影:0,-16」までにすればOK.