赞 | 2 |
VIP | 143 |
好人卡 | 1 |
积分 | 1 |
经验 | 216792 |
最后登录 | 2019-10-10 |
在线时间 | 24 小时 |
Lv1.梦旅人
- 梦石
- 0
- 星屑
- 61
- 在线时间
- 24 小时
- 注册时间
- 2008-8-5
- 帖子
- 1924
|
本帖最后由 紫苏 于 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 的现象
GetAsncKeyState 检测的是中断层的按键状态,所以跳过了 OS 的逻辑按键映射,当这个映射并不是物理左键对应逻辑左键、物理右键对应逻辑右键时,就会发生一些很有喜感的现象,如用 OS 提供的接口让鼠标左右键反转后,用户认为按下了左键(当然实际按的是鼠标右键)但程序却依然认为按下了右键,用户认为按下了右键(当然实际按的是鼠标左键)但程序却依然认为按下了左键
解决方法:用 GetKeyState 判断逻辑按键
press? 是用的 GetKeyState 来判断按键状态,但是它取了最低位,最低位是一个按键开关的状态,每次按键按下时状态会反转,要么就是对大写锁定等有开关状态的按键比较有用,要么就是用于实现多次按某个键轮流进行不同操作的功能
很明显在这里取出最低位不合适,press? 应该返回按键当前是否按下的状态,所以应该用 0x8000 取出最高位,否则会有按下一次弹起后 press? 也永远返回 true,或者没有按过 press? 就返回 true 的现象
@down.push(1) if (GetKeyState.call(1) & 0x1000) / 0x1000 == 1
这几行脚本很奇怪,为什么用的是 0x1000?用 0x1000 进行与运算取出的是倒数第四高位,而 MSDN 说按下的状态是在最高位
测试了一下,似乎当最高位为 1 时,高字节的所有位,包括低字节的最高位确实同时变成了 1,但似乎不应该以偏概全假设这个语句永远是真,很可能是平台有关的
另外这里可以不用除,也不用移位,直接判断返回值 != 0 即可
刷新的实现比较诡异,用了数组来保存这次刷新按下的键,虽然数组的长度最大也只是个位数,但频繁分配内存、搜索、释放内存也是个不容忽视的问题,可以优化为使用变量记录按下状态(同第一个问题的解),刷新时仅仅是改变 true/false
关于获取句柄,有上面某柳提到的问题,还有标题动态刷新时匹配标题失败的问题,以及同时开两个标题相同的游戏窗口时可能获取到不正确的句柄的问题,统一解决方法:http://rpg.blue/forum.php?mod=viewthread&tid=133018
关于鼠标滚轮和 move? 的实现,理论上还是有些问题,因为即便在判断的那一刻确实有 WM_MOUSEHWHEEL 或 WM_MOUSEMOVE 消息,但它们并不一定处于消息队列的头部,特别是在硬件性能差或者资源紧凑的环境下,可能每次 PeekMessage 的时候都有数个消息处于上述两个消息的前端,暂时还没有被 RM 默认的消息循环移出队列并处理,这样直接效果就是滚动或移动了但没反应
另外 WM_MOUSEHWHEEL 的 wParam 还包含滚动距离的数据,可以在默认的菜单里做相应的相应的处理,目前无论滚动多远都只会让窗口光标移动 1
关于鼠标的实现其实还有一种方法,个人感觉比这种用 GetCursorPos、GetKeyState 等函数的方法要简单,就是直接处理 WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP 等各种鼠标相关的消息
以前夏娜发过一个替换窗口过程的工程,在 DLL 里 SetWindowLong 替换 RM 的窗口过程,并用 rgss102j.dll 里的 RGSSEval 去回调 RM 脚本里的各种回调函数,方法和 RM 后台运行的一样,可以用来处理鼠标的消息
具体方法可以是:设计各种闭包,在不同的场景让一个全局的回调 Proc 保存这个闭包,然后让 DLL 里的窗口过程函数统一调用处理鼠标消息 |
|