加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 lisliz 于 2021-11-6 19:23 编辑
有玩家提醒了我,所以我决定把这个优化提上日程。顺带写一个修改教程供后人参考吧。
RM对手柄摇杆的利用代码在Input._updateGamepadState函数里,以MZ为例(与MV极度相似):
Input._updateGamepadState = function(gamepad) { const lastState = this._gamepadStates[gamepad.index] || []; const newState = []; const buttons = gamepad.buttons; const axes = gamepad.axes; const threshold = 0.5; newState[12] = false; newState[13] = false; newState[14] = false; newState[15] = false; for (let i = 0; i < buttons.length; i++) { newState[i] = buttons[i].pressed; } if (axes[1] < -threshold) { newState[12] = true; // up } else if (axes[1] > threshold) { newState[13] = true; // down } if (axes[0] < -threshold) { newState[14] = true; // left } else if (axes[0] > threshold) { newState[15] = true; // right } for (let j = 0; j < newState.length; j++) { if (newState[j] !== lastState[j]) { const buttonName = this.gamepadMapper[j]; if (buttonName) { this._currentState[buttonName] = newState[j]; } } } this._gamepadStates[gamepad.index] = newState; };
Input._updateGamepadState = function(gamepad) {
const lastState = this._gamepadStates[gamepad.index] || [];
const newState = [];
const buttons = gamepad.buttons;
const axes = gamepad.axes;
const threshold = 0.5;
newState[12] = false;
newState[13] = false;
newState[14] = false;
newState[15] = false;
for (let i = 0; i < buttons.length; i++) {
newState[i] = buttons[i].pressed;
}
if (axes[1] < -threshold) {
newState[12] = true; // up
} else if (axes[1] > threshold) {
newState[13] = true; // down
}
if (axes[0] < -threshold) {
newState[14] = true; // left
} else if (axes[0] > threshold) {
newState[15] = true; // right
}
for (let j = 0; j < newState.length; j++) {
if (newState[j] !== lastState[j]) {
const buttonName = this.gamepadMapper[j];
if (buttonName) {
this._currentState[buttonName] = newState[j];
}
}
}
this._gamepadStates[gamepad.index] = newState;
};
看到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里维护按键对应的操作名称,通过操作名称来获取。
Object.assign(Input.gamepadMapper, { 96: "s-up", // S-UP 97: "s-down", // S-DOWN 98: "s-left", // S-LEFT 99: "s-right" // S-RIGHT });
Object.assign(Input.gamepadMapper, {
96: "s-up", // S-UP
97: "s-down", // S-DOWN
98: "s-left", // S-LEFT
99: "s-right" // S-RIGHT
});
以上,我给Input.gamepadMapper增加了96-99键码,96-99键码实际是不对应手柄上的任何按键的,这是我随便取的号码,接下来会用到。s-up和s-down等四个名字是操作名称,分别对应副摇杆摇动的方向。
newState[96] = false; newState[97] = false; newState[98] = false; newState[99] = false; if (axes[3] < -thresholdY) { newState[96] = true; } else if (axes[3] > thresholdY) { newState[97] = true; } if (axes[2] < -thresholdX) { newState[98] = true; } else if (axes[2] > thresholdX) { newState[99] = true; }
newState[96] = false;
newState[97] = false;
newState[98] = false;
newState[99] = false;
if (axes[3] < -thresholdY) {
newState[96] = true;
} else if (axes[3] > thresholdY) {
newState[97] = true;
}
if (axes[2] < -thresholdX) {
newState[98] = true;
} else if (axes[2] > thresholdX) {
newState[99] = true;
}
在第二个if语句后面加入上述代码,96-99在这里用到。(为了保证其他插件的兼容性不要像我一样直接覆盖原函数,应该使用追加的写法),thresholdX和thresholdY这两个变量代表手柄摇杆死区大小,摇杆死区是游戏术语有兴趣的可以百度下。
至此,我们已经可以用Input.isPressed("s-up")来判断副摇杆是否正在往上扳动了。接下来需要根据结果移动游戏摄像机。在RM的地图界面上,控制摄像机镜头位置的变量是$gameMap._displayX和$gameMap._displayY,我只能提供一个思路来实现需求,但一个优秀的游戏往往会有非常严苛的需求,我作为游戏设计者,首先要保证拨弄摇杆时镜头始终平滑缓动不能突然镜头瞬移,而且要处理好地图边缘问题,掌握好合适的移动速度与移动距离,这些苛刻的需求加起来会让代码变的晦涩难懂,所以接下来的代码就很难解释为什么这么写了。
function Game_SightMove() { this.initialize(...arguments); } Game_SightMove.prototype.initialize = function() { this.clear(); }; Game_SightMove.prototype.clear = function() { this._state = 0; this._current = 0; this._start = 0; this._timeline = 0; }; Game_SightMove.prototype.update = function(display, min, max, n, p) { const maxTime = this.maxTime(); if(Input.isPressed(n)) { this.checkNewState(-1); } else if(Input.isPressed(p)) { this.checkNewState(1); } else { this.checkNewState(0); } if(this._timeline < maxTime) { this._timeline++; this.calc(maxTime); } if(display < min) { return this._current - display + min; } else if(display > max) { return this._current - display + max; } else { return this._current; } }; Game_SightMove.prototype.checkNewState = function(state) { if(this._state !== state) { this._state = state; this._start = this._current; this._timeline = 0; } }; Game_SightMove.prototype.calc = function(maxTime) { this._current = AnimationController.easeOutCubic(this._timeline / maxTime) * (this.maxDistance() * this._state - this._start) + this._start; }; Game_SightMove.prototype.maxTime = function() { return 60; }; Game_SightMove.prototype.maxDistance = function() { return 4; };
function Game_SightMove() {
this.initialize(...arguments);
}
Game_SightMove.prototype.initialize = function() {
this.clear();
};
Game_SightMove.prototype.clear = function() {
this._state = 0;
this._current = 0;
this._start = 0;
this._timeline = 0;
};
Game_SightMove.prototype.update = function(display, min, max, n, p) {
const maxTime = this.maxTime();
if(Input.isPressed(n)) {
this.checkNewState(-1);
} else if(Input.isPressed(p)) {
this.checkNewState(1);
} else {
this.checkNewState(0);
}
if(this._timeline < maxTime) {
this._timeline++;
this.calc(maxTime);
}
if(display < min) {
return this._current - display + min;
} else if(display > max) {
return this._current - display + max;
} else {
return this._current;
}
};
Game_SightMove.prototype.checkNewState = function(state) {
if(this._state !== state) {
this._state = state;
this._start = this._current;
this._timeline = 0;
}
};
Game_SightMove.prototype.calc = function(maxTime) {
this._current = AnimationController.easeOutCubic(this._timeline / maxTime) * (this.maxDistance() * this._state - this._start) + this._start;
};
Game_SightMove.prototype.maxTime = function() {
return 60;
};
Game_SightMove.prototype.maxDistance = function() {
return 4;
};
分别对X轴和Y轴调用两次update(移动镜头对应的中心点, 地图左(上)边缘, 地图右(下)边缘, 副摇杆左(上)方向操作名, 副摇杆右(下)方向操作名)函数分别得到XY方向上的镜头偏移量,实际效果如下图:
|