Project1

标题: 怎么做环条式血条 [打印本页]

作者: 阿尔卑斯    时间: 2021-3-10 17:44
标题: 怎么做环条式血条
如图。本意是半环,hp和mp。站上搜索环式没有,有见过这样的血条脚本吗,或提供下思路

作者: 真·可乐    时间: 2021-3-10 17:51
自己在自己的坑里实现过类似的,但是又改回长条了。和长条血条实现的方式差不多
作者: enghao_lim    时间: 2021-3-10 18:24
老实说,这种血条在rmxp不常看到主要两个原因。
1. 曲型血条真的不好画,需要对一些几何方程+rmxp bitmap熟悉才画得出,不像线性那么明了。
2. 用rmxp画几何图那性能本就惨不忍睹,不过后来有外挂DLL解决了这个问题,我以前帮天圣写图鉴脚本时就用到了该脚本,具体什么名称我真忘记了。

在以上两大限制的情况下,只能走一种古老可是不得不说能成的方法,那就是准备左右各101张血条,分别是0到100%,然后根据hp和mp的数值%显示对应的图。
作者: 阿尔卑斯    时间: 2021-3-10 18:45
真·可乐 发表于 2021-3-10 17:51
自己在自己的坑里实现过类似的,但是又改回长条了。和长条血条实现的方式差不多 ...

本身bitmap没有画圆形的方法啊。怎么差不多?能分享一下吗
作者: 阿尔卑斯    时间: 2021-3-10 18:49
enghao_lim 发表于 2021-3-10 18:24
老实说,这种血条在rmxp不常看到主要两个原因。
1. 曲型血条真的不好画,需要对一些几何方程+rmxp bitmap熟 ...

谢谢思路。虽然也不用图片的方法,但理解了方向。
作者: plain666    时间: 2021-3-10 19:17
我想到一个方法,就是用图片显示,假设把 0%~100% 分成11张图片(分的有点粗),当前 hp 和 mp 符合哪个比例就显示哪张图片,楼主你看这个方法行得通吗?
作者: RPGzh500223    时间: 2021-3-10 20:46
大概的思路

纯脚本绘制话,估计win32api,太难了;直接rgss的话,效率……

一张图片(如你给出的图)的话,看你是 简单的横截线 还是经过的圆心的线
横截线就如条形一样 算比例 直接blt

经过圆心的线的话,以角度表示血量多少
先算下圆环外员的交点,blt需要的矩形(自交点以下),这时会多出个很小的三角形的区域
clear_rect(就是填充透明色) 数学问题  清除掉多余的三角形区域

另类的方法
圆环旋转,只适用单色背景
首先大圆环 左边红 右边背景色 分成2张图片
小圆环 右边蓝 左边背景色 也是分成2张图片
红蓝blt
背景色的2张图,分别生成sprite
根据血量、蓝量旋转覆盖红环、蓝环



作者: SailCat    时间: 2021-3-11 01:59
本帖最后由 SailCat 于 2021-3-11 02:20 编辑

RMXP的bitmap处理效率不比RMVA差的,这个我很明确的测试过并得出结论
RMXP每帧纯做set_pixel能做8000次,你觉得你的血条里会有8000个像素点吗?就算有,能有16000个像素点吗?血不会每帧更新,刷血条时损失1帧又不是什么大不了的事。要知道不论RMXP还是RMVA,draw_text都是个奇慢的操作,你还经常一帧做几十次呢。

所以觉得RGSS直接做不了的,都是算法问题

有简易实现和完美实现两个办法
简易实现:特点是速度快,缺点是血条的水面是平的,不是正对圆心角的斜线。

首先制作2个空血条bitmap,作为底图传送成精灵,ox, oy设在圆心(也就是两个bitmap的左中点和右中点)(假设环直径100,底图都是50X100大小,例为血(50,50)和蓝(0,50)))
把它们的x, y设置在一起,显示出来就拼起来了
然后制作2个满血条bitmap,作为面图(z更高)传送成精灵,ox, oy,x,y也一起和前面的重合
然后根据你的血量蓝量百分比,改变两面图精灵的src_rect为高度的百分比,从底端算起,比如22%血,就传送(0,78,50,22)为血,40%蓝,就传送(0,60,50,40)为蓝
完毕了。

完美实现,底图一样,可简单用精灵做
面图的bitmap需要自己绘制:
血条直径100的话,需要执行至多100次bitmap#fill_rect(由于水平做比垂直做快,建议水平做)
以下改的是Window_Menustatus
总之不算难,效率也不差,水平做fill_rect一帧极限我记得是720次。垂直120次
这里有些复杂的三角函数运算,实际没那么多,反正在菜单中帧数大概36-37左右(我能说是被draw_text拖的么)

RUBY 代码复制
  1. #--------------------------------------------------------------------------
  2.   # ● 刷新
  3.   #--------------------------------------------------------------------------
  4.   def refresh
  5.     self.contents.clear
  6.     @item_max = $game_party.actors.size
  7.     for i in 0...$game_party.actors.size
  8.       x = 64
  9.       y = i * 116
  10.       actor = $game_party.actors[i]
  11.       draw_actor_graphic(actor, x - 40, y + 80)
  12.       draw_actor_name(actor, x, y)
  13.       draw_actor_class(actor, x + 144, y)
  14.       draw_actor_level(actor, x, y + 32)
  15.       draw_actor_state(actor, x + 90, y + 32)
  16.       draw_actor_exp(actor, x, y + 64)
  17.       draw_actor_hp(actor, x + 236, y + 32)
  18.       draw_actor_sp(actor, x + 236, y + 64)
  19.       outer = 50 # 外径
  20.       inner = 40 # 内径
  21.       ox = 300 # 血条ox
  22.       oy = i * 116 + 50 # 血条oy
  23.       hp_rate = actor.hp * 100.0 / actor.maxhp # HP 比例
  24.       sp_rate = actor.sp * 100.0 / actor.maxsp # SP 比例
  25.       hx_rate = hp_rate == 50 ? 0.0 :
  26.         Math.tan((0.5 + hp_rate * 0.01) * Math::PI)
  27.       sx_rate = sp_rate == 50 ? 0.0 :
  28.         Math.tan((0.5 - sp_rate * 0.01) * Math::PI)
  29.       hp_color = Color.new(232,90,90,255)
  30.       sp_color = Color.new(90,90,232,255)
  31.       outer.downto(-outer) do |by|
  32.         fy = by.to_f
  33.         x_in = (fy.abs >= inner ? 0 : inner * Math.sqrt([1 - (fy / inner) ** 2,
  34.           0].max)+0.5).to_i
  35.         x_out = (outer * Math.sqrt([1 - (fy / outer) ** 2, 0].max)+0.5).to_i
  36.         if by == 0
  37.           if hp_rate >= 50
  38.             contents.fill_rect(ox - x_out, oy, x_out - x_in, 1, hp_color)
  39.           end
  40.           if sp_rate >= 50
  41.             contents.fill_rect(ox + x_in, oy, x_out - x_in, 1, sp_color)
  42.           end
  43.           next
  44.         end
  45.         hx = hx_rate == 0.0 ? -999 * by : (fy / hx_rate).to_i
  46.         sx = sx_rate == 0.0 ? 999 * by : (fy / sx_rate).to_i
  47.         if hx.between?(-x_out, -x_in)
  48.           if hp_rate >= 50
  49.             contents.fill_rect(ox - x_out, oy + by, hx + x_out, 1,  hp_color)
  50.           else
  51.             contents.fill_rect(ox + hx, oy + by, -x_in - hx, 1, hp_color)
  52.           end
  53.         elsif (hx > -x_in) ^ (hp_rate <= 50)
  54.           contents.fill_rect(ox - x_out, oy + by, x_out - x_in, 1, hp_color)
  55.         end
  56.         if sx.between?(x_in, x_out)
  57.           if sp_rate >= 50
  58.             contents.fill_rect(ox + sx, oy + by, x_out - sx, 1,  sp_color)
  59.           else
  60.             contents.fill_rect(ox + x_in, oy + by, sx - x_in, 1, sp_color)
  61.           end
  62.         elsif (sx < x_in) ^ (sp_rate <= 50)
  63.           contents.fill_rect(ox + x_in, oy + by, x_out - x_in, 1, sp_color)
  64.         end
  65.       end
  66.     end
  67.   end



多说两句,如果真嫌慢(实际上是伪命题,卡的话查其他地方),做0% 50% 100%三个基础环图,然后根据hp的比例在上面加加减减的fill_rect打补丁,因为补丁最多需要工作1/4的区间(平均期望1/8),故此能再快1/2左右,而且我现在这个算法是HP越满绘制量越高,正常玩家没有谁会放着黄血一直长时间不管的吧……但是实话说其实没啥继续优化的必要

Project7 - 40 FPS 2021_3_11 1_58_51.png (259.21 KB, 下载次数: 28)

Project7 - 40 FPS 2021_3_11 1_58_51.png

作者: guoxiaomi    时间: 2021-3-11 15:08
sprite可以旋转,然后viewport不会绘制sprite超出的部分,那是不是可以做个半圆形的素材,旋转一下,超出的部分会被viewport干掉。
作者: ppspssss    时间: 2021-3-11 15:51
这么麻烦要是我就直接弄垂直血条
作者: RPGzh500223    时间: 2021-3-11 17:41
脚本绘制的图……还是用其他软件画出来的好看
横截线无论blt还是sprite.src_rect 动态环形血条 估计都不算卡

脚本是纯脚本绘制环形血条(感谢8楼,没试真不知道,真的不卡)
横截线就是简单 注释的那部分就实现了
  1. class Window_BattleStates < Window_Base
  2.   #--------------------------------------------------------------------------
  3.   # ● 刷新
  4.   #--------------------------------------------------------------------------
  5.   def refresh
  6.     self.contents.clear
  7.     @item_max = $game_party.actors.size
  8.     for i in 0...$game_party.actors.size
  9.       actor = $game_party.actors[i]
  10.       actor_x = i * 160 + 4
  11.       draw_actor_name(actor, actor_x, 0)
  12.       draw_actor_hp(actor, actor_x, 32, 120)
  13.       draw_actor_sp(actor, actor_x, 64, 120)
  14.       
  15.       #参数
  16.       c1, c2 = Color.new(255, 0, 0), Color.new(0, 0, 255)
  17.       out_r, in_r = 50, 40
  18.       cx, cy = actor_x + 70, 64
  19.       #高度版
  20. =begin      
  21.       #hp
  22.       hpy = cy + out_r - 2 * out_r * actor.hp / actor.maxhp
  23.       hpy.upto(cy + out_r) {|y|
  24.         x1 = cx - Math.sqrt(out_r ** 2 - [(y - cy).abs, out_r].min ** 2)
  25.         x2 = cx - Math.sqrt(in_r ** 2 - [(y - cy).abs, in_r].min ** 2)
  26.         self.contents.fill_rect(x1.round, y, (x1 - x2).abs.round, 1, c1)
  27.       }  
  28.       #sp
  29.       spy = cy + out_r - 2 * out_r * actor.sp / actor.maxsp
  30.       spy.upto(cy + out_r) {|y|
  31.         x1 = cx + Math.sqrt(out_r ** 2 - [(y - cy).abs, out_r].min ** 2)
  32.         x2 = cx + Math.sqrt(in_r ** 2 - [(y - cy).abs, in_r].min ** 2)
  33.         w = (x1 - x2).abs.round
  34.         self.contents.fill_rect(x2.round, y, w, 1, c2)
  35.       }
  36. =end      
  37.       #角度版
  38.       #hp
  39.       rad = Math::PI * (270 - actor.hp * 180.0 / actor.maxhp) / 180      
  40.       yy, line_x = nil,{}
  41.       in_r.upto(out_r) {|r|
  42.         x = (cx + r * Math.cos(rad)).round
  43.         y = (cy - r * Math.sin(rad)).round
  44.         if yy != y
  45.           line_x[y] = x
  46.           yy = y
  47.         end
  48.       }
  49.       out_y = (cy - out_r * Math.sin(rad)).round
  50.       in_y = (cy - in_r * Math.sin(rad)).round
  51.       [out_y, in_y].min.upto(cy + out_r) {|y|
  52.         x1 = cx - Math.sqrt(out_r ** 2 - [(y - cy).abs, out_r].min ** 2)
  53.         x2 = cx - Math.sqrt(in_r ** 2 - [(y - cy).abs, in_r].min ** 2)
  54.         if (lx = line_x[y]) != nil
  55.           w = out_y <= in_y ? (x1 - lx).abs : (lx - x2).abs
  56.         else
  57.           w = (x1 - x2).abs
  58.         end  
  59.         sx = out_y <= in_y ? x1.round : x2.round - w  
  60.         self.contents.fill_rect(sx, y, w, 1, c1)
  61.       }
  62.       #sp
  63.       rad = Math::PI * (270 + actor.sp * 180.0 / actor.maxsp) / 180      
  64.       yy, line_x = nil,{}
  65.       in_r.upto(out_r) {|r|
  66.         x = (cx + r * Math.cos(rad)).round
  67.         y = (cy - r * Math.sin(rad)).round
  68.         if yy != y
  69.           line_x[y] = x
  70.           yy = y
  71.         end
  72.       }
  73.       out_y = (cy - out_r * Math.sin(rad)).round
  74.       in_y = (cy - in_r * Math.sin(rad)).round
  75.       [out_y, in_y].min.upto(cy + out_r) {|y|
  76.         x1 = cx + Math.sqrt(out_r ** 2 - [(y - cy).abs, out_r].min ** 2)
  77.         x2 = cx + Math.sqrt(in_r ** 2 - [(y - cy).abs, in_r].min ** 2)
  78.         if (lx = line_x[y]) != nil
  79.           w = out_y <= in_y ? (x1 - lx).abs : (lx - x2).abs
  80.         else
  81.           w = (x1 - x2).abs
  82.         end  
  83.         sx = out_y <= in_y ? x1.round - w : x2
  84.         self.contents.fill_rect(sx, y, w, 1, c2)
  85.       }

  86.       
  87.       if @level_up_flags[i]
  88.         self.contents.font.color = normal_color
  89.         self.contents.draw_text(actor_x, 96, 120, 32, "LEVEL UP!")
  90.       else
  91.         draw_actor_state(actor, actor_x, 96)
  92.       end
  93.     end
  94.   end
  95. end
复制代码

作者: RPGzh500223    时间: 2021-3-12 08:33
重新换种算法,写起来很简单,总的效率差不多,但牺牲了些精度,不太适合渐变色
11楼的方法,逐行描绘,颜色数值与行数建立关系,上下渐变色不要太简单……具体效率没测试不知有多大的影响
  1. class Window_BattleStatus < Window_Base
  2.   #--------------------------------------------------------------------------
  3.   # ● 刷新
  4.   #--------------------------------------------------------------------------
  5.   def refresh
  6.     self.contents.clear
  7.     @item_max = $game_party.actors.size
  8.     for i in 0...$game_party.actors.size
  9.       actor = $game_party.actors[i]
  10.       actor_x = i * 160 + 4
  11.       draw_actor_name(actor, actor_x, 0)
  12.       draw_actor_hp(actor, actor_x, 32, 120)
  13.       draw_actor_sp(actor, actor_x, 64, 120)
  14.       
  15.       #参数
  16.       c1, c2 = Color.new(255, 0, 0), Color.new(0, 0, 255)
  17.       out_r, in_r = 50, 40
  18.       cx, cy = actor_x + 70, 64
  19.       #角度版
  20.       xx, yy = nil, nil
  21.       a1 = (270 - actor.hp * 180.0 / actor.maxhp).to_i
  22.       a2 = (270 + actor.sp * 180.0 / actor.maxsp).to_i
  23.       aa, rr = 1, 3 #2个精度
  24.       a1.step(a2, aa) {|a|
  25.         rad = Math::PI * a / 180
  26.         color = a > 270 ? c2 : c1
  27.         in_r.step(out_r, rr) {|r|
  28.           x = (cx + r * Math.cos(rad)).ceil
  29.           y = (cy - r * Math.sin(rad)).ceil
  30.           next if xx == x && yy == y
  31.           self.contents.fill_rect(x, y, rr, rr, color)
  32.           xx, yy = x, y
  33.         }
  34.       }
  35.          
  36.       if @level_up_flags[i]
  37.         self.contents.font.color = normal_color
  38.         self.contents.draw_text(actor_x, 96, 120, 32, "LEVEL UP!")
  39.       else
  40.         draw_actor_state(actor, actor_x, 96)
  41.       end
  42.     end
  43.   end
  44. end  
复制代码


作者: 阿尔卑斯    时间: 2021-3-15 20:38
RPGzh500223 发表于 2021-3-12 08:33
重新换种算法,写起来很简单,总的效率差不多,但牺牲了些精度,不太适合渐变色
11楼的方法,逐行描绘,颜 ...

非常精彩。这就是乐趣啊!!!




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