Project1

标题: [vx]鼠标系统 1楼无DLLl,2楼依赖DLL(2010-09-23更新) [打印本页]

作者: 沉影不器    时间: 2010-8-8 00:55
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-8-8 01:04
提示: 作者被禁止或删除 内容自动屏蔽
作者: wangswz    时间: 2010-8-8 01:27
签名看来那4个高级装备脚本兼容啦?
作者: summer92    时间: 2010-8-8 13:28
一直觉得很卡,家用机向吧,,,网游向???
作者: zh99998    时间: 2010-8-8 13:34
看一下我在发布区发的改过的鼠标脚本,修正了几处判定
另外我也做过兼容F2,原理就是那个不反复读取句柄,你然你这个有了我就不发布了
作者: 沉影不器    时间: 2010-8-8 21:35
提示: 作者被禁止或删除 内容自动屏蔽
作者: 貓拾弎    时间: 2010-8-9 01:20
只在默认脚本基础上添加鼠标脚本性能怎么样?如果完全兼容我想添加进去。
作者: 38571240    时间: 2010-8-10 23:36
用了这个脚本之后 门事件的行走图好像都不管用了
作者: 柳柳    时间: 2010-8-11 04:52
不参与技术讨论,只说一下实际使用zh9同学脚本遇到的问题
最麻烦的问题:click?、press?有时候有一定误差。比如用press将一个东西拽起来挪动的时候,有一定几率会没拽好掉在地上——不确定到底是什么原因,基本是随机出现;click则有一定几率(不低)会判断成类似于双击的效果等。我个人还是从逻辑上处理掉了,技术上我想应该值得改进。
另一个是如果game.exe和game.ini改名的话,鼠标失效。
第三个是如果窗口不是焦点的话,鼠标挪进来似乎也有点问题。运行过程中按F1出来帮助也是有一些问题。
作者: 紫苏    时间: 2010-8-11 14:49
本帖最后由 紫苏 于 2010-8-11 15:02 编辑

有几个问题:

不应该依赖 GetAsyncKeyState 的最低位来实现 click,因为最低位有可能被其它程序先行获取,等到 RM 程序再调用 GetAsncKeyState 的时候由于另一个程序先调用过了,虽然按键状态依然是按下,但最低位却已经变成了 0
具体原因让 MSDN 来解释(MSDN 的规律是 Remarks 内容最重要):http://msdn.microsoft.com/en-us/library/ms646293(VS.85).aspx
解决方法:用变量记录按键按下状态,click 里判断变量,如果为 true 则 click? 返回 false,如果为 false 则把变量设置为 true,返回 true;刷新时判断,如果变量为 true 且 GetKeyState 返回的状态是没有按键已弹起,则设变量为 false

实际上这个脚本中的 click? 不是我们通常所理解的鼠标单击事件,因为单击应该是按下跟一个弹开,而这里的 click? 实际上是和 Input.trigger? 实现了一样的功能,所以会有按下后立即返回 true 的现象

[line]1[/line]

GetAsncKeyState 检测的是中断层的按键状态,所以跳过了 OS 的逻辑按键映射,当这个映射并不是物理左键对应逻辑左键、物理右键对应逻辑右键时,就会发生一些很有喜感的现象,如用 OS 提供的接口让鼠标左右键反转后,用户认为按下了左键(当然实际按的是鼠标右键)但程序却依然认为按下了右键,用户认为按下了右键(当然实际按的是鼠标左键)但程序却依然认为按下了左键
解决方法:用 GetKeyState 判断逻辑按键

[line]1[/line]

press? 是用的 GetKeyState 来判断按键状态,但是它取了最低位,最低位是一个按键开关的状态,每次按键按下时状态会反转,要么就是对大写锁定等有开关状态的按键比较有用,要么就是用于实现多次按某个键轮流进行不同操作的功能
很明显在这里取出最低位不合适,press? 应该返回按键当前是否按下的状态,所以应该用 0x8000 取出最高位,否则会有按下一次弹起后 press? 也永远返回 true,或者没有按过 press? 就返回 true 的现象

[line]1[/line]

@down.push(1)   if (GetKeyState.call(1) & 0x1000) / 0x1000 == 1
这几行脚本很奇怪,为什么用的是 0x1000?用 0x1000 进行与运算取出的是倒数第四高位,而 MSDN 说按下的状态是在最高位
测试了一下,似乎当最高位为 1 时,高字节的所有位,包括低字节的最高位确实同时变成了 1,但似乎不应该以偏概全假设这个语句永远是真,很可能是平台有关的
另外这里可以不用除,也不用移位,直接判断返回值 != 0 即可

[line]1[/line]

刷新的实现比较诡异,用了数组来保存这次刷新按下的键,虽然数组的长度最大也只是个位数,但频繁分配内存、搜索、释放内存也是个不容忽视的问题,可以优化为使用变量记录按下状态(同第一个问题的解),刷新时仅仅是改变 true/false

[line]1[/line]

关于获取句柄,有上面某柳提到的问题,还有标题动态刷新时匹配标题失败的问题,以及同时开两个标题相同的游戏窗口时可能获取到不正确的句柄的问题,统一解决方法:http://rpg.blue/forum.php?mod=viewthread&tid=133018

[line]1[/line]

关于鼠标滚轮和 move? 的实现,理论上还是有些问题,因为即便在判断的那一刻确实有 WM_MOUSEHWHEEL 或 WM_MOUSEMOVE 消息,但它们并不一定处于消息队列的头部,特别是在硬件性能差或者资源紧凑的环境下,可能每次 PeekMessage 的时候都有数个消息处于上述两个消息的前端,暂时还没有被 RM 默认的消息循环移出队列并处理,这样直接效果就是滚动或移动了但没反应
另外 WM_MOUSEHWHEEL 的 wParam 还包含滚动距离的数据,可以在默认的菜单里做相应的相应的处理,目前无论滚动多远都只会让窗口光标移动 1

[line]1[/line]

关于鼠标的实现其实还有一种方法,个人感觉比这种用 GetCursorPos、GetKeyState 等函数的方法要简单,就是直接处理 WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP 等各种鼠标相关的消息
以前夏娜发过一个替换窗口过程的工程,在 DLL 里 SetWindowLong 替换 RM 的窗口过程,并用 rgss102j.dll 里的 RGSSEval 去回调 RM 脚本里的各种回调函数,方法和 RM 后台运行的一样,可以用来处理鼠标的消息
具体方法可以是:设计各种闭包,在不同的场景让一个全局的回调 Proc 保存这个闭包,然后让 DLL 里的窗口过程函数统一调用处理鼠标消息
作者: 54cn    时间: 2010-8-11 19:25
支持,新鼠标看起来不错。

==================分割线==================
坚决不做伸手党。
作者: 沉影不器    时间: 2010-8-14 10:21
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-8-14 12:46
本帖最后由 紫苏 于 2010-8-14 12:48 编辑

回复 沉影不器 的帖子
但作者给 press? 写的注释里确实说的是 down? 的功能 ><
#   Mouse.press?
#   This returns a true/false value  when you test  whether a button is pressed
#   and kept depressed.  The values you pass are 1 (for the left mouse button),
#   2 (for the right mouse button), or 3 (for the middle).
我觉着吧就是她/他没仔细看说明文档或者一时疏忽了,如果想做切换的功能可能把方法命名为 toggled? 更合适

zh 判断的 > 1,原意应该是想只判断 down?,不判断 toggled?,按常理来想这么做也确实可行,因为 GetKeyState 返回的是 16 位整数,而 Ruby 是 31 位整数,无论 GetKeyState 的返回值怎么变,直接拷贝数据到 31 位整数里的话,应该都不会影响 31 位整数的符号位,所以一旦 16 位整数的最高位被设为了 1,整个整数必然 > 1;但实际测试了下,GetKeyState 返回的数有时是负的,有时不是,是负的时候刚好是 16 位整数最高位为 1 时所表示的那个负数,说明这里有符号扩展,影响了 31 位整数的符号位,也应该是柳柳那个问题的来源。最不会有问题的办法自然还是与上 0x8000 再判断 != 0 了

在 Ruby 里 GetMessage 确实不好,因为 Graphics.update 也在同时处理消息,游戏一帧之间 Ruby 脚本可能只调用一次 GetMessage(一帧之间多次调用 GetMessage 的话,不知道什么时候该停止),而 Graphics.update 却不止,两帧之间的程序空闲时间足够调用成百上千次 GetMessage 了,当队列比较长时,可能大部分消息会被 Graphics.update 抢走
作者: 沉影不器    时间: 2010-8-14 20:51
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-8-15 09:14
回复 沉影不器 的帖子

>_< 说错了,我的意思是 Graphics.update 一帧一次,但期间这厮可能已经处理了成百上千个消息了
现在基本上可以确定 Graphics.update 是处理 RM 窗口消息的过程,由于 RM 需要锁帧,所以 Graphics.update 每帧会至多占用主线程 (1000/fps) 毫秒的 CPU 时间,特别是在配置高的机器上,假设游戏是 60 fps,最好的情况下游戏高层逻辑只需要 1 毫秒来完成,那 Graphics.update 每帧都有  (1000-1)/60,将近 17 毫秒的时间来处理各种消息
所以理论上来说 fps 越高,Graphics.update 得到的时间就越少,Ruby 抢占消息的赢面就更大……但这样做同时也增加了 CPU 负荷……|||

沉影闲下来了可以试试用夏娜的思路做一个,不必用到系统钩子,仅仅替换窗口过程~
作者: 幻の音    时间: 2010-8-15 14:05
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-8-15 21:14
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-8-15 21:30
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-8-15 21:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-8-15 21:33
提示: 作者被禁止或删除 内容自动屏蔽
作者: 38571240    时间: 2010-8-15 22:12
沉影前辈很感谢,如果能让它和【动态显示的宝箱】不冲突就更好了......
作者: 紫苏    时间: 2010-8-15 22:34
回复 沉影不器 的帖子

哦,没考虑到,那应该可以这样:click? 使用的变量 A 有三个状态,可以分别用 0、1、2 表示,0 表示弹起,1 表示上一次弹开后第一次按下,2 表示 0、1 之外的所有情况
在 update 中判断:
if 按键按下
    A = (0 == A ? 1 : 2)
else
    A = 0
end

而 click? 里返回 A == 1

没测试,不知道有没有没考虑到的场合……||
更新后用一个变量保存一种判断的实现很巧妙,赞一个,如果 click? 要用 3 个状态,那就需要 2 位,而不是现在的 1 位了
还可以牺牲一点可读性换取空间——把所有判断都放到一个变量里,31 位绝对够了
另外如果 update 里多次调用相同实参的 GetKeyState 的话,可以预先保存在临时变量里,避免了多次调用 API
作者: 沉影不器    时间: 2010-8-19 21:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: SOU    时间: 2010-8-19 21:23
首先非常感谢您制作的这个鼠标脚本
用起来感觉很不错,更新了版本之后也没有发现什么bug
但是实际操作上特别是在移动中的手感很不好
我想能不能提供一种拖动的移动方式,比如轩辕剑系列中的那种
现在的这种点击到达感觉很别扭
作者: 神思    时间: 2010-8-19 21:43
本帖最后由 神思 于 2010-8-19 21:48 编辑
2010-08-19 更新
① 统一使用GetKeyState捕捉所有鼠标键操作.
② rgss内部拦截消息基本上没有进展空间,所以 ...
沉影不器 发表于 2010-8-19 21:11



{:nm_7:}在RM内是抢不过他的。
中途插入吧。。。{:nm_4:}
作者: 幻の音    时间: 2010-8-20 18:29
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-8-22 11:55
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-8-22 12:11
本帖最后由 紫苏 于 2010-8-22 12:14 编辑

之前有人在 XP 区问滚轮的实现,就顺手写了一个,仅供参考:
http://rpg.blue/forum.php?mod=vi ... E8%BD%AE&page=2

另外我那个处理距离的方式不太好,在有些硬件条件(没有触点的滚轮)下可能滚一次不到 120,正确做法是累积 delta,量值超过一定值时作相应的处理
作者: a000b1745    时间: 2010-8-22 20:30
沉影前輩加油呢!
目前很少遊戲有強大的鼠標系統
(似乎只有日本的團隊VX遊戲才看的到)
雖然把前輩的腳本放進我的遊戲後會對
"畫面標題","戰鬥頭像顯示"的腳本衝突,
但是還是願意選擇機能性強大的鼠標系統,
畢竟在進行遊戲上方便太多了。

給個小小的想法、如果滾輪能代替方向鍵上下,
那麼在橫版SW戰鬥中就真的不需要用到鍵盤選怪了。
(真是一個懶惰的玩家啊~)
作者: 幻の音    时间: 2010-8-24 16:51
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-8-27 20:40
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-8-27 23:20
回复 沉影不器 的帖子

又查了一下,中文翻译似乎是“刻痕”(Notch),之前直接用字典查的,有点驴唇不对马嘴
刻痕就是大部分鼠标滚轮都有的一个能让滚轮位置固定在某个位置上的物体,使得每次滚轮都只会滚动到刻痕上,固定了滚动的距离。MSDN 上说了有些鼠标是没有刻痕的,即滚轮可以滚动任意的距离,不一定像之前那样是两次滚到刻痕上的距离的倍数,这时如果只是做 / 120 的话,大部分情况下都会直接返回 0,也就是滚动了很小的距离后没有反应,需要滚动超大于等于 120 的距离才有效果
那个链接里的脚本我后来更新了,使用的就是累积的 delta 值
作者: 沉影不器    时间: 2010-8-28 10:39
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-8-28 11:19
1、传递足够大小的 Ruby 字符串给 C++ 函数,在 C++ 层程序接收到的只是一个地址,然后会把它当做引用处理,返回到 Ruby 后把该 Ruby 字符串转换成可用的数据类型即可
例:
arg1 = [182364945].pack("i")
arg2 = [397574].pack("i")
intercept.call(arg1, arg2)
arg1 = arg1.unpack("i")
arg2 = arg2.unpack("i")

2、就用 GetKeyState 吧,毕竟 press? 的功能是状态的判断,和单击、双击、滚轮等突发事件不太一样
作者: 沉影不器    时间: 2010-8-28 11:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 神思    时间: 2010-8-28 13:57
关于f1的问题。WM_HELP 时显示出开 WM_PAINT 时隐藏即可!
作者: 沉影不器    时间: 2010-8-28 20:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-8-29 00:35
回复 沉影不器 的帖子

不错的设计模式,用消息作键,Proc 作值
作者: 54cn    时间: 2010-8-29 09:26
用不成……每个鼠标脚本都给我冲突…………其他脚本又很重要……
作者: 神思    时间: 2010-8-29 11:56
关于鼠标接入的实现,我有个新想法...
在Scene里放个散列,用来注册响应鼠标的事件和window和sprite...到upda ...
沉影不器 发表于 2010-8-28 20:31




奇怪,=。=我的鼠标就是这样响应的。
一直没什么问题啊。

作者: 沉影不器    时间: 2010-9-3 20:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 沉影不器    时间: 2010-9-3 23:37
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-9-3 23:53
推荐使用系统光标,毕竟精灵的效率怎么说也比不上直接的 bit blit
作者: 沉影不器    时间: 2010-9-3 23:56
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-9-4 01:12
回复 沉影不器 的帖子

确实比较诡异,平时在窗口模式下,鼠标超过一段时间没有移动就会自动隐藏,但经测试发现他并不是用的 ShowCursor 隐藏的,至今仍不知其解;全屏模式下应该是另一种情况
猜测是和 DirectDraw 处理全屏以及 DirectInput 处理鼠标输入的方式有关

另外沉影你太客气了,能否去掉那个“您”字?
作者: 沉影不器    时间: 2010-9-6 21:41
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-9-7 00:56
回复 沉影不器 的帖子

1、操作系统弹性处理的吧,资源不紧张的情况下会发送两个间隔很短的 120 消息,如果紧张可能就会合并为一个 240 的

2、应该是滚动一点就发送一个距离很小的消息,累积应该是用户代码进行的。120 只是自己定的一个界限,这个界限越小,滚动滚轮和实际效果的时间差就越小,也就是滚动效果越平滑。我记得 Mac 鼠标好像就没有刻痕,浏览网页的时候滚动十分平滑,但用在 RM 里移动光标的话,没有平滑可言,只有一次的滚动尺度,120 应该比较合理
作者: 沉影不器    时间: 2010-9-7 21:14
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-9-7 23:21
1.很可能是定时器在解析滚轮..2.不知道系统怎么处理120以下的消息?感觉没滚满120就没效果.
沉影不器 发表于 2010-9-7 21:14

1、应该是有一个定时器;
2、这个要找一个没有刻痕的鼠标测试才行,资源短缺中

作者: moy    时间: 2010-9-7 23:58
表示弹不出付费框,只是直接出一个页面提示需要付费后再下载....
作者: 柳柳    时间: 2010-9-11 05:50
唔,在《格斗纹章》中经过大量测试证明,本鼠标系统(dll的)有这么几个缺点:

1、在有一定几率情况下,无法随游戏启动。鼠标在窗口上没有反应,即使按F12也没用。感觉这种几率在2%-3%之间,我是用RMVX直接运行,不是点game.exe,大部分出现这种bug的情况时我同时在干N件事,比如启动游戏,在游戏还没起来的时候QQ和人说话去了。不知有没有影响。

2、滚轮很好用,非常好用,但是其他方面灵敏度比API版本的低,低多了。我个人推测是在click?上的判断比较严格。实际上是这样,原来API那个版本灵敏过度,click?经常会连续两帧判定成立,我当时处理方法是在scene中用逻辑判定两次click?成立之间间隔不小一定帧数;但是换了这个dll版本之后,出现的问题是有一些操作很快的情况下鼠标的点击相应无效,特别是在我用左键拖拽一个角色到某位置之后按右键点击切换技能的时候,经常无效。还有连击的时候,我总觉的N次连击时屏幕反应的次数和我点击的次数不一样。可能是左键按下的时候,无法判断右键是否click?了?

大概就是这么两个问题,都很严重,尤其是第二个,对于动作型要求高的游戏来说有点影响体验。
另外有一个可能不算bug的东西,可以干掉另外一个有问题的脚本中的bug……不提也罢。
期待更新的版本,呵呵

对了,另外闲扯一句。
我觉得真能用好这个鼠标的人,八成是不会使用默认的那些系统的吧……
作者: 夕阳武士    时间: 2010-9-11 05:59
LS是柳大,兴奋唉~话说鼠标什么的,我觉得我还用不上,而且拖累系统
作者: 柳柳    时间: 2010-9-11 06:13
哦哦,发帖过程中逐渐明白怎么回事了,测了一下果然如此:
应该是被脚本判断为“双击”的第二下点击,没有算到down?里面。
以前脚本是这样(手感差):
if Mouse.click?(2)

现在脚本是这样(手感正确):
if Mouse.click?(2) or Mouse.dbl_clk?(2)

那啥,能告诉我怎么关闭双击功能么?或者要我说,建议直接取消双击功能吧,我觉得好像没有哪个游戏里面用过双击吧……真要用的话,在scene里面写个小判断轻易就做到同样效果了。

真的建议取消
真建议取消
建议取消
取消
消……
作者: SOU    时间: 2010-9-11 08:14
柳大的回音第二行MS错了=w=
好吧我只是来吐槽的
作者: 沉影不器    时间: 2010-9-11 20:59
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-9-13 07:06
感觉所有消息都用状态判断怪怪的,突发性事件还是通过给每个场景分配一个 Proc 实现回调机制吧……(特指滚轮和 WM_*BUTTONUP)

柳柳的问题可能是这样:
每处理一个消息都会调用一次 DLL 里的窗口过程函数,所谓“每个”是指每次 SendMessage 或消息循环内部的 DispatchMessage 的调用。当 Graphics.update 调用时,Ruby 解释器的线程就把执行权让给了消息循环所在的线程,开始进行消息循环,而太快结束的鼠标按下和弹起过程,可能在 Graphics.update 返回之前就处理完了,而这时状态已经由 0 变为 1 又变回了 0,所以虽然点击了鼠标却没有响应,因为返回到 Ruby 层的时候状态已经是 0
作者: 沉影不器    时间: 2010-9-15 22:06
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-9-16 05:57
本帖最后由 紫苏 于 2010-9-16 05:59 编辑

回复 沉影不器 的帖子

http://msdn.microsoft.com/en-us/library/aa931259.aspx
(虽然是 CE 的,但应该和 NT 没区别,没测试……)

up 似乎是有的,只是第三步的 down 换成了dbclk,那在处理这个消息时也让 down 状态过渡到 1 就好了
作者: 沉影不器    时间: 2010-9-17 20:33
提示: 作者被禁止或删除 内容自动屏蔽
作者: 紫苏    时间: 2010-9-18 05:53
回复 沉影不器 的帖子

就是把 dbclk 处理为双击事件的同时也当做 down 处理,让 down 的状态变为 1

这篇文章应该是用于 .NET 的,不过确实和 CE 那篇描述的没有出入
作者: 柳柳    时间: 2010-9-19 03:33
唔,不是我说,你测过这个trigger么,乱响应啊...
作者: 森羅結界    时间: 2010-9-19 03:39
提示: 作者被禁止或删除 内容自动屏蔽
作者: zhong    时间: 2010-9-19 19:02
支持下,不厚道地收藏了,即将挖的坑里应该可以用到这些....简单的看了下你们的讨论,研究的挺深入,佩服...
作者: werhsqgl    时间: 2010-9-20 12:18
回复 沉影不器 的帖子
http://rpg.blue/thread-156705-1-1.html
大神,您要是有空的话,能不能帮我看看?还是不兼容的话,提醒我一声。拜谢。

   
作者: qiji19980124    时间: 2010-9-20 21:25
所谓的鼠标指的是用滑鼠操作?
作者: 沉影不器    时间: 2010-9-23 15:57
提示: 作者被禁止或删除 内容自动屏蔽
作者: mingaini    时间: 2010-10-13 16:10
提示: 作者被禁止或删除 内容自动屏蔽
作者: 九夜神尊    时间: 2010-10-16 22:46
表示下载下来测试不能!
作者: 一瞬间的幻觉    时间: 2010-10-18 20:09
不贵,我买一个~~~嘎哈哈
作者: 沉影不器    时间: 2010-10-21 20:02
提示: 作者被禁止或删除 内容自动屏蔽
作者: 訫﹎森    时间: 2010-10-25 03:06
支持一下,呵呵。
作者: 骑猪ol    时间: 2010-10-30 11:54
提示: 作者被禁止或删除 内容自动屏蔽
作者: koyonuji    时间: 2010-11-1 15:03
为什么我一直下下来的是PHP文件……
作者: 天朝坦克    时间: 2010-11-2 21:32
象征性的顶一下这种复杂的技术性劳动,10硬币真的可以无视...
看样子还有继续升级的余地呢..
作者: q849012557    时间: 2010-12-26 10:35
good太好了不用收费了
作者: chg1998    时间: 2011-2-15 17:52
顶啊
GOOD!GOOD!GOOD!
作者: 374152774    时间: 2011-4-3 12:29
支持一下,呵呵。

作者: 鸡腿少侠    时间: 2011-4-17 17:32
沉影不器大侠,小的用了您的脚本出了问题,希望大侠解决。
我以我在学校的故事做的游戏。刚进入开始画面点继续游戏时就有问题了:
Sinple Mouse System1.5的第351行发生了NoMethodError
undefined method‘[ ]’for nil:NilClass
能帮帮我吗?用了你的新截图存档就这样了
我真的需要这个脚本,求您帮帮我吧!
作者: yangff    时间: 2011-5-2 15:35
沉影不器 发表于 2010-8-8 00:55
我的vx鼠标历程

1.最早出现在贵论坛的vx鼠标,应该是"光的圆周率"引入的>>这个版本>vx新菜单样式这个帖子使 ...

BUg一枚……遇到Viewport就烧饼了……而且在update_mouse居然娶不到viewport#¥#¥
作者: via1314    时间: 2011-8-9 00:45
学习中`感谢楼主无私分享`
作者: ainlee    时间: 2011-10-22 16:56
靈敏度高很多的腳本!
但是我用於VX新視窗樣式取代原有的鼠標系統時會出現衝突~

怎麼解決呢?

作者: haoyu110    时间: 2011-11-5 19:44
有个问题,您的附件下载下来都是39K,是坏档! 麻烦看看!
作者: 370420939    时间: 2011-11-6 19:05
呃……和66站长的战棋脚本一起用,出错了Orz……
作者: 叶子    时间: 2012-1-25 01:00
@沉影不器@紫苏
依赖dll的那个版本,在鼠标移动时 Mouse.Down?() 有一定几率不能正确返回鼠标状态

测试方法:
打开依赖dll版本的范例工程,晃动鼠标的同时单击左键,点击计数器有一定几率不增加

对照组1:
鼠标静止时单击,点击计数器正常统计

对照组2:
不依赖dll的版本无此问题

备注
Mouse.up?()亦有同样问题
操作系统 Win 7 Ult 64bit

吐槽部分:
我就说做出来的菜单反应有点迟钝,经排查后发现这个现象,不知道是单独的机器问题还是鼠标dll的问题
作者: 虔_King    时间: 2012-3-9 19:10
感謝~來玩玩看~
作者: 食人族幽灵    时间: 2012-5-16 20:29
啥?收费?那啥,没我事了……{:2_274:}
作者: 溺水三期    时间: 2013-5-10 11:18
为什么我测试出现这种情况呢?鼠标每点一次,数字加1...本人小白...谢谢

ZRDPFHP8~GU`{~8D8NNJ0)9.jpg (28.1 KB, 下载次数: 20)

ZRDPFHP8~GU`{~8D8NNJ0)9.jpg

作者: LyMin    时间: 2013-6-28 19:31
和截图存档系统系统发生冲突:


脚本"Simple Mouse System 1.5"的第351行发生了NoMethodError
undefined method`[]' for nil:NilClass


截图存档系统:
  1. #==============================================================================
  2. # vx新截图存档 by 沉影不器
  3. #------------------------------------------------------------------------------
  4. # ☆ 核心部分是内存位图的输入输出
  5. #    (快速存储Bitmap的Marshal 作者: 柳之一) 强大啊
  6. #------------------------------------------------------------------------------
  7. # ▼ 相比rmxp正版截图存档(作者: 柳柳),主要变动如下:
  8. #     ① 省去了 screenshot.dll 文件 (因快速存储Bitmap的Marshal的存在)
  9. #     ② 无须人工新建存档文件夹,脚本将自建
  10. #     ③ 方便地更改最大存档数,只需设置最大值
  11. #==============================================================================
  12. MAX_SAVE_ID = 12                          # 最大存档数
  13. SAVE_DIR = "Saves/"                       # 改变的存档目录(不希望改变目录请删)
  14. #==============================================================================
  15. # (快速存储Bitmap的Marshal 作者: 柳之一) 强大啊
  16. #==============================================================================
  17. class Font
  18.   def marshal_dump
  19.   end
  20.   def marshal_load(obj)
  21.   end
  22. end

  23. class Bitmap
  24.   # 传送到内存的API函数
  25.   RtlMoveMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
  26.   RtlMoveMemory_ip = Win32API.new('kernel32', 'RtlMoveMemory', 'ipi', 'i')
  27.   def _dump(limit)
  28.     data = "rgba" * width * height
  29.     RtlMoveMemory_pi.call(data, address, data.length)
  30.     [width, height, Zlib::Deflate.deflate(data)].pack("LLa*") # 压缩
  31.   end
  32.   def self._load(str)
  33.     w, h, zdata = str.unpack("LLa*")
  34.     b = self.new(w, h)
  35.     RtlMoveMemory_ip.call(b.address, Zlib::Inflate.inflate(zdata), w * h * 4)
  36.     return b
  37.   end
  38. # [[[bitmap.object_id * 2 + 16] + 8] + 16] == 数据的开头
  39.   def address
  40.     buffer, ad = "rgba", object_id * 2 + 16
  41.     RtlMoveMemory_pi.call(buffer, ad, 4)
  42.     ad = buffer.unpack("L")[0] + 8
  43.     RtlMoveMemory_pi.call(buffer, ad, 4)
  44.     ad = buffer.unpack("L")[0] + 16
  45.     RtlMoveMemory_pi.call(buffer, ad, 4)
  46.     return buffer.unpack("L")[0]
  47.   end
  48. end

  49. #==============================================================================
  50. # ■ Game_Temp
  51. #==============================================================================
  52. class Game_Temp
  53.   #--------------------------------------------------------------------------
  54.   # ● 定义实例变量
  55.   #--------------------------------------------------------------------------
  56.   attr_accessor :save_bitmap        # 存档位图
  57.   #--------------------------------------------------------------------------
  58.   # ● 初始化对象
  59.   #--------------------------------------------------------------------------
  60.   alias ini initialize
  61.   def initialize
  62.     ini
  63.     @save_bitmap = Bitmap.new(1, 1)
  64.   end
  65. end

  66. #==============================================================================
  67. # ■ Window_SaveFile
  68. #==============================================================================
  69. class Window_SaveFile < Window_Base
  70.   #--------------------------------------------------------------------------
  71.   # ● 定义实例变量
  72.   #--------------------------------------------------------------------------
  73.   attr_reader   :filename                 # 文件名
  74.   attr_reader   :file_exist               # 文件存在标志
  75.   #--------------------------------------------------------------------------
  76.   # ● 初始化对象
  77.   #     file_index : 存档文件索引 (0~3)
  78.   #     filename   : 文件名
  79.   #--------------------------------------------------------------------------
  80.   def initialize(file_index, filename)
  81.     super(160, 56, 384, 360)
  82.     @file_index = file_index
  83.     @filename = filename
  84.     make_dir(SAVE_DIR) if SAVE_DIR != nil
  85.     load_gamedata
  86.     refresh(@file_index)
  87.   end
  88.   #--------------------------------------------------------------------------
  89.   # ● 读取部分游戏数据
  90.   #--------------------------------------------------------------------------
  91.   def load_gamedata
  92.     @file_exist = FileTest.exist?(SAVE_DIR + @filename)
  93.     if @file_exist
  94.       file = File.open(SAVE_DIR + @filename, "r")
  95.       begin
  96.         @characters          = Marshal.load(file)
  97.         @frame_count         = Marshal.load(file)
  98.         @last_bgm            = Marshal.load(file)
  99.         @last_bgs            = Marshal.load(file)
  100.         @game_system         = Marshal.load(file)
  101.         @game_message        = Marshal.load(file)
  102.         @game_switches       = Marshal.load(file)
  103.         @game_variables      = Marshal.load(file)
  104.         @game_self_switches  = Marshal.load(file)
  105.         @game_actors         = Marshal.load(file)
  106.         @game_party          = Marshal.load(file)
  107.         @game_troop          = Marshal.load(file)
  108.         @game_map            = Marshal.load(file)
  109.         @game_player         = Marshal.load(file)
  110.         # 读取截图
  111.         @file_bitmap         = Marshal.load(file)
  112.         @total_sec = @frame_count / Graphics.frame_rate
  113.       rescue
  114.         @file_exist = false
  115.       ensure
  116.         file.close
  117.       end
  118.     end
  119.   end
  120.   #--------------------------------------------------------------------------
  121.   # ● 刷新
  122.   #     index : 索引
  123.   #--------------------------------------------------------------------------
  124.   def refresh(index)
  125.     file_index = index
  126.     self.contents.clear
  127.     self.contents.font.color = normal_color
  128.     if @file_exist
  129.       # 描绘底部阴影
  130.       self.contents.fill_rect(12+3, 4+3, 326,249, Color.new(0,0,64))
  131.       # 描绘截图
  132.       draw_snap_bitmap(12, 4)
  133.       draw_party_characters(152, 296)
  134.       draw_playtime(0, 296+8, contents.width - 40, 2)
  135.     else
  136.       self.contents.draw_text(0, 296, 384-32, 24, "无此存档!", 1)
  137.     end
  138.   end
  139.   #--------------------------------------------------------------------------
  140.   # ◎ 更改索引
  141.   #     index : 索引
  142.   #--------------------------------------------------------------------------
  143.   def file_index=(file_index)
  144.     @file_index = file_index
  145.   end
  146.   #--------------------------------------------------------------------------
  147.   # ● 描绘游戏人物
  148.   #     x : 描画先 X 座標
  149.   #     y : 描画先 Y 座標
  150.   #--------------------------------------------------------------------------
  151.   def draw_party_characters(x, y)
  152.     for i in [email protected]
  153.       name = @characters[i][0]
  154.       index = @characters[i][1]
  155.       draw_character(name, index, x + i * 48, y)
  156.     end
  157.   end
  158.   #--------------------------------------------------------------------------
  159.   # ◎ 生成保存路径
  160.   #     path : 路径
  161.   #--------------------------------------------------------------------------
  162.   def make_dir(path)
  163.     dir = path.split("/")
  164.     for i in 0...dir.size
  165.       unless dir == "."
  166.         add_dir = dir[0..i].join("/")
  167.         begin
  168.           Dir.mkdir(add_dir)
  169.         rescue
  170.         end
  171.       end
  172.     end
  173.   end
  174.   #--------------------------------------------------------------------------
  175.   # ◎ 生成文件名
  176.   #     file_index : 存档文件索引 (0~3)
  177.   #--------------------------------------------------------------------------
  178.   def make_filename(file_index)
  179.     return "Save#{file_index + 1}.rvdata"
  180.   end
  181.   #--------------------------------------------------------------------------
  182.   # ◎ 描绘截图
  183.   #     x : 描画先 X 座標
  184.   #     y : 描画先 Y 座標
  185.   #--------------------------------------------------------------------------
  186.   def draw_snap_bitmap(x, y)
  187.     bitmap = @file_bitmap
  188.     if bitmap == nil
  189.       self.contents.draw_text(0, 112, 384-32, 24, "找不到截图文件!", 1)
  190.     else
  191.       self.contents.blur
  192.       rect = Rect.new(0, 0, 0, 0)
  193.       rect.width = bitmap.width
  194.       rect.height = bitmap.height
  195.       self.contents.blt(x, y, bitmap, rect)
  196.     end
  197.   end
  198.   #--------------------------------------------------------------------------
  199.   # ● 描绘游戏时间
  200.   #     x     : 描绘目标 X 坐标
  201.   #     y     : 描绘目标 Y 坐标
  202.   #     width : 宽
  203.   #     align : 配置
  204.   #--------------------------------------------------------------------------
  205.   def draw_playtime(x, y, width, align)
  206.     hour = @total_sec / 60 / 60
  207.     min = @total_sec / 60 % 60
  208.     sec = @total_sec % 60
  209.     time_string = sprintf("%02d:%02d:%02d", hour, min, sec)
  210.     self.contents.font.color = normal_color
  211.     self.contents.draw_text(x, y, width, WLH, time_string, 2)
  212.   end
  213. end

  214. #==============================================================================
  215. # ■ Scene_Base
  216. #==============================================================================
  217. class Scene_Base
  218.   #--------------------------------------------------------------------------
  219.   # ◎ 生成存档位图快照
  220.   #--------------------------------------------------------------------------
  221.   def snapshot_for_save
  222.     d_rect = Rect.new(0, 0, 326, 249)
  223.     s_rect = Rect.new(0, 0, 544, 416)
  224.     save_bitmap = Graphics.snap_to_bitmap
  225.     $game_temp.save_bitmap.dispose
  226.     $game_temp.save_bitmap = Bitmap.new(326, 249)
  227.     $game_temp.save_bitmap.stretch_blt(d_rect, save_bitmap, s_rect)
  228.   end
  229. end

  230. #==============================================================================
  231. # ■ Scene_Map
  232. #==============================================================================
  233. class Scene_Map < Scene_Base
  234.   #--------------------------------------------------------------------------
  235.   # ● 结束处理
  236.   #--------------------------------------------------------------------------
  237.   alias save_terminate terminate
  238.   def terminate
  239.     snapshot_for_save
  240.     save_terminate
  241.   end
  242. end

  243. #==============================================================================
  244. # ■ Scene_Title
  245. #==============================================================================
  246. class Scene_Title < Scene_Base
  247.   #--------------------------------------------------------------------------
  248.   # ● 判断继续是否有效
  249.   #--------------------------------------------------------------------------
  250.   def check_continue
  251.     @continue_enabled = (Dir.glob(SAVE_DIR + 'Save*.rvdata').size > 0)
  252.   end
  253. end

  254. #==============================================================================
  255. # ■ Scene_File
  256. #------------------------------------------------------------------------------
  257. #  处理文件的类。
  258. #==============================================================================
  259. class Scene_File < Scene_Base
  260.   #--------------------------------------------------------------------------
  261.   # ● 初始化对象
  262.   #     saving     : 存档标志 (false 为载入画面)
  263.   #     from_title : 调用标题画面的 "继续" 标志
  264.   #     from_event : 事件的 "调用存档画面" 的调用标志
  265.   #--------------------------------------------------------------------------
  266.   def initialize(saving, from_title, from_event)
  267.     @saving = saving
  268.     @from_title = from_title
  269.     @from_event = from_event
  270.   end
  271.   #--------------------------------------------------------------------------
  272.   # ● 开始处理
  273.   #--------------------------------------------------------------------------
  274.   def start
  275.     super
  276.     create_menu_background
  277.     @help_window = Window_Help.new
  278.     create_command_window
  279.     if @saving
  280.       @index = $game_temp.last_file_index
  281.       @help_window.set_text(Vocab::SaveMessage)
  282.     else
  283.       @index = self.latest_file_index
  284.       @help_window.set_text(Vocab::LoadMessage)
  285.     end
  286.     @refresh_index = @command_window.index = @index
  287.     @item_max = MAX_SAVE_ID
  288.     create_savefile_window
  289.   end
  290.   #--------------------------------------------------------------------------
  291.   # ● 结束处理
  292.   #--------------------------------------------------------------------------
  293.   def terminate
  294.     super
  295.     dispose_menu_background
  296.     @help_window.dispose
  297.     dispose_item_windows
  298.   end
  299.   #--------------------------------------------------------------------------
  300.   # ● 还原至原先的画面
  301.   #--------------------------------------------------------------------------
  302.   def return_scene
  303.     if @from_title
  304.       $scene = Scene_Title.new
  305.     elsif @from_event
  306.       $scene = Scene_Map.new
  307.     else
  308.       $scene = Scene_Menu.new(4)
  309.     end
  310.   end
  311.   #--------------------------------------------------------------------------
  312.   # ● 更新画面
  313.   #--------------------------------------------------------------------------
  314.   def update
  315.     super
  316.     update_menu_background
  317.     @help_window.update
  318.     update_savefile_windows
  319.     update_savefile_selection
  320.   end
  321.   #--------------------------------------------------------------------------
  322.   # ◎ 生成存档文件列表窗口
  323.   #--------------------------------------------------------------------------
  324.   def create_command_window
  325.     file_names = []
  326.     digit = MAX_SAVE_ID.to_s.size
  327.     str_f = ""
  328.     digit.times{|n| str_f += "0"}
  329.     str_f[str_f.size-1, 1] = digit.to_s
  330.     for i in 0...MAX_SAVE_ID
  331.       id = sprintf("%#{str_f}d", i+1)
  332.       file_names.push(Vocab::File + "\s" + id)
  333.     end
  334.     @command_window = Window_Command.new(160, file_names)
  335.     @command_window.height = 360
  336.     @command_window.y = 56
  337.   end
  338.   #--------------------------------------------------------------------------
  339.   # ● 生成存档文件窗口
  340.   #--------------------------------------------------------------------------
  341.   def create_savefile_window
  342.     @savefile_window = Window_SaveFile.new(@command_window.index, make_filename(@command_window.index))
  343.   end
  344.   #--------------------------------------------------------------------------
  345.   # ● 释放存档文件
  346.   #--------------------------------------------------------------------------
  347.   def dispose_item_windows
  348.     @command_window.dispose
  349.     @savefile_window.dispose
  350.   end
  351.   #--------------------------------------------------------------------------
  352.   # ● 更新存档文件窗口
  353.   #--------------------------------------------------------------------------
  354.   def update_savefile_windows
  355.     @command_window.update
  356.     @savefile_window.update
  357.   end
  358.   #--------------------------------------------------------------------------
  359.   # ● 更新存档文件选择
  360.   #--------------------------------------------------------------------------
  361.   def update_savefile_selection
  362.     if Input.trigger?(Input::C)
  363.       determine_savefile
  364.     end
  365.     if Input.trigger?(Input::B)
  366.       Sound.play_cancel
  367.       return_scene
  368.     end
  369.     if @refresh_index != @command_window.index
  370.       @refresh_index = @command_window.index
  371.       @savefile_window.dispose
  372.       create_savefile_window
  373.       end
  374.   end
  375.   #--------------------------------------------------------------------------
  376.   # ● 确定存档文件
  377.   #--------------------------------------------------------------------------
  378.   def determine_savefile
  379.     if @saving
  380.       Sound.play_save
  381.       do_save
  382.     else
  383.       if @savefile_window.file_exist
  384.         Sound.play_load
  385.         do_load
  386.       else
  387.         Sound.play_buzzer
  388.         return
  389.       end
  390.     end
  391.     $game_temp.last_file_index = @index
  392.   end
  393.   #--------------------------------------------------------------------------
  394.   # ◎ 生成文件名
  395.   #     file_index : 存档文件索引
  396.   #--------------------------------------------------------------------------
  397.   def make_filename(file_index)
  398.     return "Save#{file_index + 1}.rvdata"
  399.   end
  400.   #--------------------------------------------------------------------------
  401.   # ● 按时间戳选择最新的文件
  402.   #--------------------------------------------------------------------------
  403.   def latest_file_index
  404.     index = 0
  405.     latest_time = Time.at(0) # 时间戳
  406.     for i in 0...MAX_SAVE_ID
  407.       file_name = make_filename(i)
  408.       if FileTest.exist?(SAVE_DIR + file_name)
  409.         file = File.open(SAVE_DIR + file_name, "r")
  410.         if file.mtime > latest_time
  411.           latest_time = file.mtime
  412.           index = i
  413.         end
  414.       end
  415.     end
  416.     return index
  417.   end
  418.   #--------------------------------------------------------------------------
  419.   # ● 执行存档
  420.   #--------------------------------------------------------------------------
  421.   def do_save
  422.     file_name = make_filename(@command_window.index)
  423.     file = File.open(SAVE_DIR + file_name, "wb")
  424.     write_save_data(file)
  425.     # 保存位图
  426.     $file_bitmap = $game_temp.save_bitmap
  427.     Marshal.dump($file_bitmap, file)
  428.     file.close
  429.     return_scene
  430.   end
  431.   #--------------------------------------------------------------------------
  432.   # ● 执行载入
  433.   #--------------------------------------------------------------------------
  434.   def do_load
  435.     file_name = make_filename(@command_window.index)
  436.     file = File.open(SAVE_DIR + file_name, "rb")
  437.     read_save_data(file)
  438.     file.close
  439.     $scene = Scene_Map.new
  440.     RPG::BGM.fade(1500)
  441.     Graphics.fadeout(60)
  442.     Graphics.wait(40)
  443.     @last_bgm.play
  444.     @last_bgs.play
  445.   end
  446. end
复制代码





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