Project1

标题: 脚本基础教学一、自定义动态光标的实现方法 [打印本页]

作者: 洛克人SZ    时间: 2007-12-31 20:54
标题: 脚本基础教学一、自定义动态光标的实现方法
写在前面的话

    这个教学的主要是面向对脚本多少了解一些的玩家,目的是使玩家了解自定义动态光标的实现原理及方法,所以对一些基础的东西不会做太多讲解。还有,我自己也是新人,且这是第一次写教程,所以错漏之处在所难免,还望前辈们予以指正。最后,这篇教学是为了配合我所发布的动态光标脚本类型 A http://rpg.blue/viewthread.php?tid=69995而写。
    好了,废话到此结束,我们进入正题。
—————————————我是分割线—————————————

    首先,我们将光标 A 定义为 Sprite 类的子类,然后来看看光标 A 的 initialize 过程:
        def initialize
          super                                               # 调用父类的同名过程
          self.visible = false                                # 让自己不可见
          self.bitmap = RPG::Cache.windowskin("cursor.png")   # 为自己生成内容
          self.z = 9999                                       # 设置自己的 z 值
          @count = 0                                          # 初始化计数用变量 a
          @wait_count = 0                                     # 初始化计数用变量 b
          update                                              # 调用 update 过程
        end
在 initialize 过程中只有让自己不可见和将自己的 z 值设定的很高比较重要,让自己不可见的目的是在自己最初建立的时候避免显示在错误的地方,将 z 值设的很高是为了使自己显示在目标窗口的上层。

    接下来我们来看看 update 过程:(使用的是左右横移的动态效果)
    def update
      super                                               # 调用父类的同名过程
      if @wait_count == 0                                 # 根据计数用变量 b 判断
        @count = 0                                        # 设置计数用变量 a
      elsif @wait_count == 8
        @count = 1                                        # 设置计数用变量 a
      end
      if @count == 0                                      # 根据计数用变量 a 判断
        self.ox += 1                                      # 自己的 ox 值加 1
        @wait_count += 1                                  # 计数用变量 b 的值加 1
      elsif @count == 1
        self.ox -= 1                                      # 自己的 ox 值减 1
        @wait_count -= 1                                  # 计数用变量 b 的值减 1
      end
    end
在脚本的执行过程中,理论上各种对象的 update 过程是每帧都会执行的,所以用了两个计数用变量来限定左右横移时的距离和方向,最重要的是改变的坐标是自己的 ox (精灵传送元原点的 X 座标)值而不是直接改变自己的 x 值,原因很简单,用通俗一些的说法就是对象的 x 与 y 值是确定自己位置的绝对坐标,而 ox 与 oy 则是调整自己显示位置的相对坐标(用来设置自己 x 与 y 值的基准点),在调整自己的绝对坐标时需要重设 x 与 y 值,所以在这里必须调整自己的 ox 与 oy 值。

    继续,我们来看看最后一个过程,也是惟一一个没有调用父类的全新过程 opacitysz :(用来调整自己的透明度)
        def opacitysz(type)                                   # type 为传递给该过程的参数
          if type == 0                                        # 根据 type 判断
            self.opacity -= 8 if self.opacity > 180           # 在自己的透明度大于 180 的情况下减小透明度
          else
            self.opacity += 8 if self.opacity < 255           # 在自己的透明度小于 255 的情况下增大透明度
          end
        end
这个很简单的过程的用途在后面会说到。

    可以看到,光标 A 本身的内容很简单,下面就来讲解使用光标 A 的方法:

    首先,我们来到 Window_Selectable 这个类,在 initialize 过程的最后添加下面两行代码:
        self.cursor_rect.empty                                # 将原有的光标矩形清空,与 self.cursor_rect.set(0, 0, 0, 0) 的效果相同
        @cr = Sprite_Cursor.new                               # 用 @cr 变量建立光标 Sprite
基础,没什么可解释的。

    终于到最重要的地方了,调整 update_cursor_rect 过程的内容:
  def update_cursor_rect
    if @index < 0 or self.active == false                # 光标位置不满 0 或自己未激活的情况下
      #@cr.opacitysz(0)                                  # 调用 opacitysz 过程使光标半透明化(这里没有使用)
      @cr.visible = false                                # 使光标不可见(默认设置)
      return
    end
    @cr.visible = true if self.visible && self.active    # 在窗口可见或被激活的情况下使光标可见
    @cr.opacitysz(1) if @cr.opacity < 255                # 在光标的透明度小于 255 的情况下调用 opacitysz 过程使光标不透明化
    # 获取当前的行
    row = @index / @column_max
    # 当前行被显示开头行前面的情况下
    if row < self.top_row
      # 从当前行向开头行滚动
      self.top_row = row
    end
    # 当前行被显示末尾行之后的情况下
    if row > self.top_row + (self.page_row_max - 1)
      # 从当前行向末尾滚动
      self.top_row = row - (self.page_row_max - 1)
    end
    # 计算光标的宽度
    cursor_width = self.width / @column_max - 32
    # 计算光标坐标
    x = (self.x + 8) + @index % @column_max * (cursor_width + 32)  # 计算光标的 x 坐标
    y = (self.y + 25) + @index / @column_max * 32 - self.oy        # 计算光标的 y 坐标
    # 更新光标位置
    @cr.x = x                                                      # 重设光标的 x 坐标
    @cr.y = y                                                      # 重设光标的 y 坐标
  end
对比原始的 update_cursor_rect 过程可以看到修改的地方并不多,最重要的就是计算坐标的变化,在原有的基础上增加了 self.x + 偏移 这段,作用就是在计算坐标时加上窗口自身的坐标和偏移确保光标显示在需要的地方,偏移的数值可以根据玩家自己的喜好来调整。

    向下看,来到 update 过程,在 super 之后插入下面这行:
        @cr.update if @cr.visible and @cr.opacity == 255 and self.active and @item_max >= 1
使光标具有动态效果最关键的一行代码,内容是在光标可见且完全不透明、窗口被激活且项目数大于等于 1 的情况下调用光标的 update 过程。

    最后,在 Window_Selectable 类的最后追加定义一个过程:
        def dispose                # 名称必须是 dispose
          @cr.dispose              # 释放光标
          super                    # 调用父类的同名过程
        end
这个过程的作用是在窗口释放时将光标一同释放掉。

    好了,在游戏中插入光标 A 脚本并且完成修改后就可以运行游戏来看看效果了。(别忘了保存)
             效果 1:                      效果 2:
   
    可以看到,效果 1 中光标遮挡到了字符,相当难看,为了实现效果 2 ,我们回到脚本中。
    来到 Window_Command 这个类,向下找到 draw_item 过程:
  def draw_item(index, color)
    self.contents.font.color = color
    rect = Rect.new(4, 32 * index, self.contents.width - 8, 32)  # 计算用来描绘文字的矩形
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    self.contents.draw_text(rect, @commands[index])
  end
我们只要将标有注释的那行改成这样 rect = Rect.new(18, 32 * index, self.contents.width - 8, 32) 即可,只是在原有的 x 坐标上加上了偏移量,这个偏移可以由玩家自行决定,默认是加上 14 (4+14=18)。

    保存,运行游戏,效果 2 实现。

    至此,自定义动态光标的教学结束,终于可以与那个难看的光斑说再见了。其他窗口的修改原理与上面相同,具体修改方法可以看看范例。

    嗯……不知道大家看明白了没有?还有,在输入姓名的窗口中用光标 A 效果不太好,因为不断左右横移导致定位不太准确,下一次(如果有的话……)我会介绍使用光标 B 的方法来解决这个问题。

              [本贴由 叶舞枫 于 2008-1-24 21:57:36 进行了编辑]
作者: 洛克人SZ    时间: 2007-12-31 20:54
标题: 脚本基础教学一、自定义动态光标的实现方法
写在前面的话

    这个教学的主要是面向对脚本多少了解一些的玩家,目的是使玩家了解自定义动态光标的实现原理及方法,所以对一些基础的东西不会做太多讲解。还有,我自己也是新人,且这是第一次写教程,所以错漏之处在所难免,还望前辈们予以指正。最后,这篇教学是为了配合我所发布的动态光标脚本类型 A http://rpg.blue/viewthread.php?tid=69995而写。
    好了,废话到此结束,我们进入正题。
—————————————我是分割线—————————————

    首先,我们将光标 A 定义为 Sprite 类的子类,然后来看看光标 A 的 initialize 过程:
        def initialize
          super                                               # 调用父类的同名过程
          self.visible = false                                # 让自己不可见
          self.bitmap = RPG::Cache.windowskin("cursor.png")   # 为自己生成内容
          self.z = 9999                                       # 设置自己的 z 值
          @count = 0                                          # 初始化计数用变量 a
          @wait_count = 0                                     # 初始化计数用变量 b
          update                                              # 调用 update 过程
        end
在 initialize 过程中只有让自己不可见和将自己的 z 值设定的很高比较重要,让自己不可见的目的是在自己最初建立的时候避免显示在错误的地方,将 z 值设的很高是为了使自己显示在目标窗口的上层。

    接下来我们来看看 update 过程:(使用的是左右横移的动态效果)
    def update
      super                                               # 调用父类的同名过程
      if @wait_count == 0                                 # 根据计数用变量 b 判断
        @count = 0                                        # 设置计数用变量 a
      elsif @wait_count == 8
        @count = 1                                        # 设置计数用变量 a
      end
      if @count == 0                                      # 根据计数用变量 a 判断
        self.ox += 1                                      # 自己的 ox 值加 1
        @wait_count += 1                                  # 计数用变量 b 的值加 1
      elsif @count == 1
        self.ox -= 1                                      # 自己的 ox 值减 1
        @wait_count -= 1                                  # 计数用变量 b 的值减 1
      end
    end
在脚本的执行过程中,理论上各种对象的 update 过程是每帧都会执行的,所以用了两个计数用变量来限定左右横移时的距离和方向,最重要的是改变的坐标是自己的 ox (精灵传送元原点的 X 座标)值而不是直接改变自己的 x 值,原因很简单,用通俗一些的说法就是对象的 x 与 y 值是确定自己位置的绝对坐标,而 ox 与 oy 则是调整自己显示位置的相对坐标(用来设置自己 x 与 y 值的基准点),在调整自己的绝对坐标时需要重设 x 与 y 值,所以在这里必须调整自己的 ox 与 oy 值。

    继续,我们来看看最后一个过程,也是惟一一个没有调用父类的全新过程 opacitysz :(用来调整自己的透明度)
        def opacitysz(type)                                   # type 为传递给该过程的参数
          if type == 0                                        # 根据 type 判断
            self.opacity -= 8 if self.opacity > 180           # 在自己的透明度大于 180 的情况下减小透明度
          else
            self.opacity += 8 if self.opacity < 255           # 在自己的透明度小于 255 的情况下增大透明度
          end
        end
这个很简单的过程的用途在后面会说到。

    可以看到,光标 A 本身的内容很简单,下面就来讲解使用光标 A 的方法:

    首先,我们来到 Window_Selectable 这个类,在 initialize 过程的最后添加下面两行代码:
        self.cursor_rect.empty                                # 将原有的光标矩形清空,与 self.cursor_rect.set(0, 0, 0, 0) 的效果相同
        @cr = Sprite_Cursor.new                               # 用 @cr 变量建立光标 Sprite
基础,没什么可解释的。

    终于到最重要的地方了,调整 update_cursor_rect 过程的内容:
  def update_cursor_rect
    if @index < 0 or self.active == false                # 光标位置不满 0 或自己未激活的情况下
      #@cr.opacitysz(0)                                  # 调用 opacitysz 过程使光标半透明化(这里没有使用)
      @cr.visible = false                                # 使光标不可见(默认设置)
      return
    end
    @cr.visible = true if self.visible && self.active    # 在窗口可见或被激活的情况下使光标可见
    @cr.opacitysz(1) if @cr.opacity < 255                # 在光标的透明度小于 255 的情况下调用 opacitysz 过程使光标不透明化
    # 获取当前的行
    row = @index / @column_max
    # 当前行被显示开头行前面的情况下
    if row < self.top_row
      # 从当前行向开头行滚动
      self.top_row = row
    end
    # 当前行被显示末尾行之后的情况下
    if row > self.top_row + (self.page_row_max - 1)
      # 从当前行向末尾滚动
      self.top_row = row - (self.page_row_max - 1)
    end
    # 计算光标的宽度
    cursor_width = self.width / @column_max - 32
    # 计算光标坐标
    x = (self.x + 8) + @index % @column_max * (cursor_width + 32)  # 计算光标的 x 坐标
    y = (self.y + 25) + @index / @column_max * 32 - self.oy        # 计算光标的 y 坐标
    # 更新光标位置
    @cr.x = x                                                      # 重设光标的 x 坐标
    @cr.y = y                                                      # 重设光标的 y 坐标
  end
对比原始的 update_cursor_rect 过程可以看到修改的地方并不多,最重要的就是计算坐标的变化,在原有的基础上增加了 self.x + 偏移 这段,作用就是在计算坐标时加上窗口自身的坐标和偏移确保光标显示在需要的地方,偏移的数值可以根据玩家自己的喜好来调整。

    向下看,来到 update 过程,在 super 之后插入下面这行:
        @cr.update if @cr.visible and @cr.opacity == 255 and self.active and @item_max >= 1
使光标具有动态效果最关键的一行代码,内容是在光标可见且完全不透明、窗口被激活且项目数大于等于 1 的情况下调用光标的 update 过程。

    最后,在 Window_Selectable 类的最后追加定义一个过程:
        def dispose                # 名称必须是 dispose
          @cr.dispose              # 释放光标
          super                    # 调用父类的同名过程
        end
这个过程的作用是在窗口释放时将光标一同释放掉。

    好了,在游戏中插入光标 A 脚本并且完成修改后就可以运行游戏来看看效果了。(别忘了保存)
             效果 1:                      效果 2:
   
    可以看到,效果 1 中光标遮挡到了字符,相当难看,为了实现效果 2 ,我们回到脚本中。
    来到 Window_Command 这个类,向下找到 draw_item 过程:
  def draw_item(index, color)
    self.contents.font.color = color
    rect = Rect.new(4, 32 * index, self.contents.width - 8, 32)  # 计算用来描绘文字的矩形
    self.contents.fill_rect(rect, Color.new(0, 0, 0, 0))
    self.contents.draw_text(rect, @commands[index])
  end
我们只要将标有注释的那行改成这样 rect = Rect.new(18, 32 * index, self.contents.width - 8, 32) 即可,只是在原有的 x 坐标上加上了偏移量,这个偏移可以由玩家自行决定,默认是加上 14 (4+14=18)。

    保存,运行游戏,效果 2 实现。

    至此,自定义动态光标的教学结束,终于可以与那个难看的光斑说再见了。其他窗口的修改原理与上面相同,具体修改方法可以看看范例。

    嗯……不知道大家看明白了没有?还有,在输入姓名的窗口中用光标 A 效果不太好,因为不断左右横移导致定位不太准确,下一次(如果有的话……)我会介绍使用光标 B 的方法来解决这个问题。

              [本贴由 叶舞枫 于 2008-1-24 21:57:36 进行了编辑]
作者: 洛克人SZ    时间: 2008-1-2 21:48
嗯…………没什么人看………………
自己稍微顶一下………………
作者: 阁中人    时间: 2008-1-3 05:54
接近期末了,没什么时间在坛子里逛……

楼主这系列的动态光标我很感兴趣啊!

不过呢,我目前还没有要用到这个……

个人还是偏向事件解决这种选择框的……

显示一个透明PNG然后用事件来控制其移动不是更简单易懂么?
作者: 洛克人SZ    时间: 2008-1-3 19:20
    用事件做光标的确是简单,不过却无法实现动态效果(就算可以实现执行效率也很低),还有就是在 RMXP 中事件其实只不过是定义好的脚本过程,用事件做光标首先要在事件脚本中处理一次,然后才会实际执行(多了一个过程),效率与直接用脚本相比要低很多。所以,游戏中需要的各种效果我会尽可能的用脚本来实现。
作者: 叶舞枫    时间: 2008-1-25 06:01
发布完毕{/wx}
http://rpg.blue/web/htm/news904.htm
VIP 2




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1