• RPG Maker VX地图随机气泡对话 Ver1.0


    最近刚看完了Ruby的《元编程》,收获颇丰。

    想趁着印象深刻的时候多写点东西练练手,于是想到了这么一个功能。

    在辉光翼战记中,玩家在迷宫中移动时,角色偶尔会在屏幕下方进行一些小对话。虽然对剧情本身没有什么影响,

    但是这个功能不但削弱了在迷宫中移动时的烦躁感,而且还可以加深对人物性格的刻画。

    RPG Maker VX的屏幕默认只有544 * 416大小,在冒险中专门取出一块地方来进行小对话会让游戏画面显得更加狭窄,由此选择了使用漂浮气泡对话。

    本脚本主要使用了RPG Maker VX的跟随和村人のつぶやき两个脚本,以这两个脚本为基础,设计了一个小型DSL,这两个脚本的内容附在文章最后。

    使用方法如下:

    story_event "First Event" do
    
      message [-1, 'First Message']
    
      message [0, 'Second Message']
    
      scope [1, 2, 3, 5]
    
    
    end

    每个小对话使用 story_event来定义,并使用一个字符串对其命名,便于区别其他的小对话

    message用来声明一条信息,其中第一个参数表示说话角色,第二个参数表示信息内容。

    scope用来声明作用范围,其内容为一个包含了剧情可发生地图的id数组,上例中该剧情可以在map_id为1、2、3、5的地图中发生。

    通过提前设置好对话和作用范围,就可以实现玩家在地图移动时的随机对话了。

    作为1.0版本,只实现了一个大致上的功能。还有一些设想中的功能没有制作,内容包括:

    1. 添加一个run_switch, 内容是一个开关id数组,当run_switch中的所有开关打开时该剧情才可能发生。

    2. 添加一个close_switch,其功能和run_switch相反,用来终止一个剧情的发生。

    3. 每条message除了可以声明角色和信息内容以外,还可以指定气泡滞留时间,使得句子长的信息滞留更久,句子短的信息消失更快。

    4. 可以通过给地图增加特殊声明来修改该地图中随机剧情的出现频度

    大概会在后续版本中添加上去。

    附录

    地图随机剧情脚本

      #==============================================================================
      #
      #   通过扩展Kernel的功能实现的小型DSL
      #   由于RMVX的问题,class_eval内的代码是被迫实现的
      #   原版代码如下
      #   Kernel.send :define_method, :story_event do |name, &block|
      #     now_event = event_list[name] = Move_Talking::Story_Event.new(name)
      #     block.call
      #   end
      #   在RMVX中,由于|name, &block|处会报语法错误,所以变成了现在的丑陋样子。
      #   如果有人知道在RMVX中怎么通过define_method定义方法并可以传递block请告诉我。
      #
    #=============================================================================
      lambda {
        
        event_list = {}   #用以储存所有小剧情事件
        now_event = nil   #用以构造小剧情事件
        
        
        #--------------------------------------------------------------------------
        # ● 定义story_event方法
        #--------------------------------------------------------------------------
        Kernel.class_eval('def story_event(name, &block)
          event_list = get_event_list
          event_list[name] = Move_Talking::Story_Event.new(name)
          now_event = event_list[name]
          set_now_event(now_event)
          block.call
        end')
        
        #--------------------------------------------------------------------------
        # ● 获取event_list
        #     story_event方法专用
        #     如果可以将story_event改回使用define_method实现,本方法可作废
        #--------------------------------------------------------------------------
        Kernel.send :define_method, :get_event_list do
          return event_list
        end
        
        #--------------------------------------------------------------------------
        # ● 设置now_event
        #     story_event方法专用
        #     如果可以将story_event改回使用define_method实现,本方法可作废
        #--------------------------------------------------------------------------
        Kernel.send :define_method, :set_now_event do |event|
          now_event = event
        end
        
        #--------------------------------------------------------------------------
        # ● 定义message方法
        #     接收一个数组作为message内容
        #--------------------------------------------------------------------------
        Kernel.send :define_method, :message do |param|
          now_event.msg_data << param
        end
        
        #--------------------------------------------------------------------------
        # ● 定义scope方法
        #     接收一个数组作为scope内容
        #--------------------------------------------------------------------------
        Kernel.send :define_method, :scope do |scope|
          now_event.scope = scope
        end
        
        #--------------------------------------------------------------------------
        # ● 定义run_switch方法
        #      接收一个数组作为run_switch内容
        #--------------------------------------------------------------------------
        Kernel.send :define_method, :run_switch do |run_switch|
          now_event.run_switch = run_switch
        end
        
        #--------------------------------------------------------------------------
        # ● 获取当前地图适用的小剧情
        #     此处只考虑map_id其他操作在scene_map中具体判别
        #--------------------------------------------------------------------------
        Kernel.send :define_method, :get_story_event do |map_id|
          result = []
          event_list.each do |key, val|
            if    val.scope.nil?            then result.push val 
            elsif val.scope.include? map_id then result.push val
            end
          end
          return result
        end
        
      }.call
    
      #==============================================================================
      # ■ Move_Talking
      #------------------------------------------------------------------------------
      #  移动中对话的主要模块
      #==============================================================================
      module Move_Talking
        TRIGGER_COOLDOWN = 1500
        AWP = 500
        
        #--------------------------------------------------------------------------
        # ●Story_Event
        #
        #   用来装载小对话的类
        #--------------------------------------------------------------------------
        class Story_Event
          
          attr_accessor :scope
          attr_accessor :run_switch
          attr_accessor :name
          attr_accessor :msg_data
          attr_accessor :cooldown
          
          #--------------------------------------------------------------------------
          # ● 初始化
          #--------------------------------------------------------------------------
          def initialize(name)
            @name = name
            @msg_data = []
            @scope = nil
            @run_switch = nil
            @index = 0
            @cooldown = 200
          end
    
          
          #--------------------------------------------------------------------------
          # ● 运行
          #    每次显示一条信息
          #--------------------------------------------------------------------------
          def run
            player = @msg_data[@index][0]
            message = @msg_data[@index][1].dup
            if player == -1
              $game_player.message = message
            else
              $game_party.characters[player].message = message
            end
            @index += 1
          end
          
          #--------------------------------------------------------------------------
          # ● 判断是否全部显示完毕
          #--------------------------------------------------------------------------
          def over?
            @index == @msg_data.size
          end
          
          #--------------------------------------------------------------------------
          # ● 重置
          #    将index复位,以便重复播放
          #--------------------------------------------------------------------------
          def reset
            @index = 0
          end
          
        end
      end
      #==============================================================================
      # ■ Scene_Map
      #------------------------------------------------------------------------------
      #  处理地图画面的类。
      #==============================================================================
      class Scene_Map < Scene_Base
        
        #--------------------------------------------------------------------------
        #    初始化
        #--------------------------------------------------------------------------
        alias story_event_initialize initialize
        def initialize
          story_event_initialize
          @story_count = 0
          @message_count = 0
          @story_event = nil
        end
        
        #--------------------------------------------------------------------------
        #    更新
        #--------------------------------------------------------------------------
        alias story_event_update update
        def update
          @story_count += 1
          if @story_count >= 1500 && @story_event.nil?
            events = get_story_event($game_map.map_id)
            @story_event = events[rand(events.size)]
            @story_event.reset
            @story_event.run
            @message_count = 0
          elsif @story_event != nil
            @message_count += 1
            if @message_count == 200
              @story_event.run
              @message_count = 0
            end
            if @story_event.over?
              @story_event = nil
              @story_count = 0
            end
          end
          story_event_update
        end
        
      end
    地图随机剧情脚本

     村人のつぶやき

    #==============================================================================
    # ★ RGSS2_村人のつぶやき
    # tomoaky (http://hikimoki.hp.infoseek.co.jp/)
    #==============================================================================
    
    #==============================================================================
    # ■ 設定項目
    #==============================================================================
    module TMRBT
      SW_MESSAGE_STOP = 2             # 機能無効化スイッチのID
      MESSAGE_DURATION = 180          # メッセージの表示時間(フレーム)
      MESSAGE = {}
      
      # おじさんタイプのメッセージリスト
      MESSAGE["Test"] = [
        'Test message'
      ]
    end
    
    #==============================================================================
    # ■ コマンド
    #==============================================================================
    module TMRBT
      module Commands
        module_function
        #--------------------------------------------------------------------------
        # ● 指定したIDのイベントにメッセージを強制
        #--------------------------------------------------------------------------
        def mes(id, text, count = 300)
          event = $game_map.interpreter.get_character(id)
          if event != nil
            event.message = text
            event.message_count = count
          end
        end
        #--------------------------------------------------------------------------
        # ● 指定したIDのイベントの村人タイプを変更
        #--------------------------------------------------------------------------
        def mes_type(id, type = nil)
          return if id < 0    # プレイヤーには無効
          event = $game_map.interpreter.get_character(id)
          if event != nil
            event.murabito = type
          end
        end
      end
    end
    
    class Game_Interpreter
      include TMRBT::Commands
    end
    
    #==============================================================================
    # ■ Game_Character
    #==============================================================================
    class Game_Character
      #--------------------------------------------------------------------------
      # ● 公開インスタンス変数
      #--------------------------------------------------------------------------
      attr_accessor :message                  # フキダシメッセージ
      attr_accessor :message_count            # メッセージ待機カウント
      #--------------------------------------------------------------------------
      # ● オブジェクト初期化
      #--------------------------------------------------------------------------
      alias tmrbt_game_character_initialize initialize
      def initialize
        tmrbt_game_character_initialize
        @message = ""
        @message_count = 60 + rand(240)
      end
      #--------------------------------------------------------------------------
      # ○ メッセージを表示できる状態かどうかを返す
      #--------------------------------------------------------------------------
      def message_can_speak?
        return false if @character_name == "" and @tile_id == 0
        return false if $game_switches[TMRBT::SW_MESSAGE_STOP]
        return true
      end
    end
    
    #==============================================================================
    # ■ Game_Event
    #==============================================================================
    class Game_Event < Game_Character
      #--------------------------------------------------------------------------
      # ● 公開インスタンス変数
      #--------------------------------------------------------------------------
      attr_accessor :murabito                 # 村人タイプ
      #--------------------------------------------------------------------------
      # ● オブジェクト初期化
      #     map_id : マップ ID
      #     event  : イベント (RPG::Event)
      #--------------------------------------------------------------------------
      alias tmrbt_game_event_initialize initialize
      def initialize(map_id, event)
        tmrbt_game_event_initialize(map_id, event)
        @murabito = event.name =~ /<村人:(S+?)>/i ? $1 : nil  # 村人判定
      end
      #--------------------------------------------------------------------------
      # ● フレーム更新
      #--------------------------------------------------------------------------
      alias tmrbt_game_event_update update
      def update
        tmrbt_game_event_update
        if @murabito != nil
          if @message_count > 0
            @message_count -= 1
            if @message_count == 0
              if message_can_speak?
                n = TMRBT::MESSAGE[@murabito].size
                @message = TMRBT::MESSAGE[@murabito][rand(n)].clone
              end
              @message_count = 270 + rand(300)
            end
          end
        end
      end
      #--------------------------------------------------------------------------
      # ○ 指定したIDのイベントにメッセージを強制(カスタム移動用)
      #--------------------------------------------------------------------------
      def mes(id, text, count = 300)
        TMRBT::Commands.mes(id == 0 ? @id : id, text, count)
      end
      #--------------------------------------------------------------------------
      # ○ 指定したIDのイベントの村人タイプを変更(カスタム移動用)
      #--------------------------------------------------------------------------
      def mes_type(id, type = nil)
        TMRBT::Commands.mes_type(id == 0 ? @id : id, type)
      end
    end
    
    #==============================================================================
    # ■ Sprite_Character
    #==============================================================================
    class Sprite_Character < Sprite_Base
      @@bitmap_murawin = Cache.system("murawin")
      @@windowskin = Cache.system("Window")
      #--------------------------------------------------------------------------
      # ● オブジェクト初期化
      #     viewport  : ビューポート
      #     character : キャラクター (Game_Character)
      #--------------------------------------------------------------------------
      alias tmrbt_sprite_character_initialize initialize
      def initialize(viewport, character = nil)
        @message_duration = 0
        tmrbt_sprite_character_initialize(viewport, character)
      end
      #--------------------------------------------------------------------------
      # ● 解放
      #--------------------------------------------------------------------------
      alias tmrbt_sprite_character_dispose dispose
      def dispose
        dispose_message
        tmrbt_sprite_character_dispose
      end
      #--------------------------------------------------------------------------
      # ● フレーム更新
      #--------------------------------------------------------------------------
      alias tmrbt_sprite_character_update update
      def update
        tmrbt_sprite_character_update
        update_message
        if @character.message != ""
          @message = @character.message
          start_message
          @character.message = ""
        end
      end
      #--------------------------------------------------------------------------
      # ○ 文字色取得
      #     n : 文字色番号 (0~31)
      #--------------------------------------------------------------------------
      def text_color(n)
        x = 64 + (n % 8) * 8
        y = 96 + (n / 8) * 8
        return @@windowskin.get_pixel(x, y)
      end
      #--------------------------------------------------------------------------
      # ○ 特殊文字の変換
      #--------------------------------------------------------------------------
      def convert_special_characters
        if @message == nil
          @message = ""
        end
        @message.gsub!(/\L/)              { "x00" }
        @message.gsub!(/\V[([0-9]+)]/i) { $game_variables[$1.to_i] }
        @message.gsub!(/\N[([0-9]+)]/i) { $game_actors[$1.to_i].name }
        @message.gsub!(/\C[([0-9]+)]/i) { "x01[#{$1}]" }
        @message.gsub!(/\\/)             { "\" }
      end
      #--------------------------------------------------------------------------
      # ○ フキダシメッセージ表示の開始
      #--------------------------------------------------------------------------
      def start_message
        dispose_message
        convert_special_characters
        @message_duration = TMRBT::MESSAGE_DURATION
        bitmap = Bitmap.new(160, 160)
        bitmap.font.name = "Verdana"
        bitmap.font.size = 14
        w = 0
        contents_x = 4
        contents_y = 0
        loop do
          c = @message.slice!(/./m)         # 次の文字を取得
          case c
          when nil
            break                           # 描画すべき文字がなければ終了
          when "x00"                       # 改行
            w = contents_x if w < contents_x
            contents_x = 4
            contents_y += 16
            next
          when "x01"                       # C[n]  (文字色変更)
            @message.sub!(/[([0-9]+)]/, "")
            bitmap.font.color = text_color($1.to_i)
            next
          else
            bitmap.font.color = Color.new(255, 255, 255)
            bitmap.draw_edge_text(contents_x, contents_y, 40, 16, c, 0, Color.new(94, 24, 24))
            c_width = bitmap.text_size(c).width
            contents_x += c_width
            if contents_x >= 140            # 右端にきていれば改行
              w = contents_x if w < contents_x
              contents_x = 4
              contents_y += 16
            end
          end
        end
        h = contents_y + (contents_x == 4 ? 0 : 16)
        w = (w < contents_x ? contents_x + 4 : w + 4)
        @message_sprite = ::Sprite.new(viewport)
        @message_sprite.ox = w / 2
        @message_sprite.oy = h + 4
        @message_sprite.bitmap = Bitmap.new(w, h + 8)
        rect = Rect.new(0, 0, 8, 8)
        @message_sprite.bitmap.blt(0, 0, @@bitmap_murawin, rect, 192)
        rect.x += 8
        @message_sprite.bitmap.blt(w - 8, 0, @@bitmap_murawin, rect, 192)
        rect.y += 8
        @message_sprite.bitmap.blt(w - 8, h - 8, @@bitmap_murawin, rect, 192)
        rect.x -= 8
        @message_sprite.bitmap.blt(0, h - 8, @@bitmap_murawin, rect, 192)
        rect.set(16, 0, 8, 8)
        @message_sprite.bitmap.blt(@message_sprite.ox - 4, h,
          @@bitmap_murawin, rect, 192)
        color = @@bitmap_murawin.get_pixel(8, 8)
        color.alpha = 192
        @message_sprite.bitmap.fill_rect(8, 0, w - 16, h, color)
        @message_sprite.bitmap.fill_rect(0, 8, 8, h - 16, color)
        @message_sprite.bitmap.fill_rect(w - 8, 8, 8, h - 16, color)
        @message_sprite.bitmap.blt(0, 0, bitmap, bitmap.rect)
        bitmap.dispose
        update_message
      end
      #--------------------------------------------------------------------------
      # ○ フキダシメッセージの更新
      #--------------------------------------------------------------------------
      def update_message
        if @message_duration > 0
          @message_duration -= 1
          if @message_duration == 0 or not @character.message_can_speak?
            @message_duration = 0
            dispose_message
          else
            @message_sprite.x = x
            @message_sprite.y = y - height
            @message_sprite.z = z + 200
            @message_sprite.opacity = @message_duration * 24
          end
        end
      end
      #--------------------------------------------------------------------------
      # ○ フキダシメッセージの解放
      #--------------------------------------------------------------------------
      def dispose_message
        if @message_sprite != nil
          @message_sprite.dispose
          @message_sprite = nil
        end
      end
    end
    村人のつぶやき

    跟随脚本

    http://bbs.66rpg.com/thread-111958-1-1.html

  • 相关阅读:
    软件杯学习:python爬虫爬取新浪新闻并保存为csv格式
    操作系统实验1:时间片轮转和存储管理动态分区分配及回收
    软件测试
    实验6-使用TensorFlow完成线性回归
    实验一
    pycharm中设置anaconda环境
    架构之美读书笔记一
    2.1学习总结:决策树分类器
    python自学日记一
    爱甩卖网站正式上线啦
  • 原文地址:https://www.cnblogs.com/deadblue/p/3203423.html
Copyright © 2020-2023  润新知