搜索附件  
同能RPG制作大师 附件中心 同能RM技术讨论区 RPG Maker VA 讨论区 【RGD/三版通用】SINVideo for RGD(视频功能增强·拓展改造): 6620aac2cb004.png

【RGD/三版通用】SINVideo for RGD(视频功能增强·拓展改造): 6620aac2cb004.png

 

【RGD/三版通用】SINVideo for RGD(视频功能增强·拓展改造):
起因是前段时间看到有人整理“使用RGD后的播放视频解决方案”,结果还是没什么稳定的替代方案
我就想起了SINVideo这个用DirectShow写入Bitmap内存来实现的老物(原贴:https://rpg. blue/thread-306831-1-1.html
不知是帖子标题难以搜索、效率或兼容存在问题还是单纯年代过于久远的原因,一直都没什么讨论度,感到颇为可惜
试着用RGD的Bitmap#process_color改造了一下发现效率还算可以,800x600+60FPS的视频基本也运行能稳60帧

本来到这里就算完了,然后我就发现SINVideo实现的功能太少了...基本上除了播放和停止,只有个变更音量和重播,甚至连暂停都没有
于是去翻阅了文档把DirectShow本来有的几个方法加上了,因为没dll实际源码也没编译环境,还是采用的内存修改的方式做的拓展
由于对汇编不怎么了解实在是苦战了一番,不过多亏原贴有附同种实现的源码,花了一周多时间也终于还是搞定了
我个人没有放视频的需求,囤手里烂了也不应当,分享出来看看能不能作为一种解决方案吧
为了方便演示,顺带研究了一下截获窗口标题的FPS值,搞了一些基于RGD和RGSS300/301.dll的内核修改,大概过些日子也会发布【当然这个是后话了



已在Win10/Win7环境下,使用RGD(v1.3.2/1.5.4/1.6.1)/VA/XP测试通过,暂未发现问题
【人家dll都现成的 就算有问题我也没(bu)法(fang)修(bian)x
视频文件基本只支持AVI格式和MPEG系列编码,可以用格式工厂之类的工具查看和转换
以及理所当然的,视频文件不支持放入加密档

由于使用起来需要一定的代码基础,推荐下载范例工程
原版演示视频无声,Video文件夹下另行准备了名为title1.avi的视频文件,将其替换title.avi可用于测试音频相关的功能
按ESC/X键暂停继续,左右键变速,上下键调整音量,A/S键调整声道
进入标题画面并开始游戏后,与NPC交互以演示在地图上播放视频(故意将视频坐标调整到了玩家脚下)

截图(GIF警告)







范例工程(推荐


蓝奏云(密码:lei)
文件体积较大,不方便以附件形式上传,如果实在无法访问我再考虑其他方式
SINVideo.dll 核心



SINVideo for RGD 本体脚本
SINVideo_RGD
[Ruby] 纯文本查看 复制代码
#encoding: utf-8
#
# ■ [SIN]Video(RGD兼容)
#        MPEG Player for RPG Maker / RGDirect (RGD)
#
#  使用你的精灵来播放 MPEG 视频(RGD/XP/VX/VA通用)
#
# [更新记录]
#    - 2013.04.21 By Shy07, Syalon(真正的作者)
#      * 初版亦终版
#
#    - 2022.07.27 By 岚风 雷(gqxastg)
#      * 优化代码,改造为RGD兼容版
#
# [使用方法]
#    - SIN::PLAYER.play("Video/title.avi")  #=> 加载并开始播放视频(可追加音量)
#    - SIN::PLAYER.update                   #=> 更新视频画面,原则上必须每帧调用
#    - SIN::PLAYER.stop                     #=> 停止播放并释放视频
#
#    - SIN::PLAYER.playing?                 #=> 获取视频是否播放中
#    - SIN::PLAYER.rewind                   #=> 从头重新开始播放
#    - SIN::PLAYER.replay_at_finish         #=> 播放完成后则重新播放
#    - SIN::PLAYER.volume                   #=> 获取播放音量(-10000~0)
#    - SIN::PLAYER.volume = 0               #=> 设置播放音量
#    - SIN::Video.regen                     #=> 重新生成播放器精灵(可追加参数)
#
#    - 其余使用方法请参考下面的代码以及范例工程中的 SINVideoDemo
#
#
module SIN
  module VideoConfig
    #------------------------------------------------------------------------
    # ■ 以下为设定部分
    #------------------------------------------------------------------------

    # SINVideo.dll的存放路径

    DLLPATH = 'System/SINVideo.dll'

    # 使用原版时的视频更新模式
    # 该项为true时: 效率优先,直接修改位图数据
    # 该项为false时:稳定优先,通过缓存中转修改
    # 默认为true,如使用RGD则无须修改此项

    ORIMODE = true

    # 使用RGD时的视频显示模式
    # RGD因位图存储格式不同,相对原版从下往上显示将导致颠倒
    # 该项为0时:初始会自动将mirror和angle分别设为true和180以矫正颠倒
    # 该项为1时:每帧更新视频时,自动翻转位图数据以矫正颠倒
    # 该项为nil时则不作任何处理
    # 默认为0,如使用原版则无须修改此项

    RGDFlip = 0

    #------------------------------------------------------------------------
    # ■ 设定部分到此结束,使用者无须修改以下内容
    #------------------------------------------------------------------------
    ORIMODE = false if defined?(RGD)
    RGDFlip = nil unless defined?(RGD)
  end
  #
  class ::Bitmap
    # API
    @@RtlMoveMemory_pl = Win32API.new('kernel32', 'RtlMoveMemory', 'pll', 'l')
    @@RtlMoveMemory_lp = Win32API.new('kernel32', 'RtlMoveMemory', 'lpl', 'l')
    @@RtlMoveMemory_ll = Win32API.new('kernel32', 'RtlMoveMemory', 'lll', 'l')##
    def _dump(limit) ############
      data = "rgba" * width * height
      if respond_to?(:process_color)
        process_color {|color_arr| color_arr.save_data(data, width * height) }
      else
        @@RtlMoveMemory_pl.call(data, address, width * height * 4)
      end
      [width, height, Zlib::Deflate.deflate(data)].pack("LLa*")
    end
    def self._load(str) #############
      w, h, zdata = str.unpack("LLa*")
      data = Zlib::Inflate.inflate(zdata)
      bmp = self.new(w, h)
      if method_defined?(:process_color)
        bmp.process_color {|color_arr| color_arr.load_data(data, w * h) }
      else
        @@RtlMoveMemory_lp.call(bmp.address, Zlib::Inflate.inflate(zdata),
        w * h * 4)
      end
      return bmp
    end
    def direct_load(str, rev = false) #############
      if rev
        buf = "\0" * 4 * width * height # str.bytesize
        len = width * 4
        src = [str].pack('p').unpack('L').first + width * height * 4
        dst = [buf].pack('p').unpack('L').first
        height.times do |i|
          src -= len
          @@RtlMoveMemory_ll.call(dst, src, len)
          dst += len
        end
        str = buf
      end
      #str = str.scan(/.{#{width * 4}}/m).reverse.join if rev
      if respond_to?(:process_color)
        process_color {|color_arr| color_arr.load_data(str, width * height) }
      else
        @@RtlMoveMemory_lp.call(address, str, width * height * 4)
      end
      self
    end
    # [[[bitmap.object_id * 2 + 16] + 8] + 16] == header
    def address
      buffer, ad = "rgba", object_id * 2 + 16
      @@RtlMoveMemory_pl.call(buffer, ad, 4)
      ad = buffer.unpack("L")[0] + 8
      @@RtlMoveMemory_pl.call(buffer, ad, 4)
      ad = buffer.unpack("L")[0] + 16
      @@RtlMoveMemory_pl.call(buffer, ad, 4)
      return buffer.unpack("L")[0]
    end
    #
    def g_fill_rect(*arg)
      if arg[0].is_a? Rect
        col1, col2 = 1, 2
      else
        col1, col2 = 4, 5
      end
      color1, color2 = arg[col1], arg[col2]
      arg[col1] = Color.new((color1 >> 16) & 0xFF,
                      (color1 >> 8) & 0xFF, (color1 & 0xFF), (color1 >> 24))
      arg[col2] = Color.new((color2 >> 16) & 0xFF,
                      (color2 >> 8) & 0xFF, (color2 & 0xFF), (color2 >> 24))
      gradient_fill_rect(*arg)
    end
  end
  #
  class Video < Sprite

    DLLPATH = VideoConfig::DLLPATH ############# 'System/SINVideo.dll'

    @@__video__ = nil

    def self.new(viewport = nil)
      return @@__video__ ||= super(viewport)
    end

    def self.regen(*args) #############
      @@__video__.dispose if @@__video__ && !@@__video__.disposed?
      @@__video__ = nil
      ::SIN.const_set(:PLAYER, Video.new(*args))
    end

    undef clone rescue nil
    undef dup rescue nil

    VideoLoad       = Win32API.new(DLLPATH, "__rgssx_video_load", "ppp", "i")
    VideoPlay       = Win32API.new(DLLPATH, "__rgssx_video_play", "ll", "v")
    VideoPlay_p     = Win32API.new(DLLPATH, "__rgssx_video_play", "pl", "v")###
    VideoPlaying    = Win32API.new(DLLPATH, "__rgssx_video_is_playing", "v", "i")
    VideoUpdate     = Win32API.new(DLLPATH, "__rgssx_video_update", "v", "v")
    VideoStop       = Win32API.new(DLLPATH, "__rgssx_video_stop", "v", "v")
    VideoRewind     = Win32API.new(DLLPATH, "__rgssx_video_rewind", "v", "v")
    VideoGetVolume  = Win32API.new(DLLPATH, "__rgssx_video_get_volume", "v", "l")
    VideoSetVolume  = Win32API.new(DLLPATH, "__rgssx_video_set_volume", "l", "v")

    def initialize(*args) ############## viewport = nil
      super(*args) ############## viewport
      @__playing = false
      ##############
      return unless VideoConfig::RGDFlip == 0
      self.angle = 180
      self.mirror = true
    end

    def dispose
      stop
      super
    end

    def volume
      return VideoGetVolume.call
    end

    def volume=(v)
      VideoSetVolume.call(v)
    end  

    def playing?
      return false unless @__playing
      return true if VideoPlaying.call != 0
      false #stop ############## 取消自动停止,避免出错
    end

    def replay_at_finish
      return unless @__playing
      return if VideoPlaying.call != 0
      VideoRewind.call
    end

    def stop
      return unless @__playing
      VideoStop.call
      bitmap.dispose
      self.bitmap = nil
      @bmpbuf = nil ##############
      @__playing = false
    end

    def rewind
      VideoRewind.call
    end

    def play(filename, volume = 0)
      return Video.regen.play(filename, volume) if disposed? ##############
      w = [0].pack("L")
      h = [0].pack("L")
      return false if VideoLoad.call(filename, w, h) == 0
      w = w.unpack("L").first
      h = h.unpack("L").first
      bitmap.dispose if bitmap
      self.bitmap = Bitmap.new(w, h)
      self.ox = w / 2
      self.oy = h / 2
      self.x = Graphics.width / 2
      self.y = Graphics.height / 2

      @bmpbuf = "\0" * w * h * 4 ##############
      #@bmpbuf.force_encoding('ASCII-8BIT')

      ##############
      if VideoConfig::ORIMODE
        VideoPlay.call(bitmap.address, volume)
      else
        VideoPlay_p.call(@bmpbuf, volume) ##############
      end
      ##############

      #self.bitmap.direct_load(@bmpbuf, VideoConfig::RGDFlip == 1)##############

      @__playing = true
      return true
    end

    def update
      return unless @__playing
      VideoUpdate.call
      self.bitmap.direct_load(@bmpbuf, VideoConfig::RGDFlip == 1) unless
      VideoConfig::ORIMODE ##############
      super
    end

  end

  PLAYER.dispose if const_defined?(:PLAYER) && !PLAYER.disposed? ##############
  PLAYER = Video.new # ||=
end


SINVideo 拓展插件(请置于本体下方)
SINVideo_Hacker
[Ruby] 纯文本查看 复制代码
#encoding: utf-8
#
# ■ [SIN]Video Hacker
#        Extension Plugin for SINVideo
#
#  SINVideo(视频功能增强)拓展插件
#
# [更新记录]
#    - 2022.07.27 By 岚风 雷(gqxastg)
#      * 初版
#
# [使用方法]
#    - 首先请确保安装了SINVideo脚本(原版或RGD兼容版皆可),并将此脚本置于其下
#
#    - SIN::PLAYER.convert_time(1, :SECOND, :MEDIA) #=>
#      转换播放时间单位(:SECOND为秒,:MEDIA为默认单位)
#
#    - SIN::PLAYER.getDuration        #=> 获取视频的完整长度(不受结束位置影响)
#    - SIN::PLAYER.getCurrentPosition #=> 获取播放的当前位置
#    - SIN::PLAYER.getStopPosition    #=> 获取播放的结束位置
#    - SIN::PLAYER.getPositions       #=> 获取播放的当前位置和结束位置
#    - SIN::PLAYER.setPositions(0)    #=> 设置播放的当前位置和结束位置
#      可使用 :AbsolutePositioning 和 :NoPositioning 标志来分别指定是否要设置
#      如:(0, :NoPositioning, 100, :AbsolutePositioning) 则只设置结束位置
#
#    - SIN::PLAYER.getCurFile         #=> 获取当前播放视频的文件名
#    - SIN::PLAYER.getState           #=> 获取当前播放状态(0结束、1暂停、2播放)
#    - SIN::PLAYER.pausing?           #=> 获取视频是否暂停中
#    - SIN::PLAYER.pause              #=> 暂停播放
#    - SIN::PLAYER.run                #=> 继续播放(暂停时)
#    - SIN::PLAYER.getRate            #=> 获取播放倍速(正常速度为1.0)
#    - SIN::PLAYER.setRate(2.0)       #=> 设置播放倍速
#
#    - SIN::PLAYER.get_Balance        #=> 获取播放声道平衡(-100~100)
#    - SIN::PLAYER.put_Balance(-5.0)  #=> 设置播放声道平衡
#    - SIN::PLAYER.get_Volume         #=> 获取播放音量(0~100)
#    - SIN::PLAYER.put_Volume(50.0)   #=> 设置播放音量
#
#    - SIN::PLAYER.playing? 判断播放中包括暂停状态
#    - SIN::PLAYER.getPositions 等方法涉及时间的数值请用convert_time进行转换
#
#    - 其余使用方法请参考下面的代码以及范例工程中的 SINVideoDemo
#
#
module SIN
  raise '未检测到SINVideo,请将此脚本置于其下' unless defined?(SIN::Video)
  # 兼容XP/VX
  class ::String
    def bytesize; size; end unless method_defined?(:bytesize)
    def start_with?(*args)
      args.any? {|s| self[0, s.size] == s.to_s }
    end unless method_defined?(:start_with?)
  end
  class ::Symbol
    def upcase;  self.to_s.upcase.to_sym; end unless method_defined?(:upcase)
    def to_proc; proc {|a| a.send(self) }; end unless method_defined?(:to_proc)
  end
  class ::Array
    alias videohacker_flatten flatten unless ([].flatten(0) rescue false)
    def flatten(level = nil)
      return self.videohacker_flatten if !level || (level < 0)
      return self if level == 0
      from, to = nil, self
      level.times do
        from, to = to, []
        from.each {|a| a.is_a?(Array) ? to.push(*a) : to.push(a) }
      end
      to
    end if method_defined?(:videohacker_flatten)
  end
  #----------------------------------------------------------------------------
  module VideoHacker
    MultiByteToWideChar =
    Win32API.new('kernel32', 'MultiByteToWideChar', 'iipipi', 'i')
    WideCharToMultiByte =
    Win32API.new('kernel32', 'WideCharToMultiByte', 'iipipipp', 'i')
    #--------------------------------------------------------------------------
    def self.u2w(str)
      buf = "\0" * 2 *
      MultiByteToWideChar.call(65001, 0, str, -1, nil, 0)
      MultiByteToWideChar.call(65001, 0, str, -1, buf, buf.bytesize / 2)
      buf
    end
    #--------------------------------------------------------------------------
    def self.w2u(str, zero = true)
      buf = "\0" *
      WideCharToMultiByte.call(65001, 0, str, -1, nil, 0, nil, nil)
      WideCharToMultiByte.call(65001, 0, str, -1, buf, buf.bytesize, nil, nil)
      buf.sub!(/\0+$/, '') if zero
      buf
    end
    #--------------------------------------------------------------------------

    RtlMoveMemory_pl = Win32API.new('kernel32', 'RtlMoveMemory', 'pll', 'l')
    RtlMoveMemory_lp = Win32API.new('kernel32', 'RtlMoveMemory', 'lpl', 'l')
    RtlMoveMemory_ll = Win32API.new('kernel32', 'RtlMoveMemory', 'lll', 'l')
    #RtlMoveMemory_pp = Win32API.new('kernel32', 'RtlMoveMemory', 'ppl', 'l')
    GetModuleHandle  = Win32API.new('kernel32', 'GetModuleHandle', 'p', 'l')

    CallWindowProc_lplll =
    Win32API.new('user32', 'CallWindowProc', 'lpLLL', 'i')
    CallWindowProc_lllll =
    Win32API.new('user32', 'CallWindowProc', 'llLLL', 'i')
    CallWindowProc_lpppp =
    Win32API.new('user32', 'CallWindowProc', 'lpppp', 'i')
    #CallWindowProc_lplpl =
    #Win32API.new('user32', 'CallWindowProc', 'lpLpL', 'i')

    MEM_COMMIT  = 0x00001000
    MEM_RESERVE = 0x00002000
    MEM_RELEASE = 0x00008000
    PAGE_EXECUTE_READWRITE = 0x40

    VirtualAlloc = Win32API.new('kernel32', 'VirtualAlloc', 'llll', 'l')
    VirtualFree  = Win32API.new('kernel32', 'VirtualFree', 'lll', 'l')
    #VirtualProtect = Win32API.new('kernel32', 'VirtualProtect', 'llll', 'l')

    #--------------------------------------------------------------------------

    S_OK = 0x00000000
    S_FALSE = 0x00000001
    E_INVALIDARG = 0x80070057
    E_OUTOFMEMORY = 0x8007000E
    E_UNEXPECTED = 0x8000FFFF
    E_NOTIMPL = 0x80004001
    E_FAIL = 0x80004005
    E_POINTER = 0x80004003
    E_HANDLE = 0x80070006
    E_ABORT = 0x80004004
    E_ACCESSDENIED = 0x80070005
    E_NOINTERFACE = 0x80004002

    VFW_S_STATE_INTERMEDIATE = 0x00040237
    VFW_S_CANT_CUE = 0x00040268
    VFW_E_NOT_CONNECTED = 0x80040209
    VFW_E_WRONG_STATE = 0x80040227
    VFW_E_UNSUPPORTED_AUDIO = 0x8004025C
    VFW_E_UNSUPPORTED_VIDEO = 0x8004025D

    ReturnValueList = constants.select {|sym|
    sym.to_s.start_with?('S_', 'E_', 'VFW_S_', 'VFW_E_') }.collect(&:to_sym)

    AM_SEEKING_NoPositioning = 0x00
    AM_SEEKING_AbsolutePositioning = 0x01
    AM_SEEKING_RelativePositioning = 0x02
    AM_SEEKING_IncrementalPositioning = 0x03
    AM_SEEKING_PositioningBitsMask = 0x03
    AM_SEEKING_SeekToKeyFrame = 0x04
    AM_SEEKING_ReturnTime = 0x08
    AM_SEEKING_Segment = 0x10
    AM_SEEKING_NoFlush = 0x20

    ALL_TIME_FORMAT = %w{TIME_FORMAT_NONE TIME_FORMAT_FRAME TIME_FORMAT_SAMPLE
    TIME_FORMAT_FIELD TIME_FORMAT_BYTE TIME_FORMAT_MEDIA_TIME}.collect(&:to_sym)

    ConvertTimeList = %w{SECOND FRAME MEDIA}.collect(&:to_sym)

    TIME_FORMAT_NONE       = "\0" * 16
    TIME_FORMAT_FRAME      = [0x7b785570, 0x8c82, 0x11cf,
    0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
    TIME_FORMAT_SAMPLE     = [0x7b785572, 0x8c82, 0x11cf,
    0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
    TIME_FORMAT_FIELD      = [0x7b785573, 0x8c82, 0x11cf,
    0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
    TIME_FORMAT_BYTE       = [0x7b785571, 0x8c82, 0x11cf,
    0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
    TIME_FORMAT_MEDIA_TIME = [0x7b785574, 0x8c82, 0x11cf,
    0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')

    #--------------------------------------------------------------------------

    DSHOWprocList = {
      :m_pBasicAudio => [
        [:put_Volume,  0x1C, 'put_Volume'],
        [:get_Volume,  0x20, 'get_Volume'],
        [:put_Balance, 0x24, 'put_Balance'],
        [:get_Balance, 0x28, 'get_Balance'],
      ],
      :m_pSampleGrabber => [
        [:GetCurrentBuffer, 0x1C, 'getCurrentBuffer'],
      ],
      :m_pMediaSeeking => [
        [:SetTimeFormat,      0x18, 'setTimeFormat'],
        [:GetTimeFormat,      0x1C, 'getTimeFormat'],
        [:IsUsingTimeFormat,  0x20, 'isUsingTimeFormat'],
        [:IsFormatSupported,  0x24, 'isFormatSupported'],
        [:GetDuration,        0x28, 'getDuration'],
        [:GetStopPosition,    0x2C, 'getStopPosition'],
        [:GetCurrentPosition, 0x30, 'getCurrentPosition'],
        [:ConvertTimeFormat,  0x34, 'convertTimeFormat'],
        [:SetPositions,       0x38, 'setPositions'],
        [:GetPositions,       0x3C, 'getPositions'],
        [:GetAvailable,       0x40, 'getAvailable'],
        [:SetRate,            0x44, 'setRate'],
        [:GetRate,            0x48, 'getRate'],
      ],
      :m_pMediaControl => [
        [:Run,      0x1C, 'run'],
        [:Pause,    0x20, 'pause'],
        [:Stop,     0x24, nil],
        [:GetState, 0x28, 'getState'],
      ],
      :m_pMediaEvent => [
      ],
      :pFileSourceFilter => [
        [:Load,       0x0C, nil],
        [:GetCurFile, 0x10, 'getCurFile'],
      ],
    }

    #--------------------------------------------------------------------------

    arr = DSHOWprocList.values.flatten(1)
    ProcSym = arr.collect {|a| a[0] }
    MethodSym = arr.collect {|a| a[2] }.compact

    DLLmodule = GetModuleHandle.call(File.basename(::SIN::Video::DLLPATH))
    DLLproc = {}
    DSHOWproc = {}
    ProcSym.each {|key| DSHOWproc[key] = 0 }

    #DSHOWproc_p = DSHOWproc.clone
    #DSHOWproc_p.each {|key, value| DSHOWproc_p[key] = [value].pack('L') }

    #--------------------------------------------------------------------------

    if const_defined?(:MACHproc) && (ptr = MACHproc.values[0]) && ptr != 0
      VirtualFree.call(ptr, 0, MEM_RELEASE)
    end

    MACHcode = {
      :BASE =>
      [0x8B, 0x44, 0x24, 0x04, 0x8B, 0x4C, 0x24, 0x08, 0xFF, 0x34, 0x88, 0xE2,
       0xFB, 0xFF, 0x10, 0xC3].pack('C*'),
      :GetCurrentBuffer =>
      [0x8B, 0x44, 0x24, 0x04, 0x8B, 0x4C, 0x24, 0x08, 0xB2, 0xFF, 0x83, 0xC0,
       0x03, 0x88, 0x10, 0x83, 0xC0, 0x04, 0xE2, 0xF9, 0xC2, 0x10, 0x00].
       pack('C*'),
    }
#~     [0x8B, 0x44, 0x24, 0x04, 0xFF, 0x70, 0x14, 0xFF, 0x70, 0x10, 0xFF, 0x70,
#~      0x0C, 0xFF, 0x70, 0x08, 0xFF, 0x70, 0x04, 0xFF, 0x10, 0xC3].pack('C*'),
#~     [0x89, 0xE0, 0xFF, 0x70, 0x10, 0xFF, 0x70, 0x0C, 0xFF, 0x70, 0x08, 0xFF,
#~     0x70, 0x04, 0x68].pack('C*') +
#~     [DLLproc[1]].pack('L') +
#~     [0xFF, 0x15].pack('C*') +
#~     [DSHOWproc_p[:SetPositions]].pack('p') +
#~     [0xC3].pack('C*')
    MACHproc = {}

    hash = MACHcode#.clone
    #hash.each {|key, value| hash[key] = value.call }
    all = hash.values.join

    ptr = VirtualAlloc.call(0, all.bytesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    raise '无法申请内存' if ptr == 0
    RtlMoveMemory_lp.call(ptr, all, all.bytesize)

    hash.each do |key, value|
      MACHproc[key] = ptr
      ptr += value.bytesize
    end

    #--------------------------------------------------------------------------

    buf = "\0" * 4
    RtlMoveMemory_pl.call(buf, DLLmodule + 0xD8AC, 4)
    DLLproc[:BASE] = buf.unpack('L').first

    #--------------------------------------------------------------------------
    def self.recalc_DSHOWproc#(resetMACH = true)
      buf = "\0" * 4

      # mov esi,[SINVideo.dll+D8AC]

      # m_pBasicAudio
      # mov eax,[esi+14]
      RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x14, 4)
      DLLproc[:m_pBasicAudio] = buf.unpack('L').first

      # m_pSampleGrabber
      # mov eax,[esi+10]
      RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x10, 4)
      DLLproc[:m_pSampleGrabber] = buf.unpack('L').first

      # m_pMediaSeeking->SetPositions
      # mov eax,[esi+0C]
      RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x0C, 4)
      DLLproc[:m_pMediaSeeking] = buf.unpack('L').first

      # m_pMediaControl->Stop
      # mov eax,[esi+04]
      RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x04, 4)
      DLLproc[:m_pMediaControl] = buf.unpack('L').first

      # m_pMediaEvent
      # mov eax,[esi+08]
      RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x08, 4)
      DLLproc[:m_pMediaEvent] = buf.unpack('L').first

      # m_pGraphBuilder
      # mov eax,[esi]
      RtlMoveMemory_pl.call(buf, DLLproc[:BASE], 4)
      DLLproc[:m_pGraphBuilder] = buf.unpack('L').first

      # pFileSourceFilter
      # [[[[[SINVideo.dll+D8AC]]+BC]+8]]+44
      adr = DLLproc[:BASE]
      [0, 0xBC, 8, 0].each do |off|
        buf = "\0" * 4
        RtlMoveMemory_pl.call(buf, adr + off, 4)
        adr = buf.unpack('L').first
        break if adr == 0
      end
      adr += 0x44 unless adr == 0
      DLLproc[:pFileSourceFilter] = adr

      #------------------------------------------------------------------------

      DSHOWprocList.each do |key, value|
        value.each do |arr|
          sym, off = arr
          # mov eax,[SINVideo.dll+D8AC]
          # mov eax,[eax+**]
          adr = DLLproc[key]
          break value.each {|a| DSHOWproc[a[0]] = 0 } if adr == 0
          buf = "\0" * 4
          # mov eax,[eax]
          RtlMoveMemory_pl.call(buf, adr, 4)
          adr = buf.unpack('L').first
          next DSHOWproc[sym] = 0 if adr == 0
          # mov eax,[eax+**]
          RtlMoveMemory_pl.call(buf, adr + off, 4)
          DSHOWproc[sym] = buf.unpack('L').first
        end
      end

      #DSHOWproc_p.each {|key, value|
      #RtlMoveMemory_pp.call(value, [DSHOWproc[key]].pack('L'), 4) }

      #reset_MACHcode if resetMACH
    end
    #--------------------------------------------------------------------------
    def self.clear_DSHOWproc
      DSHOWproc.each_key {|key| DSHOWproc[key] = 0 }
    end
    #--------------------------------------------------------------------------
#~     def self.reset_MACHcode
#~       return if MACHproc.values[0] == 0
#~       all = MACHcode.values.collect(&:call).join
#~       RtlMoveMemory_lp.call(MACHproc.values[0], all, all.bytesize)
#~     end
    #--------------------------------------------------------------------------
    def self.convert_ret(ret)
      ret = ret + 0x100000000 if ret < 0
      sym = ReturnValueList.find {|s| const_get(s) == ret }
      sym ? sym : ret
    end
    #--------------------------------------------------------------------------
    def self.convert_time(time, from = :SECOND, to = :MEDIA)
      2.times do |i|
        format = (i == 0) ? from : to
        format = (format.is_a?(Symbol) || format.is_a?(String)) ?
                  format.to_sym.upcase : ConvertTimeList[format.to_i]
        (i == 0) ? from = format : to = format
      end
      case from
      when :SECOND
      when :FRAME then time = time / Graphics.frame_rate.to_f
      when :MEDIA then time = time / 10000000.0
      end
      case to
      when :SECOND
      when :FRAME then time = (time * Graphics.frame_rate).to_i
      when :MEDIA then time = (time * 10000000).to_i
      end
      time
    end
    #--------------------------------------------------------------------------
    # m_pBasicAudio
    #--------------------------------------------------------------------------
    def self.put_Volume(lVolume = 100)
      key, sym = :m_pBasicAudio, :put_Volume
      return false if DSHOWproc[sym] == 0
      lVolume = (lVolume * 100 - 10000).to_i
      params = [DSHOWproc[sym], DLLproc[key], lVolume].pack('LLl')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      convert_ret(ret)
    end
    #--------------------------------------------------------------------------
    def self.get_Volume
      key, sym = :m_pBasicAudio, :get_Volume
      return false if DSHOWproc[sym] == 0
      plVolume = "\0" * 4 #[0].pack('L')
      params = [DSHOWproc[sym], DLLproc[key], plVolume].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      (plVolume.unpack('l').first + 10000) / 100.0
    end
    #--------------------------------------------------------------------------
    def self.put_Balance(lBalance = 0)
      key, sym = :m_pBasicAudio, :put_Balance
      return false if DSHOWproc[sym] == 0
      lBalance = (lBalance * 100).to_i
      params = [DSHOWproc[sym], DLLproc[key], lBalance].pack('LLl')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      convert_ret(ret)
    end
    #--------------------------------------------------------------------------
    def self.get_Balance
      key, sym = :m_pBasicAudio, :get_Balance
      return false if DSHOWproc[sym] == 0
      plBalance = "\0" * 4 #[0].pack('L')
      params = [DSHOWproc[sym], DLLproc[key], plBalance].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      plBalance.unpack('l').first / 100.0
    end
    #--------------------------------------------------------------------------
    # m_pSampleGrabber
    #--------------------------------------------------------------------------
    def self.getCurrentBuffer(dst = nil)
      key, sym = :m_pSampleGrabber, :GetCurrentBuffer
      return false if DSHOWproc[sym] == 0
      pBufferSize = "\0" * 4 #[0].pack('L')
      pBuffer = nil
      params = [DSHOWproc[sym], DLLproc[key], pBufferSize, pBuffer].pack('LLpp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
      size = pBufferSize.unpack('L').first
      return false if size == 0

      if dst.is_a?(Integer)
        pBuffer = true
        params = [DSHOWproc[sym], DLLproc[key], pBufferSize, dst].pack('LLpL')
      else
        pBuffer = dst ? dst : ("\0" * size)
        params =
        [DSHOWproc[sym], DLLproc[key], pBufferSize, pBuffer].pack('LLpp')
      end
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
      return false if ret != 0
      pBuffer
    end
    #--------------------------------------------------------------------------
    # m_pMediaSeeking
    #--------------------------------------------------------------------------
    def self.setTimeFormat(pFormat = :TIME_FORMAT_MEDIA_TIME)
      key, sym = :m_pMediaSeeking, :SetTimeFormat
      return false if DSHOWproc[sym] == 0
      pFormat = const_get(pFormat)
      params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
      CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
    end
    #--------------------------------------------------------------------------
    def self.getTimeFormat
      key, sym = :m_pMediaSeeking, :GetTimeFormat
      return false if DSHOWproc[sym] == 0
      pFormat = "\0" * 16
      params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      ALL_TIME_FORMAT.find {|key| const_get(key) == pFormat }
    end
    #--------------------------------------------------------------------------
    def self.isUsingTimeFormat(pFormat)
      key, sym = :m_pMediaSeeking, :IsUsingTimeFormat
      return false if DSHOWproc[sym] == 0
      pFormat = const_get(pFormat)
      params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      ret == 0
    end
    #--------------------------------------------------------------------------
    def self.isFormatSupported(pFormat)
      key, sym = :m_pMediaSeeking, :IsFormatSupported
      return false if DSHOWproc[sym] == 0
      pFormat = const_get(pFormat)
      params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
      CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
    end
    #--------------------------------------------------------------------------
    def self.getDuration
      key, sym = :m_pMediaSeeking, :GetDuration
      return false if DSHOWproc[sym] == 0
      pDuration = "\0" * 8 #[0].pack('q')
      params = [DSHOWproc[sym], DLLproc[key], pDuration].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      pDuration.unpack('q').first
    end
    #--------------------------------------------------------------------------
    def self.getStopPosition
      key, sym = :m_pMediaSeeking, :GetStopPosition
      return false if DSHOWproc[sym] == 0
      pStop = "\0" * 8 #[0].pack('q')
      params = [DSHOWproc[sym], DLLproc[key], pStop].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      pStop.unpack('q').first
    end
    #--------------------------------------------------------------------------
    def self.getCurrentPosition
      key, sym = :m_pMediaSeeking, :GetCurrentPosition
      return false if DSHOWproc[sym] == 0
      pCurrent = "\0" * 8 #[0].pack('q')
      params = [DSHOWproc[sym], DLLproc[key], pCurrent].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      pCurrent.unpack('q').first
    end
    #--------------------------------------------------------------------------
    def self.convertTimeFormat(pTargetFormat, source, pSourceFormat = nil)
      key, sym = :m_pMediaSeeking, :ConvertTimeFormat
      return false if DSHOWproc[sym] == 0
      pTarget = "\0" * 8 #[0].pack('q')
      pTargetFormat = const_get(pTargetFormat) if pTargetFormat
      pSourceFormat = const_get(pSourceFormat) if pSourceFormat
      params = [DSHOWproc[sym], DLLproc[key],
      pTarget, pTargetFormat, source, pSourceFormat].pack('LLppqp')
      # 因为传的是longlong(8字节)所以多占一个参数
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 6, 0, 0)
      pTarget.unpack('q').first
    end
    #--------------------------------------------------------------------------
    def self.setPositions(pCurrent = 0, dwCurrentFlags = 1, pStop = 0,
      dwStopFlags = 0)
      key, sym = :m_pMediaSeeking, :SetPositions
      return false if DSHOWproc[sym] == 0
      pCurrent = [pCurrent].pack('q')
      pStop = [pStop].pack('q')

      2.times do |i|
        flags = (i == 0) ? dwCurrentFlags : dwStopFlags
        flags = flags.is_a?(Array) ?
        flags.inject(0) {|r, m| r | const_get("AM_SEEKING_#{m}") } :
        (flags.is_a?(Symbol) || flags.is_a?(String)) ?
        const_get("AM_SEEKING_#{flags}") : flags.to_i
        (i == 0) ? dwCurrentFlags = flags : dwStopFlags = flags
      end

      params = [DSHOWproc[sym], DLLproc[key],
      pCurrent, dwCurrentFlags, pStop, dwStopFlags].pack('LLpLpL')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 5, 0, 0)
      #CallWindowProc_lplpl.call(MACHproc[:SetPositions],
      #pCurrent, dwCurrentFlags, pStop, dwStopFlags)

      ret = convert_ret(ret)
      ret = [ret, pCurrent.unpack('q').first, pStop.unpack('q').first] if
      (dwCurrentFlags & AM_SEEKING_ReturnTime != 0) ||
      (dwStopFlags & AM_SEEKING_ReturnTime != 0)
      ret
    end
    #--------------------------------------------------------------------------
    def self.getPositions
      key, sym = :m_pMediaSeeking, :GetPositions
      return false if DSHOWproc[sym] == 0
      pCurrent = "\0" * 8 #[0].pack('q')
      pStop = "\0" * 8 #[0].pack('q')
      params = [DSHOWproc[sym], DLLproc[key], pCurrent, pStop].pack('LLpp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
      [pCurrent.unpack('q').first, pStop.unpack('q').first]
    end
    #--------------------------------------------------------------------------
    def self.getAvailable
      key, sym = :m_pMediaSeeking, :GetAvailable
      return false if DSHOWproc[sym] == 0
      pEarliest = "\0" * 8 #[0].pack('q')
      pLatest = "\0" * 8 #[0].pack('q')
      params = [DSHOWproc[sym], DLLproc[key], pEarliest, pLatest].pack('LLpp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
      [pEarliest.unpack('q').first, pLatest.unpack('q').first]
    end
    #--------------------------------------------------------------------------
    def self.setRate(dRate = 1)
      key, sym = :m_pMediaSeeking, :SetRate
      return false if DSHOWproc[sym] == 0
      params = [DSHOWproc[sym], DLLproc[key], dRate].pack('LLD')
      # 因为传的是double(8字节)所以多占一个参数
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
      convert_ret(ret)
    end
    #--------------------------------------------------------------------------
    def self.getRate
      key, sym = :m_pMediaSeeking, :GetRate
      return false if DSHOWproc[sym] == 0
      pdRate = "\0" * 8 #[0].pack('D')
      params = [DSHOWproc[sym], DLLproc[key], pdRate].pack('LLp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
      pdRate.unpack('D').first
    end
    #--------------------------------------------------------------------------
    # m_pMediaControl
    #--------------------------------------------------------------------------
    def self.run
      key, sym = :m_pMediaControl, :Run
      return false if DSHOWproc[sym] == 0
      params = [DSHOWproc[sym], DLLproc[key]].pack('LL')
      CallWindowProc_lplll.call(MACHproc[:BASE], params, 1, 0, 0)
    end
    #--------------------------------------------------------------------------
    def self.pause
      key, sym = :m_pMediaControl, :Pause
      return false if DSHOWproc[sym] == 0
      params = [DSHOWproc[sym], DLLproc[key]].pack('LL')
      CallWindowProc_lplll.call(MACHproc[:BASE], params, 1, 0, 0)
    end
    #--------------------------------------------------------------------------
    def self.getState(msTimeout = 0)
      key, sym = :m_pMediaControl, :GetState
      return false if DSHOWproc[sym] == 0
      params = [DSHOWproc[sym], DLLproc[key],
      msTimeout, state = "\0" * 4].pack('LLlp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
      # State_Stopped = 0, State_Paused = 1, State_Running = 2
      state.unpack('L').first
    end
    #--------------------------------------------------------------------------
    # pFileSourceFilter
    #--------------------------------------------------------------------------
    def self.getCurFile
      key, sym = :pFileSourceFilter, :GetCurFile
      return false if DSHOWproc[sym] == 0
      ppszFileName = "\0" * 4 #[0].pack('L')
      pmt = nil #"\0" * (32 + 2 + 4 + 16 + 4 + 4 + 1)
      params = [DSHOWproc[sym], DLLproc[key], ppszFileName, pmt].pack('LLpp')
      ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)

      adr = ppszFileName.unpack('L').first
      if adr == 0
        name = nil
      else
        name, buf = '', "\0" * 2
        begin
          RtlMoveMemory_pl.call(buf, adr, 2)
          name << buf
          adr += 2
        end until buf == "\0\0"
        name = w2u(name)
      end
      #[name, pmt.unpack('a16a16C2La16LLC')]
      name
    end
  end
  class Video < Sprite
    class VideoUpdate_caller
      CallWindowProc_lplll = ::SIN::VideoHacker::CallWindowProc_lplll
      attr_accessor :mode, :default
      #------------------------------------------------------------------------
      def initialize(default)
        @default, @mode = default, false
      end
      #------------------------------------------------------------------------
      def call
        return @default.call unless @mode
        ::SIN::PLAYER.instance_eval do
          return 1 unless @bmpbuf
          getCurrentBuffer(@bmpbuf)
          return 1 unless @bmpbuf
          CallWindowProc_lplll.call(
          ::SIN::VideoHacker::MACHproc[:GetCurrentBuffer],
          @bmpbuf, width * height, 0, 0)
        end
        0
      end
    end
#~     VideoUpdate = VideoUpdate_caller.new(VideoUpdate) unless
#~                   VideoUpdate.is_a?(VideoUpdate_caller)
#~     VideoUpdate.mode = true
    #--------------------------------------------------------------------------
    alias hackerold_play play
    def play(*args)
      return hackerold_play(*args) if disposed?
      hackerold_play(*args)
      ::SIN::VideoHacker.recalc_DSHOWproc if @__playing
    end
    #--------------------------------------------------------------------------
    alias hackerold_stop stop
    def stop
      hackerold_stop
      ::SIN::VideoHacker.clear_DSHOWproc if
      ::SIN::VideoHacker::DSHOWproc.values[0] != 0
    end
    #--------------------------------------------------------------------------
    alias hackerold_playing? playing?
    def playing?
      return false unless @__playing
      return true if getState == 1 # 避免暂停状态被误判
      hackerold_playing?
    end
    #--------------------------------------------------------------------------
    alias hackerold_replay_at_finish replay_at_finish
    def replay_at_finish
      return unless @__playing
      return unless (s = getState) && s != 1 # 避免暂停状态被误判
      hackerold_replay_at_finish
    end
    #--------------------------------------------------------------------------
    def pausing?
      return false unless @__playing
      return true if getState == 1
      false
    end
    #--------------------------------------------------------------------------
    def convert_time(*args)
      ::SIN::VideoHacker.convert_time(*args)
    end
    #--------------------------------------------------------------------------
    ::SIN::VideoHacker::MethodSym.each do |name|
      define_method(name) do |*args|
        return unless @__playing
        ::SIN::VideoHacker.send(name, *args)
      end
    end
  end
end


附:演示代码(RGD/VA用)
SINVideoDemo
[Ruby] 纯文本查看 复制代码
#------------------------------------------------------------------------------
# ○ 改造自原版范例,仅供参考不建议直接使用
#------------------------------------------------------------------------------
Graphics.resize_screen(640, 480)

@viewport = Viewport.new ##########
$data_system ||= load_data("Data/System.rvdata2") ##########

SIN::PLAYER.viewport = @viewport ##########
SIN::PLAYER.play("Video/title.avi")
SIN::PLAYER.z = 50

#################################################################
@fps_spr = Sprite.new
@fps_spr.z = 0xFFFF
@fps_spr.bitmap = Bitmap.new(56, 40)
@fps_spr.bitmap.font.size = 32
lastfps = (Graphics.fps rescue '')
@fps_spr.bitmap.draw_text(@fps_spr.bitmap.rect, lastfps, 1)
updatefps = -> do
  if (n = (Graphics.fps rescue '')) != lastfps
    lastfps = n
    @fps_spr.bitmap.clear
    @fps_spr.bitmap.draw_text(@fps_spr.bitmap.rect, lastfps, 1)
  end
end
#################################################################

#################################################################
class << SIN::PLAYER
  alias videodemo_update update
  def update
    videodemo_update
    return unless @__playing
    begin
      @tt ||= 0
      p [::SIN::VideoHacker::MACHproc[:BASE],
      ::SIN::VideoHacker::DLLproc[:m_pMediaSeeking],
      ::SIN::VideoHacker::DSHOWproc[:SetPositions]].collect {|n|
      n.to_s(16) } if @tt == 0
      if @tt == 0
        s = convert_time(11) # * 10000000
        p setPositions(0, :NoPositioning, s, :AbsolutePositioning)
        #setTimeFormat(:TIME_FORMAT_FRAME)
        #p isUsingTimeFormat(:TIME_FORMAT_FRAME)
        #p isUsingTimeFormat(:TIME_FORMAT_MEDIA_TIME)
        #p isFormatSupported(:TIME_FORMAT_FRAME)
        #p isFormatSupported(:TIME_FORMAT_MEDIA_TIME)
        p getTimeFormat
        #p [getCurrentPosition, getStopPosition]
        p [t = getPositions, getDuration]
        p [t.collect {|n| convert_time(n, -1, 0) },
           convert_time(getDuration, -1, 0)]
        #p getAvailable
        #p setRate(2)
        #p getRate
        #p convertTimeFormat(:TIME_FORMAT_FRAME, t[0], :TIME_FORMAT_MEDIA_TIME)
        #p getState
        #p pause
        p getCurFile
      end
      #p run if @tt == 29
      @tt = (@tt + 1) % 60
    end
  end
end
#################################################################

@spt1 = Sprite.new
@spt1.bitmap = Bitmap.new(Graphics.width, Graphics.height) # 640, 480
@spt1.bitmap.font.size = 24
@spt1.bitmap.font.shadow = true
@spt1.bitmap.font.outline = false
@spt1.z = 100

@spt2 = Sprite.new(@viewport) ###########
@spt2.bitmap = Bitmap.new(Graphics.width, Graphics.height) # 640, 480
@spt2.bitmap.g_fill_rect(@spt2.bitmap.rect, 0xffff0000, 0xff0000ff)
@spt2.visible = false
@spt2.z = 0

#################################################################
if defined?(RGD) # RGD中Sprite的波效果无法显示位图尺寸外的部分 此处特别处理
  @display = Sprite.new(@viewport)
  @display.visible = false
  @display.bitmap = Bitmap.new(Graphics.width, Graphics.height)
  @display.ox = @display.bitmap.width / 2
  @display.oy = @display.bitmap.height / 2
  @display.x = Graphics.width / 2
  @display.y = Graphics.height / 2
  @display.z = 50

  rect = Rect.new((@display.bitmap.width - SIN::PLAYER.bitmap.width * 0.5) / 2,
  (@display.bitmap.height - SIN::PLAYER.bitmap.height * 0.5) / 2,
  SIN::PLAYER.bitmap.width * 0.5, SIN::PLAYER.bitmap.height * 0.5)
  @display.bitmap.stretch_blt(rect, SIN::PLAYER.bitmap, SIN::PLAYER.src_rect)

  class << SIN::PLAYER
    def bind_flash_sprite(sprite); @flash_sprite = sprite; end
    alias oldtest_flash flash
    def flash(*args) # color, duration
      @flash_sprite.flash(*args) if @flash_sprite && !@flash_sprite.disposed?
      oldtest_flash(*args)
    end
  end

  SIN::PLAYER.bind_flash_sprite(@display)
end
#################################################################

def show_msg(argh)
  return @spt1.bitmap.clear if argh[:clear]
  x = argh[:x].nil? ? 0 : argh[:x]
  y = argh[:y].nil? ? 0 : argh[:y]
  width = argh[:width].nil? ? 640 : argh[:width]
  height = argh[:height].nil? ? 480 : argh[:height]
  y = (Graphics.height - height) / 2 if y == :middle
  str = argh[:str].nil? ? "" : argh[:str]
  align = argh[:align].nil? ? 0 : argh[:align]
  align = 0 if align == :left
  align = 1 if align == :middle
  align = 2 if align == :right
  @spt1.bitmap.draw_text(x, y, width, height, str, align)
end

@step = 0
@wait = 30
@rotating = false

while true
  Graphics.update
  Input.update
  SIN::PLAYER.update
  SIN::PLAYER.replay_at_finish
  @spt1.update
  SIN::PLAYER.angle += 1 if @rotating

  updatefps.call ##########

  if Input.trigger?(:C) && @wait == -7
    show_msg({ :clear => true })
    case @step
    when 0
      SIN::PLAYER.zoom_x = 0.5
      SIN::PLAYER.zoom_y = 0.5
    when 1
      SIN::PLAYER.zoom_x = (Graphics.width * 1.0) / SIN::PLAYER.width
      SIN::PLAYER.zoom_y = (Graphics.height * 1.0) / SIN::PLAYER.height
    when 2
      SIN::PLAYER.mirror = SIN::VideoConfig::RGDFlip != 0 ########## true
    when 3
      SIN::PLAYER.zoom_x = 0.5
      SIN::PLAYER.zoom_y = 0.5
      @rotating = true
    when 4
      @spt2.visible = true
      SIN::PLAYER.blend_type = 2
    when 5
      SIN::PLAYER.blend_type = 0
      SIN::PLAYER.tone = Tone.new(0, 0, 0, 255)
    when 6
      SIN::PLAYER.tone = Tone.new(0, 0, 0, 0)
      SIN::PLAYER.flash(Color.new(0, 255, 0), 30)
    when 7
      SIN::PLAYER.wave_amp = 8
      SIN::PLAYER.wave_length = 240
      SIN::PLAYER.wave_speed = 360
    when 8
      #疑似开始旋转后角度置0以外的值会导致图片无法更新 原版就存在这个问题(RGD没事)
      SIN::PLAYER.angle = SIN::VideoConfig::RGDFlip == 0 ? 180 : 0 ##########
      @rotating = false
    when 9
      @spt2.visible = false
      SIN::PLAYER.mirror = SIN::VideoConfig::RGDFlip == 0 ########## false
      SIN::PLAYER.wave_amp = 0
      #SIN::PLAYER.wave_length = 0 # RGD里这样设置会崩溃(预设是360) ##########
      SIN::PLAYER.wave_speed = 0
      SIN::PLAYER.zoom_x = (Graphics.width * 1.0) / SIN::PLAYER.width
      SIN::PLAYER.zoom_y = (Graphics.height * 1.0) / SIN::PLAYER.height
    when 10
      rate = 255 / 60
      90.times do |i|
        Graphics.update
        updatefps.call ##########
        SIN::PLAYER.update
        SIN::PLAYER.opacity -= rate
      end
      SIN::PLAYER.stop
      break
    end
    @step += 1
    @wait = 30
  #################################################################
  elsif Input.trigger?(:B)
    p t = SIN::PLAYER.getPositions
    p t.collect {|n| SIN::PLAYER.convert_time(n, :MEDIA, :SECOND) }
    SIN::PLAYER.getState != 2 ?
    (Sound.play_ok rescue nil; p SIN::PLAYER.run) :
    (Sound.play_cancel rescue nil; p SIN::PLAYER.pause)
  elsif key = [:LEFT, :RIGHT].find {|k| Input.repeat?(k) }
    if key == :LEFT
      a = SIN::PLAYER.getRate - 0.25
      a = -0.25 if a == 0
    else
      a = SIN::PLAYER.getRate + 0.25
      a = 0.25 if a == 0
    end
    Sound.play_cursor rescue nil
    p SIN::PLAYER.setRate(a)
    p SIN::PLAYER.getRate
  elsif key = [:DOWN, :UP].find {|k| Input.repeat?(k) }
    if key == :DOWN
      a = SIN::PLAYER.get_Volume - 1
    else
      a = SIN::PLAYER.get_Volume + 1
    end
    Sound.play_cursor rescue nil
    p SIN::PLAYER.put_Volume(a)
    p SIN::PLAYER.get_Volume
  elsif key = [:X, :Y].find {|k| Input.repeat?(k) }
    if key == :X
      a = SIN::PLAYER.get_Balance - 1
    else
      a = SIN::PLAYER.get_Balance + 1
    end
    Sound.play_cursor rescue nil
    p SIN::PLAYER.put_Balance(a)
    p SIN::PLAYER.get_Balance
  end
  #################################################################

  #################################################################
  if SIN::PLAYER.zoom_x == 0.5
    @display.bitmap.clear
    rect = Rect.new((@display.bitmap.width - SIN::PLAYER.bitmap.width * 0.5) / 2,
    (@display.bitmap.height - SIN::PLAYER.bitmap.height * 0.5) / 2,
    SIN::PLAYER.bitmap.width * 0.5, SIN::PLAYER.bitmap.height * 0.5)
    @display.bitmap.stretch_blt(rect, SIN::PLAYER.bitmap, SIN::PLAYER.src_rect)

    %w{angle wave_amp wave_length wave_speed mirror opacity blend_type
    color tone}.each {|var|
    @display.send("#{var}=", SIN::PLAYER.send(var)) }
    @display.update
    @display.wave_phase = SIN::PLAYER.wave_phase
    @display.visible, SIN::PLAYER.visible = true, false
  else
    @display.visible, SIN::PLAYER.visible = false, true
  end if @display
  #################################################################

  next if @wait == -7
  if @wait.zero?
    case @step
    when 0
      show_msg({  :y      => :middle,
                  :height => 24,
                  :str    => "窗口太小,看不到全部画面?按下空格试试",
                  :align  => :middle
                })
    when 1
      show_msg({  :y      => 32,
                  :height => 24,
                  :str    => "画面太小,黑边难看?按下空格试试",
                  :align  => :middle
                })
    when 2
      show_msg({  :y      => :middle,
                  :height => 24,
                  :str    => "这样可以吧?但是再按下空格试试",
                  :align  => :middle
                })
    when 3
      show_msg({  :y      => 32,
                  :height => 24,
                  :str    => "再按下空格试试",
                  :align  => :middle
                })
    when 4
      show_msg({  :y      => 32,
                  :height => 24,
                  :str    => "再按下空格试试",
                  :align  => :middle
                })
    when 5
      show_msg({  :y      => 32,
                  :height => 24,
                  :str    => "再按……累死了,您自便吧,等结束了,我再回来",
                  :align  => :middle
                })
    when 10
      show_msg({  :y      => :middle,
                  :height => 24,
                  :str    => "演示就到这里,最后再按下空格,转到标题吧",
                  :align  => :middle
                })
    end
    @wait = -7
    next
  end
  @wait -= 1
end

#################################################################
@fps_spr.bitmap.dispose
@fps_spr.dispose
@spt1.bitmap.dispose
@spt2.bitmap.dispose
@spt1.dispose
@spt2.dispose
SIN::PLAYER.viewport = nil
@viewport.dispose

if @display
  @display.bitmap.dispose
  @display.dispose
  SIN::Video.regen
end
#################################################################


附:游戏窗口失去/获得焦点时自动暂停/继续的小插件(RGD用)

[Ruby] 纯文本查看 复制代码
module RGD
  class << self
    alias videopause_focus_out focus_out if method_defined?(:focus_out)
    alias videopause_focus_in focus_in if method_defined?(:focus_in)
  end
  # 失去焦点
  def self.focus_out
    SIN::PLAYER.pause if SIN::PLAYER.getState == 2
    videopause_focus_out if respond_to?(:videopause_focus_out)
  end
  # 获得焦点
  def self.focus_in
    SIN::PLAYER.run if SIN::PLAYER.getState == 1
    videopause_focus_in if respond_to?(:videopause_focus_in)
  end
end if defined?(RGD)


             本帖来自P1论坛作者gqxastg,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg. blue/forum.php?mod=viewthread&tid=490464  若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
【RGD/三版通用】SINVideo for RGD(视频功能增强·拓展改造): 6620aac2cb004.png
关闭

幸运抽奖

社区每日抽奖来袭,快来试试你是欧皇还是非酋~

立即查看

Loading...

QQ|Archiver|手机版|小黑屋|同能RPG制作大师 ( 沪ICP备12027754号-3 )

GMT+8, 2024-11-25 00:18 , Processed in 0.047588 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

返回顶部