查看: 1109|回复: 1

[原创发布] XP事件行走图描边脚本260402

[复制链接]
梦石
0
星屑
564
在线时间
70 小时
注册时间
2025-5-4
回帖
1
发表于 2026-3-22 06:47:09 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 HFDange 于 2026-4-2 21:09 编辑

效果图:
QQ截图20260321184745.png

2026.4.2更新:根据回帖建议优化了脚本,并增加了渐变描边功能。

[pre lang="ruby"]#==============================================================================
# ■ XP行走图描边脚本 - Character Stroker 2026.3.21 By HFDange
#------------------------------------------------------------------------------
#   使用方法:在需要描边的事件名中添加“.stX”后缀。
#             X为标记描边颜色的数字,如“.st0”为灰色描边、“.st1”为红色描边。
#             若要更改描边颜色,在CSC_COLORSET中添加或修改。
#
#   2026.4.2更新:描边渐变
#
#   使用方法:事件名后缀“.stXgX”
#             如“.st1g0”为红->灰->红渐变
#
#==============================================================================

#==============================================================================
# ● 设置部分
#==============================================================================

module CharSt_Config
  # 是否八方向描边(默认FALSE)
  CSC_IF_8_DIRECTIONS = FALSE
  # 描边颜色表(RGBA),默认颜色为:灰 红 绿 蓝 黄 紫 青 白 黑
  CSC_COLORSET = [
    [64, 56, 56, 255], [255, 18, 18, 255], [18, 255, 18, 255],
    [18, 18, 255, 255], [255, 255, 18, 255], [255, 18, 255, 255],
    [18, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]
  ]
  # 渐变周期长度 - 以帧为单位(默认20)
  CSC_GRADIENT_CYCLE = 20
end

#==============================================================================
# ● 描边脚本
#==============================================================================

module CharactorStroker
  include CharSt_Config
  
  def self.need?(name) # 从事件名判断是否需要描边
    return false if CharactorStroker.type(name) == nil
    return name.include?(".st")
  end
  
  def self.needgd?(name) # 判断描边是否需要渐变
    reg = name.match(/\.st\d+g/)
    return reg != nil
  end
  
  def self.type(name) # 判断描边颜色
    type = name.match(/(?<=\.st)\d+/)
    return 0 unless type
    return type[0].to_i
  end
  
  def self.gdtype(name) # 判断描边渐变颜色
    type = name.match(/\.st\d+g(\d+)/i)
    return type[1].to_i
  end
  
  def self.gdrate # 计算渐变比率 (0~1)
    frame = Graphics.frame_count
    prirate = frame % (CSC_GRADIENT_CYCLE * 2)
    rate = prirate.to_f / CSC_GRADIENT_CYCLE if prirate < CSC_GRADIENT_CYCLE
    rate = (CSC_GRADIENT_CYCLE * 2 - prirate.to_f) / CSC_GRADIENT_CYCLE if prirate >= CSC_GRADIENT_CYCLE
    return rate
  end
  
  def self.gdcolor(name) # 计算渐变颜色
    col = CSC_COLORSET[CharactorStroker.type(name)]
    colgd = CSC_COLORSET[CharactorStroker.gdtype(name)]
    rate = CharactorStroker.gdrate
    r = (col[0] + (colgd[0] - col[0]) * rate).to_i
    g = (col[1] + (colgd[1] - col[1]) * rate).to_i
    b = (col[2] + (colgd[2] - col[2]) * rate).to_i
    a = (col[3] + (colgd[3] - col[3]) * rate).to_i
    return [r, g, b, a]
  end
  
  def self.sync(event, lists, states, viewport) # 事件描边同步行走图
    id = event.id
    lists[id].x = event.screen_x - 1
    lists[id].y = event.screen_y - 1
    lists[id].z = event.screen_z - 2
    name = event.event.name
    if CharactorStroker.needgd?(name)
      gd = 1
    else
      gd = 0
    end
    new_state = [event.character_name, name, event.direction, event.pattern, gd]
    if states[id] != new_state
      if CharactorStroker.need?(name)
        col = CharactorStroker.type(name)
        CharactorStroker.draw(lists, states, col, id, viewport)
      else
        lists[id].bitmap.dispose
        lists[id].dispose
        lists.delete(id)
        states.delete(id)
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 绘制描边精灵图
  #--------------------------------------------------------------------------
  def self.draw(lists, states, col, id, viewport)
    lists[id] = Sprite.new(viewport) if !lists[id] || lists[id].disposed?
    lists[id].color.set(CSC_COLORSET[col][0],CSC_COLORSET[col][1],CSC_COLORSET[col][2],CSC_COLORSET[col][3])
    tevent = $game_map.events[id]
    if CharactorStroker.needgd?(tevent.event.name)
      gd = 1
    else
      gd = 0
    end
    states[id] = [tevent.character_name, tevent.event.name, tevent.direction, tevent.pattern, gd]
    t1 = RPG::Cache.character(tevent.character_name, tevent.character_hue)
    rw = t1.width / 4
    rh = t1.height / 4
    lists[id].ox = rw / 2
    lists[id].oy = rh
    if lists[id].bitmap != nil && !lists[id].bitmap.disposed?
      lists[id].bitmap.dispose
    end
    lists[id].bitmap = Bitmap.new(rw+2, rh+2)
    rx = tevent.pattern * rw
    case tevent.direction
    when 2
      dir = 0
    when 4
      dir = 1
    when 6
      dir = 2
    when 8
      dir = 3
    end
    ry = dir * rh
    evcrect = Rect.new(rx, ry, rw, rh)
    t2 = lists[id].bitmap
    t2.blt(0, 1, t1, evcrect)
    t2.blt(2, 1, t1, evcrect)
    t2.blt(1, 0, t1, evcrect)
    t2.blt(1, 2, t1, evcrect)
    if CSC_IF_8_DIRECTIONS
      t2.blt(2, 2, t1, evcrect)
      t2.blt(0, 0, t1, evcrect)
      t2.blt(2, 0, t1, evcrect)
      t2.blt(0, 2, t1, evcrect)
    end
  end
  
end

#==============================================================================
# ● 公开event变量
#==============================================================================

class Game_Event < Game_Character
  
  attr_reader :event
  
end

#==============================================================================
# ● 修改Spriteset_Map初始化、释放、刷新函数
#==============================================================================

class Spriteset_Map
  
  alias cst_old_initialize initialize
  alias cst_old_dispose dispose
  alias cst_old_update update
  
  def initialize
    cst_old_initialize
    @cst_lists = {}
    @cst_states = {}
    cst_refresh
  end
  
  def dispose
    cst_old_dispose
    @cst_lists.each_value{ |cstcheck|
      next unless cstcheck.respond_to?(:dispose)
      if cstcheck.bitmap && !cstcheck.bitmap.disposed?
        cstcheck.bitmap.dispose
      end
      cstcheck.dispose unless cstcheck.disposed?
    }
    @cst_lists.clear
    @cst_states.clear
  end
  
  def update
    cst_old_update
    if @cst_lists
      @cst_lists.each_key{ |cstid|
        event = $game_map.events[cstid]
        CharactorStroker.sync(event, @cst_lists, @cst_states, @viewport1)
        if @cst_states[cstid][4] == 1
          gdcol = CharactorStroker.gdcolor(event.event.name)
          @cst_lists[cstid].color.set(gdcol[0], gdcol[1], gdcol[2], gdcol[3])
        end
      }
    end
  end
  
  def cst_refresh
    @cst_lists.each_value{ |cstcheck|
      next unless cstcheck.respond_to?(:dispose)
      if cstcheck.bitmap && !cstcheck.bitmap.disposed?
        cstcheck.bitmap.dispose
      end
      cstcheck.dispose unless cstcheck.disposed?
    }
    @cst_lists.clear
    @cst_states.clear
    $game_map.events.each_value{ |value|
      event = value.event
      name = event.name
      if CharactorStroker.need?(name) && value.character_name != ""
        col = CharactorStroker.type(name)
        CharactorStroker.draw(@cst_lists, @cst_states, col, event.id, @viewport1)
      end
    }
  end
   
end[/pre]

范例工程:
描边脚本260402.zip (207.13 KB, 下载次数: 3)
梦石
0
星屑
683
在线时间
5 小时
注册时间
2017-1-20
回帖
5
发表于 2026-3-24 14:20:19 | 显示全部楼层
在 CharactorStroker.draw 方法中存在一个致命Bug:Bitmap内存泄漏。

在draw方法中,有这样一行代码:
lists[id].bitmap = Bitmap.new(rw+2, rh+2)
当事件行走或转身时,状态改变,sync会调用draw。每次调用draw,都会Bitmap.new生成一张新的位图赋值给Sprite。
但你没有释放旧的Bitmap。

建议将原本的这段代码:
[pre lang="ruby"]lists[id].ox = rw / 2
    lists[id].oy = rh
    lists[id].bitmap = Bitmap.new(rw+2, rh+2)
    rx = tevent.pattern * rw[/pre]

修改为:
[pre lang="ruby"]lists[id].ox = rw / 2
    lists[id].oy = rh
    if lists[id].bitmap != nil && !lists[id].bitmap.disposed?
      lists[id].bitmap.dispose
    end
    lists[id].bitmap = Bitmap.new(rw+2, rh+2)
    rx = tevent.pattern * rw[/pre]
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部