Sprite_Vision for RMXP

这是一篇关于RMXP游戏引擎的自定义脚本,实现了Sprite_Vision地图视野功能。作者viktor提供了视野设置方法,通过地图标记来确定不同地形的高度,影响角色的视线。脚本中包含了视野的配置选项,如渐变速率、平滑开关等,并详细定义了不同的更新方法。
摘要由CSDN通过智能技术生成
#======================================
# Sprite_Vision 地图视野
# 作者: viktor
# 原创脚本。仅供讨论使用,不可以用于商用。转载请注明出处/暂时不对脚本进行更多的解释了。
# 设置视野方法:使用地图标记,原则是地图标记越大,高度越高。
#
# 最低的地面:0
# 墙:比下方的地面高1
# 竖直墙的顶部:比下方的墙高1
# 高地:与在高地下侧的竖直墙顶部高度相同。这样在高地上可以看到下面的东西
# 背后可通行的障碍物(如树木):可通行的部分与周围地面相同,底部+1
#
# 例如:
# 样例中的地面(浅绿草地):0
# 石头墙:1
# 石头墙顶部:2
# 高地(绿草地):2
# 高地上的树:树根3 树冠2
# 山顶的草:3
#======================================
#$Call_fillshadow = Win32API.new("bmp", "fill_shadow", 'lllli', 'i')
$width = 640 if $width == nil
$height = 480 if $height == nil

class Sprite_Vision < Sprite
# 配置
# 脚本开关
SWITCH = 30
# 渐变速率
STEP = 0.1
# 平滑开关
SMOOTH = true
# 可变配置
def init_param
    # directional lighting
    # [前 侧 后]方向的最大亮度
    @lighting=[255, 160, 64]
    @directional = [[0, 1, 1, 2],
                    [1, 0, 2, 1],
                    [1, 2, 0, 1],
                    [2, 1, 1, 0]]
    # 由近到远的亮度变化率。1表示不改变(可以看到前方无限远)
    @rate = 1.0
    # 阈值:小于此亮度的格子不可见
    @threshold = 0
    # 视野阴影的z值。如果遮挡了窗口需要调整这个。
    self.z=10
end
# 直接修改init_param中的参数的方法。每秒至多调用一次因为有refresh
def set_param(param, value)
  self.instance_variable_set(param, value)
  refresh
end
# 经验公式。设置视野范围 0..1 为近..远 每秒至多调用一次因为有refresh
def set_range(param)
  @threshold = (64 * (1.0 - param)).to_i
  @rate = 0.4 + 0.6 * param
  refresh
end

    def initialize(viewport, tilemap)
        super(viewport)
        @tilemap = tilemap
        init_param
        init_cache

        self.bitmap = Bitmap.new(@tilemap.map_data.xsize * 32,
                                 @tilemap.map_data.ysize * 32)
        self.bitmap.fill_rect(self.bitmap.rect, @gray[255])
        # 内部亮度矩阵,存放各个角色看到的亮度的最大值
        @brightness = Table.new(@tilemap.map_data.xsize, @tilemap.map_data.ysize)
        # 显示亮度矩阵
        @disp = Table.new(@tilemap.map_data.xsize, @tilemap.map_data.ysize)
        # 玩家位置
        @cx = -1; @cy = -1
        # 跟随的玩家数量初始化为1
        init_br(1)
        # 设置绘制方法
        @update_func = SMOOTH ? self.method(:smooth_update) : 
                                self.method(:rough_update)
        # 刷新列表初始化
        @xlist=[0]*1000
        @ylist=[0]*1000
        @blist=[0]*1000  # 亮度
        @tlist=[1]*1000  # 是否要刷新。正数为需要
        @list_size = 0

        refresh
    end

    def dispose
        self.bitmap.dispose if self.bitmap != nil
        super
    end

    def init_cache
        srand; @gray = []
        # 颜色对象        
        (0..255).each{|x|@gray.push(Color.new(46, 29, 27, x))}
        # 测试用颜色对象
        # (0..255).each{|a| @gray.push(Color.new(rand(255), rand(255), rand(255), 64))}

        # 地图标记
        @tags = Table.new(@tilemap.map_data.xsize, @tilemap.map_data.ysize)
        for x in 0...@tags.xsize
            for y in 0...@tags.ysize
                @tags[x, y]=$game_map.terrain_tag(x, y)
            end
        end
    end

    def init_br(n)
        # multiple lighting
        # 初始化每个角色的亮度矩阵
        @br=Table.new(@tilemap.map_data.xsize, @tilemap.map_data.ysize, n)
        @nc = n
        @current = 0
    end

    # 取某方向的最大亮度
    def base_brightness(direction)
        return @lighting[@directional[$game_player.direction/2-1][direction/2-1]]
    end
    # 计算刷新矩形
    def count_refresh_rect
        @min_x = $game_map.display_x / 128
        @min_y = $game_map.display_y / 128
        @max_x = @min_x + $width / 32
        @max_y = @min_y + $height / 32
        # 调整
        case $game_player.direction
        when 2
            @max_y += 3
        when 4
            @min_x -= 3
        when 6
            @max_x += 3
        when 8
            @min_y -= 3
        end
        # 剪裁,规范化
        @min_x = [@min_x, 0].max.to_i
        @min_y = [@min_y, 0].max.to_i
        @max_x = [@max_x, @tilemap.map_data.xsize].min.to_i
        @max_y = [@max_y, @tilemap.map_data.ysize].min.to_i
    end

    def set_brightness(x, y)
        # 设置内部亮度值
        m=0
        (0...@nc).each{|z|
            m=[m, @br[x, y, z]].max
        }
        @brightness[x, y]=m
        # 更新刷新列表
        if m != @disp[x, y]
            @xlist[@list_size] = x
            @ylist[@list_size] = y
            @list_size += 1
        end
    end

    def refresh
        @cx = $game_player.x
        @cy = $game_player.y
        @list_size = 0

        count_refresh_rect
        @br[@cx, @cy, @current] = 255
        set_brightness(@cx, @cy)
        @ctag = @tags[@cx, @cy]
        # 四方向预处理
        b=base_brightness(2)
        (@cy+1...@max_y).to_a.each{|y|
            @br[@cx, y, @current] = b
            set_brightness(@cx, y)
            b *= @rate
            break if @tags[@cx, y] > @ctag
        }

        b=base_brightness(4)
        (@min_x...@cx).to_a.reverse_each{|x|
            @br[x, @cy, @current] = b
            set_brightness(x, @cy)
            b *= @rate
            break if @tags[x, @cy] > @ctag
        }

        b=base_brightness(6)
        (@cx+1...@max_x).to_a.each{|x|
            @br[x, @cy, @current] = b
            set_brightness(x, @cy)
            b *= @rate
            break if @tags[x, @cy] > @ctag
        }

        b=base_brightness(8)
        ref_tag = @ctag + 1
        (@min_y...@cy).to_a.reverse_each{|y|
            @br[@cx, y, @current] = b
            set_brightness(@cx, y)
            b *= @rate
            this_tag = @tags[@cx, y]
            break if this_tag > ref_tag
            # ref_tag = [this_tag, ref_tag].max
        }

        # 填充其他格子
        # up-right
        iterate_tiles(@cx+1...@max_x,
                      (@min_y...@cy).to_a.reverse,
                      1, -1) # rescue p @min_y, @cy
        # up-left
        iterate_tiles((@min_x...@cx).to_a.reverse,
                      (@min_y...@cy).to_a.reverse,
                      -1, -1)
        # down-left
        iterate_tiles((@min_x...@cx).to_a.reverse,
                      @cy+1...@max_y,
                      -1, 1)
        # down-right
        iterate_tiles(@cx+1...@max_x, 
                      @cy+1...@max_y,
                      1, 1)
        
        # 滚动
        @current = (@current + 1) % @nc
    end

    def iterate_tiles(xarray, yarray, dx, dy)
      ref_tag = @ctag
      for y in yarray
        for x in xarray
          tag1 = @tags[x - dx, y]
          tag2 = @tags[x, y - dy]
          
          # 墙判定
          br1 = tag1 > @ctag ? 0 : @br[x-dx, y, @current]
          br2 = tag2 > ref_tag ? 0 : @br[x, y-dy, @current]
  
          ref_tag = tag2 if dy==-1 and ref_tag < tag2
          
          # 计算亮度
          tmp = (br1 + br2) / 2
          @br[x, y, @current] = (tmp>@threshold?tmp:0)
          set_brightness(x, y)
        end
      end
    end
    
    def update_bitmap(step=STEP)
        new_size=0
        for i in 0...@list_size
            x = @xlist[i]; y = @ylist[i]
            diff = @brightness[x, y] - @disp[x, y]
            if diff!=0
              @disp[x, y] += [diff.abs*step, 1].max.to_i * (diff<=>0)
              @xlist[new_size]=x; @ylist[new_size]=y;
              @blist[new_size]=@disp[x, y]
              self.bitmap.fill_rect(x*32, y*32, 32, 32, @gray[255-@blist[new_size]])
              #self.bitmap.draw_text(x*32, y*32, 32, 32, @tags[x, y].to_s, 1)
              new_size += 1
            end
        end
        
        @list_size = new_size
        # puts new_size
        # 调用dll
        #$Call_fillshadow.call(self.bitmap.object_id,
        #            @xlist.object_id, @ylist.object_id, @blist.object_id, 
        #            @list_size)
    end

    def smooth_update
        # 移动时刷新视野数据
        refresh if $game_player.x != @cx or $game_player.y != @cy
        # 每一帧平滑刷新视野图形
        update_bitmap
    end
    def rough_update
        # 移动时刷新视野数据
        if $game_player.x != @cx or $game_player.y != @cy
          refresh 
          update_bitmap
        end
    end    
    def update
      @update_func.call
      super
    end
end

# 接入部分
class Spriteset_Map
  attr_accessor :vision_sprite
  
  def update_vision
    if @vision_sprite==nil
      @vision_sprite=Sprite_Vision.new(@viewport1, @tilemap)
      @vision_sprite.init_br($game_party.actors.size)
      @vision_sprite.opacity=0
    end
    if $game_switches[Sprite_Vision::SWITCH]
      @vision_sprite.visible=true
      @vision_sprite.opacity+=10 if @vision_sprite.opacity < 255
      @vision_sprite.ox=$game_map.display_x / 4
      @vision_sprite.oy=$game_map.display_y / 4
      @vision_sprite.update
    else
      if @vision_sprite!=nil
        @vision_sprite.opacity-=10
        @vision_sprite.visible=false if @vision_sprite.opacity<30
      end
    end
  end
  
  alias viktor_vision_update update
  def update
    update_vision
    viktor_vision_update
  end
  
  alias viktor_vision_dispose dispose
  def dispose
    @vision_sprite.dispose if @vision_sprite!=nil
    viktor_vision_dispose
  end
end

class Scene_Map
  attr_accessor :spriteset
end

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值