设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 1438|回复: 1
打印 上一主题 下一主题

[讨论] 突然发现有些游戏用手柄的副摇杆能调整摄像机视角

[复制链接]

Lv4.逐梦者

梦石
0
星屑
5060
在线时间
705 小时
注册时间
2018-12-11
帖子
241
跳转到指定楼层
1
发表于 2021-11-6 19:23:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

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

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

x
本帖最后由 lisliz 于 2021-11-6 19:23 编辑

有玩家提醒了我,所以我决定把这个优化提上日程。顺带写一个修改教程供后人参考吧。

RM对手柄摇杆的利用代码在Input._updateGamepadState函数里,以MZ为例(与MV极度相似):

JAVASCRIPT 代码复制
  1. Input._updateGamepadState = function(gamepad) {
  2.     const lastState = this._gamepadStates[gamepad.index] || [];
  3.     const newState = [];
  4.     const buttons = gamepad.buttons;
  5.     const axes = gamepad.axes;
  6.     const threshold = 0.5;
  7.     newState[12] = false;
  8.     newState[13] = false;
  9.     newState[14] = false;
  10.     newState[15] = false;
  11.     for (let i = 0; i < buttons.length; i++) {
  12.         newState[i] = buttons[i].pressed;
  13.     }
  14.     if (axes[1] < -threshold) {
  15.         newState[12] = true; // up
  16.     } else if (axes[1] > threshold) {
  17.         newState[13] = true; // down
  18.     }
  19.     if (axes[0] < -threshold) {
  20.         newState[14] = true; // left
  21.     } else if (axes[0] > threshold) {
  22.         newState[15] = true; // right
  23.     }
  24.     for (let j = 0; j < newState.length; j++) {
  25.         if (newState[j] !== lastState[j]) {
  26.             const buttonName = this.gamepadMapper[j];
  27.             if (buttonName) {
  28.                 this._currentState[buttonName] = newState[j];
  29.             }
  30.         }
  31.     }
  32.     this._gamepadStates[gamepad.index] = newState;
  33. };


看到const axes = gamepad.axes;这一行,接调试器发现axes是个长度为4的数组,axes[0]和axes[1]是主摇杆的X轴移动距离和Y轴移动距离,那么axes[2]和axes[3]自然就是副摇杆的X和Y轴移动距离。
一般我们对RM这个系统做出扩充时都本着尽量不破坏原有代码结构,这样可以增加与其他插件的兼容性,本着这个原则来看,我们需要学习RM对主摇杆的利用方式,用相同的方式利用副摇杆。

看代码的前两个if语句,RM的编程人员直接根据axes[0]和axes[1]移动距离,分别赋值newState的12~15下标,12、13、14、15是上下左右对应的手柄键码,因为主摇杆的操作和手柄十字键的操作相同。但副摇杆却没有对应的键码,因此我们需要在Input.gamepadMapper这个名称到键码的映射中增加四个元素。

帮助大家理解下Input.gamepadMapper和Input.keyMapper为什么要存在于RM中,因为RM可以用多个不同的按键触发同样一个操作(例如ESC和X都会开启菜单),所以需要一个东西来记录到底是哪些按键会触发某个操作(多对一),所以当按键被按下时,RM会利用这个东西来分析这个按键触发的操作名然后记录下来,其他脚本和插件就可以通过Input.isTriggered("操作名字")等函数来获取某个操作是否触发。

正因为如此,符合RM代码结构的插件和脚本一般是不会直接获取某个按键是否按下,而是在Input.gamepadMapper或者Input.keyMapper里维护按键对应的操作名称,通过操作名称来获取。

JAVASCRIPT 代码复制
  1. Object.assign(Input.gamepadMapper, {
  2.         96: "s-up",         // S-UP
  3.         97: "s-down", // S-DOWN
  4.         98: "s-left", // S-LEFT
  5.         99: "s-right" // S-RIGHT
  6. });

以上,我给Input.gamepadMapper增加了96-99键码,96-99键码实际是不对应手柄上的任何按键的,这是我随便取的号码,接下来会用到。s-up和s-down等四个名字是操作名称,分别对应副摇杆摇动的方向。

JAVASCRIPT 代码复制
  1. newState[96] = false;
  2.     newState[97] = false;
  3.     newState[98] = false;
  4.     newState[99] = false;
  5.     if (axes[3] < -thresholdY) {
  6.         newState[96] = true;
  7.     } else if (axes[3] > thresholdY) {
  8.         newState[97] = true;
  9.     }
  10.     if (axes[2] < -thresholdX) {
  11.         newState[98] = true;
  12.     } else if (axes[2] > thresholdX) {
  13.         newState[99] = true;
  14.     }

在第二个if语句后面加入上述代码,96-99在这里用到。(为了保证其他插件的兼容性不要像我一样直接覆盖原函数,应该使用追加的写法),thresholdX和thresholdY这两个变量代表手柄摇杆死区大小,摇杆死区是游戏术语有兴趣的可以百度下。

至此,我们已经可以用Input.isPressed("s-up")来判断副摇杆是否正在往上扳动了。接下来需要根据结果移动游戏摄像机。在RM的地图界面上,控制摄像机镜头位置的变量是$gameMap._displayX和$gameMap._displayY,我只能提供一个思路来实现需求,但一个优秀的游戏往往会有非常严苛的需求,我作为游戏设计者,首先要保证拨弄摇杆时镜头始终平滑缓动不能突然镜头瞬移,而且要处理好地图边缘问题,掌握好合适的移动速度与移动距离,这些苛刻的需求加起来会让代码变的晦涩难懂,所以接下来的代码就很难解释为什么这么写了。

JAVASCRIPT 代码复制
  1. function Game_SightMove() {
  2.         this.initialize(...arguments);
  3. }
  4.  
  5. Game_SightMove.prototype.initialize = function() {
  6.         this.clear();
  7. };
  8.  
  9. Game_SightMove.prototype.clear = function() {
  10.         this._state = 0;
  11.         this._current = 0;
  12.         this._start = 0;
  13.         this._timeline = 0;
  14. };
  15.  
  16. Game_SightMove.prototype.update = function(display, min, max, n, p) {
  17.         const maxTime = this.maxTime();
  18.         if(Input.isPressed(n)) {
  19.                 this.checkNewState(-1);
  20.         } else if(Input.isPressed(p)) {
  21.                 this.checkNewState(1);
  22.         } else {
  23.                 this.checkNewState(0);
  24.         }
  25.         if(this._timeline < maxTime) {
  26.                 this._timeline++;
  27.                 this.calc(maxTime);
  28.         }
  29.         if(display < min) {
  30.                 return this._current - display + min;
  31.         } else if(display > max) {
  32.                 return this._current - display + max;
  33.         } else {
  34.                 return this._current;
  35.         }
  36. };
  37.  
  38. Game_SightMove.prototype.checkNewState = function(state) {
  39.         if(this._state !== state) {
  40.                 this._state = state;
  41.                 this._start = this._current;
  42.                 this._timeline = 0;
  43.         }
  44. };
  45.  
  46. Game_SightMove.prototype.calc = function(maxTime) {
  47.         this._current = AnimationController.easeOutCubic(this._timeline / maxTime) * (this.maxDistance() * this._state - this._start) + this._start;
  48. };
  49.  
  50. Game_SightMove.prototype.maxTime = function() {
  51.         return 60;
  52. };
  53.  
  54. Game_SightMove.prototype.maxDistance = function() {
  55.         return 4;
  56. };

分别对X轴和Y轴调用两次update(移动镜头对应的中心点, 地图左(上)边缘, 地图右(下)边缘, 副摇杆左(上)方向操作名, 副摇杆右(下)方向操作名)函数分别得到XY方向上的镜头偏移量,实际效果如下图:

评分

参与人数 3+3 收起 理由
www826001 + 1 精品文章
煎蛋流Kira☆ + 1 精品文章
哇哇哇啊叭叭 + 1 脚本美工兼得可平天下

查看全部评分

Lv3.寻梦者

梦石
0
星屑
2851
在线时间
446 小时
注册时间
2016-9-26
帖子
1222
2
发表于 2021-11-6 20:05:02 | 只看该作者
大佬牛皮
回复 支持 反对

使用道具 举报

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

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-29 14:19

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表