じ☆ve冰风 发表于 2024-4-20 01:53:53

【教程】如何制作储物箱

相关问题:
https://rpg.blue/thread-486363-1-1.html

示意图:


解决这个问题,通常的做法是创建一个Scene类,然后用事件call这个类,就跟使用商店一样。上面的示意图也就是我们所想要的效果:整个场景分为4部分,当左下或者右下窗口激活时,按下空格来交换队伍里的物品和储物箱里的物品。
接下来我会仔细的讲解该如何写这个脚本。对于RGSS的初学者来说,可以当作写Scene的教程。这个教程会逐步完善场景类所需的各种内容,并且每完成一小部分就会展示一下效果。

【第一节:场景类的基本结构】
首先创建一个模块,以避免跟其他的脚本同名,后续的所有类的定义都是在这个模块里进行。这里使用提问者的名字Taeckle作为模块名。
接下来创建场景的类Scene_Storage。这个场景的类需要包括:

1. main方法,在范例里,main方法被拆分成了3部分:create_windows,scene_loop和dispose_windows。
create_windows 用来创建4个窗口,使用几个比较基础的窗口类作为place holder,这里只是用来检查窗口位置是否正确,此方法会在后面的步骤里被新的定义覆盖:RUBY 代码
# ----------------------------------------- #
# Help Window (640 x 64)                  #
# ----------------------------------------- #
# Menu Window (640 x 64)                  #
# --------------------+-------------------- #
# Item Window Party   | Item Window Storage #
#    (320 x 352)      |    (320 x 352)      #
# --------------------+-------------------- #
def create_windows
# Help Window
@help_window = Window_Help.new
# Horizontal Command Window
@menu_window = Window_Selectable.new(0, 64, 640, 64)
# Item Window left: Party items
@item_window_party = Window_Selectable.new(0, 128, 320, 480 - 64 * 2)
# Item Window right: Storage items
@item_window_storage = Window_Selectable.new(320, 128, 320, 480 - 64 * 2)
end


scene_loop 直接照抄RGSS的内容。dispose_windows使用RGSS3的风格,将属于Scene的所有Window类的实例对象关闭。

由于scene_loop里呼叫了update方法,所以还需要创建一个空的update方法。

2. 数据的处理,这个场景涉及到RGSS以外的数据。所以需要定义数据的类型Data,以及场景所需的实例变量@data。
考虑到具体要解决的问题是储物箱,所以该场景要占用一个全局变量来存储数据,把这个变量的ID传给场景的initialize方法。顺便传递了储存箱最大能装的数量作为第2个参数。
RUBY 代码
def initialize(data_vid, item_max_size = 99)
@scene_class = $scene.class
@item_max_size = .min
if !$game_variables.is_a?(Data)
    $game_variables = Data.new
end
@data = $game_variables
end

在initialize方法里,会检测传入的全局变量是否为Data类型,如果不是就初始化一下,然后绑定此全局变量。

储物箱应该要提供存入和取出两个方法。定义store和fetch两个空方法,其参数是要存储的东西和数量(默认为1)

最终脚本如下:RUBY 代码下载

# encoding: utf-8
module Taeckle
@version = "0.1"

classData
end

class Scene_Storage
    attr_reader :data

    def initialize(data_vid, item_max_size = 99)
      @scene_class = $scene.class
      @item_max_size = .min
      if !$game_variables.is_a?(Data)
      $game_variables = Data.new
      end
      @data = $game_variables
    end

    def main
      create_windows
      scene_loop
      dispose_windows
    end

    # layout
    # ----------------------------------------- #
    # Help Window (640 x 64)                  #
    # ----------------------------------------- #
    # Menu Window (640 x 64)                  #
    # --------------------+-------------------- #
    # Item Window Party   | Item Window Storage #
    #    (320 x 352)      |    (320 x 352)      #
    # --------------------+-------------------- #
    def create_windows
      # Help Window
      @help_window = Window_Help.new
      # Horizontal Command Window
      @menu_window = Window_Selectable.new(0, 64, 640, 64)
      # Item Window left: Party items
      @item_window_party = Window_Selectable.new(0, 128, 320, 480 - 64 * 2)
      # Item Window right: Storage items
      @item_window_storage = Window_Selectable.new(320, 128, 320, 480 - 64 * 2)
    end

    def update
    end

    def scene_loop
      Graphics.transition
      # 主循环
      loopdo
      # 刷新游戏画面
      Graphics.update
      # 刷新输入信息
      Input.update
      # 刷新画面
      update
      # 如果画面切换就中断循环
      if$scene != self
          break
      end
      end
      # 准备过渡
      Graphics.freeze
    end

    def dispose_windows
      self.instance_variables.eachdo |name|
      obj = self.instance_variable_get(name)
      obj.disposeif obj.is_a?(Window)
      end
    end

    def store(item, number = 1)
    end

    def fetch(item, number = 1)
    end
end
end


【第二节:测试工程】
创建一个空白的工程,并且将写好的scene_storage_base.rb插入到脚本编辑器的main前。根据具体的需求,创建如下自动事件:

按F12执行,进入新游戏后,自动执行本事件,就会进入到Scene_Storage里了。这个时候场景里还是一片空白:


【第三节:数据结构】
接下来我们需要完善Data类,以及store和fetch两个方法。ruby允许随时打开一个类添加新的内容,也允许覆盖原来的同名方法。

1. Data类是存储在箱子里的物品、武器等。所以Data类可以直接套用Game_Party的部分内容:
RUBY 代码
classData
attr_reader :item
attr_reader :weapons
attr_reader :armors

def initialize
    @items = {}
    @weapons = {}
    @armors = {}
end

# ...
end
复制粘贴Game_Party的item_number, weapon_number, armor_number, gain_item, gain_weapon, gain_armor这6个方法即可。
由于拥有完全相同的6个方法和对应的数据结构,Game_Party和Data的实例在某些地方可以互相替换。详见后面的@bind_data部分。

2. store方法,需要根据传入的变量的不同类型(item/weapon/armor),来选择调用指定的方法。此外,当箱子或者队伍的背包已经满了的时候,就不能继续放入物品,定义一个函数来判断这种情况:
RUBY 代码
def can_store?(num_party, num_storage, number)
num_party - number >= 0 && num_party - number = 0 && num_storage + number0
      @items = [.max, 99].min
      end
    end

    #--------------------------------------------------------------------------
    # ● 增加武器 (减少)
    #   weapon_id : 武器 ID
    #   n         : 个数
    #--------------------------------------------------------------------------
    def gain_weapon(weapon_id, n)
      # 更新 hash 的个数数据
      if weapon_id > 0
      @weapons = [.max, 99].min
      end
    end

    #--------------------------------------------------------------------------
    # ● 增加防具 (减少)
    #   armor_id : 防具 ID
    #   n      : 个数
    #--------------------------------------------------------------------------
    def gain_armor(armor_id, n)
      # 更新 hash 的个数数据
      if armor_id > 0
      @armors = [.max, 99].min
      end
    end
end

class Scene_Storage
    def store(item, number = 1)
      # RPG::Item
      if item.is_a?(RPG::Item)
      num_party = $game_party.item_number(item.id)
      num_storage = @data.item_number(item.id)
      if can_store?(num_party, num_storage, number)
          $game_party.lose_item(item.id, number)
          @data.gain_item(item.id, number)
          returntrue
      end
      end
      # RPG::Weapon
      if item.is_a?(RPG::Weapon)
      num_party = $game_party.weapon_number(item.id)
      num_storage = @data.weapon_number(item.id)
      if can_store?(num_party, num_storage, number)
          $game_party.lose_weapon(item.id, number)
          @data.gain_weapon(item.id, number)
          returntrue
      end
      end
      # RPG::Armor
      if item.is_a?(RPG::Armor)
      num_party = $game_party.armor_number(item.id)
      num_storage = @data.armor_number(item.id)
      if can_store?(num_party, num_storage, number)
          $game_party.lose_armor(item.id, number)
          @data.gain_armor(item.id, number)
          returntrue
      end
      end
      returnfalse
    end

    def fetch(item, number = 1)
      store(item, -number)
    end

    def can_store?(num_party, num_storage, number)
      num_party - number >= 0 && num_party - number = 0 && num_storage + number0
          @data.push($data_items)
      end
      end
      # 在战斗中以外添加武器、防具
      unless$game_temp.in_battle
      for i in1...$data_weapons.size
          if@bind_data.weapon_number(i) > 0
            @data.push($data_weapons)
          end
      end
      for i in1...$data_armors.size
          if@bind_data.armor_number(i) > 0
            @data.push($data_armors)
          end
      end
      end
      # Sort by description
      if@data.first.respond_to?(:desc)
      @data.sort! { |a, b| a.descb.desc}
      end
      # 如果项目数不是 0 就生成位图、重新描绘全部项目
      @item_max = @data.size
      if@item_max > 0
      self.contents = Bitmap.new(width - 32, row_max * 32)
      for i in0...@item_max
          draw_item(i)
      end
      end
    end

    #--------------------------------------------------------------------------
    # ● 描绘项目
    #   index : 项目编号
    #--------------------------------------------------------------------------
    def draw_item(index)
      item = @data
      case item
      whenRPG::Item
      number = @bind_data.item_number(item.id)
      whenRPG::Weapon
      number = @bind_data.weapon_number(item.id)
      whenRPG::Armor
      number = @bind_data.armor_number(item.id)
      end
      ifself.active
      self.contents.font.color = normal_color
      else
      self.contents.font.color = disabled_color
      end
      x = 4
      y = index * 32
      rect = Rect.new(x, y, self.width / @column_max - 32, 32)
      self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
      bitmap = RPG::Cache.icon(item.icon_name)
      opacity = self.contents.font.color == normal_color ? 255 : 128
      self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24), opacity)
      self.contents.draw_text(x + 28, y, 212, 32, item.name, 0)
      self.contents.draw_text(x + 240, y, 16, 32, ":", 1)
      self.contents.draw_text(x + 256, y, 24, 32, number.to_s, 2)
    end

    #--------------------------------------------------------------------------
    # ● 刷新帮助文本
    #--------------------------------------------------------------------------
    def update_help
      @help_window.set_text(self.item == nil ? "" : self.item.description)
    end
end
end

将这段脚本添加到F11脚本编辑器里,效果如图:

范例如下:


使用的小技巧:
1. 由于数据存储在全局变量里,会跟随存档一起存下来。
2. 场景绑定不同的变量就会对应不同的储物箱。
3. 如果预先设置好变量的内容,就可以做到储物箱在第一次打开时就有东西了。
$game_variables = Taeckle::Data.new$game_variables.gain_item(1, 1)复制代码
4. 设置可以存储的物品上限为0,并且在箱子里的每件物品数量恰好为1,此时箱子里的物品只能取出不能放回。
5. 可以在条件分歧里使用脚本里检查箱子里的内容:$game_variables.item_number(1) > 0复制代码

             本帖来自P1论坛作者xp兔子徒弟,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg.blue/forum.php?mod=viewthread&tid=486514若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
页: [1]
查看完整版本: 【教程】如何制作储物箱