该插件用mv的方式内置一个支持中文输入法键盘
内容:
- 制作中文词典管理器及获取数据
- 创建输入法窗体
1.制作中文词典管理器及获取数据
第一创建一个文件Dictionnary.js
并在其中创建一个字典管理类DictionaryManager
,再将其导入到mv的插件管理器中。
function DictionaryManager(){
throw new Error('This is a static class');
}
然后创建字典对象,数据可以在任意字典网站按拼音、部首、五笔导出即可,此文以拼音格式排序。最后存入一个数据对象中ChineseDictionary
, 每个拼音即字典对象的key
,后面会使用到,每个拼音对应的字即字典对象的value
。字数过多则不一一列出,大致结构如下:
DictionaryManager.ChineseDictionary = {
a : ['吖阿呵啊锕腌嗄錒'],
ai : ['砹唉娭挨埃捱娾啀....'],
an : ['俺隌谙偣腤安暗黯....'],
ang : [.......]
ao : [.......]
.......
}
字典模板有了,接下来就是每次输入拼音时能获取到的数据处理:
处理函数其一
将符合条件的数据以字符串的方式拼接并返回数据:getDictionaryString_Zh_Cn
。
接收拼音字符串value
并将其转换为小写.toLowerCase()
,循环遍历字典对象的key
值,符合以下条件的数据将拼接到一个变量中在最后进行返回:
1.使用hasOwnProperty(key)
判断key
值只属于字典对象ChineseDictionary
的;
2:key
的长度大于等于拼音字符串的长度;
3:截取key
和拼音字符串等长的字符长度然后等于拼音的;
结构如下 。
DictionaryManager.getDictionaryString_Zh_Cn = function(value){
let texts = "";
value = value.toLowerCase();
for (let key in DictionaryManager.ChineseDictionary) {
if(!DictionaryManager.ChineseDictionary.hasOwnProperty(key)){
continue;
}
if(key.substring(0, value.length) !== value){
continue;
}
texts+=DictionaryManager.ChineseDictionary[key];
}
return texts;
}
处理函数其二
将符合条件的数据以数组的方式返回数据:getDictionaryList_Zh_Cn
。
DictionaryManager.getDictionaryList_Zh_Cn = function(value){
let texts = this.getDictionaryString_Zh_Cn(value);
return texts.split("");
}
以上字典管理器部分完成
2.创建输入法窗体
支持中文输入的界面构成分为:输入框区域 / 拼音区域 / 候选字区域 / 功能按键区域 / 字母区域
2.1 创建一个文件KeyboardWindow.js
并在其中创建一个继承自Window_Base
的窗体对象Window_Keyboard
,再将其导入到mv的插件管理器中。
function Window_Keyboard() {
this.initialize.apply(this, arguments);
}
Window_Keyboard.prototype = Object.create(Window_Base.prototype);
Window_Keyboard.prototype.constructor = Window_Keyboard;
2.2 输入法窗体的基础框架搭建
2.2.1:初始化大小位置以及添加窗体的一些基础参数
Window_Keyboard.MODE_Zh_cn = 0;// "Chinese";
Window_Keyboard.MODE_BE = 1; //"BigChar";
Window_Keyboard.MODE_SE = 2; // "SmallChar";
Window_Keyboard.MODE_N = 3; //"Number";
Window_Keyboard.BASEMODE = ["中文", "大写", "小写", "数字"];
Window_Keyboard.prototype.initialize = function() {
//这里初始化配置窗体占用整个画面,具体大小可按需配置。
Window_Base.prototype.initialize.call(this, 0, 0, Graphics.boxWidth, Graphics.boxHeight);
this._modeIndex = 0; //输入法索引
this._modeText = Window_Keyboard.BASEMODE[this._modeIndex];//输入法中文文本
this._modeFixed = false; //锁定窗体输入模式
this._maxInt = 9; //数字模式下最大文本长度
this._helpText = ""; //输入提示语文本
this.initMember();
this.name = "KeyboardWindow";
this.hide(); //窗体初始化完成后就隐藏
};
Window_Keyboard.prototype.initMember = function() {
this._text = ""; //当前输入框中的文本
this._callWindow = null; //唤醒输入法窗体的对象
this._callFun = null; //完成输入后回调的函数
this._keys = []; //键的集合
this._buttons = []; //功能按键的集合
this._oldKey = null; //上一个选中的键
this.clear();
}
Window_Keyboard.prototype.clear = function() {
this._words = []; //展示的候选字
this._wordsbase = []; //当前拼音的所有候选字
this._phonetic = ""; //输入的拼音
this._wordsIndex = 0; //候选字分页索引
}
2.2.2:设置唤醒输入法的窗体及回调的函数
/**
* 设置打开输入法窗体的父级窗体
* 在确认打开该窗体时再调用
* 调用该函数的同时父级窗体停止活动,并设置当前输入法窗体活动
* */
Window_Keyboard.prototype.setCallWindow = function(win) {
this._callWindow = win;
win.deactivate();//父级窗体停止活跃
this.activate();//当前窗体活跃
this.open();
}
//设置完成输入后的回调函数
Window_Keyboard.prototype.setCallFun = function(fun) {
this._callFun = fun;
}
//设置初始文本
Window_Keyboard.prototype.setText = function(text) {
this._text = text.toString();
}
//设置提示语文本
Window_Keyboard.prototype.setHelpText = function(text) {
this._helpText = text;
}
//设置当锁定数字模式时输入的最大值
Window_Keyboard.prototype.setMaxInt = function(maxInt) {
this._maxInt = Number(maxInt);
}
2.3 输入法的布局样式
2.3.1 确定各区域位置相对于Y轴的位置
Window_Keyboard.prototype.getOriginY = function(type){
if(!type){ return 0; }
let contentsHeight = this.contentsHeight();
let lineHeight = this.lineHeight();
let startLine = 0;
switch(type){
case "input" : startLine = 8; break; //间隔两行,倒数第8排为当前已输入的文本数据的区域
case "phonetic" : startLine = 6; break; //倒数第6排为拼音的区域
case "word" : startLine = 5; break; //倒数第5排为候选字的区域
case "button" : startLine = 4; break; //倒数第4排为功能按键的区域
case "key" : startLine = 3; break; //最下面三排为字母、数字按键区域
}
return contentsHeight - startLine * lineHeight;
}
2.3.2 不同模式下字母、数字按键的矩形区域及显示的文本:
非数字模式时键盘显示26个字母和一个空格共计27个按键,数字模式时显示10个数字和一个小数点合计11个按键:
//创建基本按键
Window_Keyboard.prototype.createKeysData = function() {
let keys= "";
switch(this._modeIndex){
case Window_Keyboard.MODE_Zh_cn:
case Window_Keyboard.MODE_BE:
keys = "QWERTYUIOASDFGHJKLZXCVBNMP ";//9
break;
case Window_Keyboard.MODE_SE:
keys = "qwertyuioasdfghjklzxcvbnmp ";//9
break;
case Window_Keyboard.MODE_N:
keys ="1234567890"//4
if(!this._integer){//如果不是限制只能输入整数,则加入小数点
keys += ".";
}
break;
}
keys = keys.split("");
this._keys = [];
keys.forEach((key, index) => {
this._keys.push(this.keyRect(index, key));
}, this);
}
//根据按键索引配置对应的矩形区域,整个区域置于窗体底部
Window_Keyboard.prototype.keyRect = function(index, key){
let contentsWidth = this.contentsWidth() ;
let lineHeight = this.lineHeight();
//每行按键的数量,数字为4个,非数字为9个
let lineNum = this._modeIndex == Window_Keyboard.MODE_N? 4 : 9;
let rect = new Rectangle();
rect.width = contentsWidth / lineNum;
rect.height = lineHeight;
rect.x = (index % lineNum) * rect.width;
rect.y = this.getOriginY("key") + Math.floor(index / lineNum) * lineHeight;
rect.x2 = rect.x + rect.width;
rect.y2 = rect.y + rect.height;
rect.key = key; //按键的原本值
rect.text = key === " " ? "空格" : key;//按键的显示值
rect.isKey = true;//该矩形区域是按键区域
return rect;
}
2.3.3 功能按键的区域绘制
切换键盘模式的切换键,切换中文模式候选字的上/下一页建,每次点击删除拼音末尾字符或输入框末尾字符的删除键,清空拼音和输入框的清空键,确认键,撤销键;
其它按键可按需添加,最后实现其对应功能即可。
//所有按钮矩形区域
Window_Keyboard.prototype.createButtonData = function(){
let buttons = [];
//输入模式不固定的时候加入该按键以切换输入模式
if(!this._modeFixed){
buttons = ["切换"];
}
//中文模式有候选字,需要加入分页按钮
if(this._modeIndex == Window_Keyboard.MODE_Zh_cn){
buttons = buttons.concat(["上一页","下一页"]);
}
buttons = buttons.concat(["删除", "清空", "确认", "撤销"]);
this._buttons = [];
let len = buttons.length;
buttons.forEach((button, index) => {
this._buttons.push(this.buttonRect(index, button, len));
}, this);
}
//根据按钮索引配置对应的矩形区域,整个区域置于窗体底部按键区域的上部
Window_Keyboard.prototype.buttonRect = function(index, button, len){
let contentsWidth = this.contentsWidth() ;
let lineHeight = this.lineHeight();
let rect = new Rectangle();
//根据按钮总数动态获取宽
rect.width = contentsWidth / len;
rect.height = lineHeight;
rect.x = (index % len) * rect.width;
rect.y = this.getOriginY("button");
rect.x2 = rect.x + rect.width;
rect.y2 = rect.y + rect.height;
rect.key = button;
//当该按键为切换时显示的文本为输入模式的文本
rect.text = button == "切换" ? this._modeText : button;
rect.isButton = true;//该区域是功能按钮区域
return rect;
}
2.3.4 候选字区域绘制
//所有候选字矩形区域
Window_Keyboard.prototype.createWordData = function(){
this._wordsIndex = 0;
if(this._phonetic){
//通过拼音在字典管理器获取字的集合
this._wordsbase = DictionaryManager.getDictionaryList_Zh_Cn(this._phonetic);
}else{
this._wordsbase = [];
}
this.updateWordData();
}
//根据索引切换候选字
Window_Keyboard.prototype.updateWordData = function(){
let listbase = this._wordsbase;
let list = [];
this._words = [];
//限制候选字的最大索引页
this._wordsIndex = this._wordsIndex.clamp(0, Math.floor(listbase.length / 9));
//取出当前索引的数据
list = listbase.slice(this._wordsIndex * 9, (this._wordsIndex + 1) * 9);
list.forEach((word, index) => { this._words.push(this.wordRect(index, word))}, this);
}
//根据候选字索引配置对应的矩形区域,整个区域置于窗体底部按钮区域的上部
Window_Keyboard.prototype.wordRect = function(index, word){
let contentsWidth = this.contentsWidth() ;
let lineHeight = this.lineHeight();
let rect = new Rectangle();
//一页显示9个候选字
rect.width = contentsWidth / 9;
rect.height = lineHeight;
rect.x = (index % 9) * rect.width;
rect.y = this.getOriginY("word");
rect.x2 = rect.x + rect.width;
rect.y2 = rect.y + rect.height;
rect.key = word;
rect.isWord = true; //该区域是候选字区域
return rect;
}
2.3.5 拼音矩形区域
//拼音区域,整个区域置于窗体底部候选字区域的上部
Window_Keyboard.prototype.phoneticRect = function(){
let contentsWidth = this.contentsWidth() ;
let lineHeight = this.lineHeight();
let rect = new Rectangle();
rect.width = contentsWidth;
rect.height = lineHeight;
rect.x = 0;
rect.y = this.getOriginY("phonetic");
rect.x2 = rect.x + rect.width;
rect.y2 = rect.y + rect.height;
rect.key = this._phonetic;
rect.isInputChar = true; //该区域是拼音区域
rect.align = "center"; //居中绘制
return rect;
}
2.3.6 输入框矩形区域
//输入框区域
Window_Keyboard.prototype.inputRect = function() {
let rect = new Rectangle();
rect.width = this.width * 0.8;
rect.height = this.lineHeight();
rect.x = (this.width - rect.width) / 2;
rect.y = this.getOriginY("input");
return rect;
}
2.4 各按键区域数据已经能获取到了,接下来在合适的时机去获取并绘制
在输入法模式改变时,需要清除输入法的数据,然后重新获取各区域数据;
//设置窗体的模式
Window_Keyboard.prototype.setMode = function(modeIndex, fixed) {
this._modeIndex = modeIndex;
this._modeFixed = !!fixed;
this._modeText = Window_Keyboard.BASEMODE[this._modeIndex];
this._words = [];
this._phonetic = "";
//输入模式变更时更新的按键区域数据
this.createButtonData();
this.createKeysData();
}
Window_Keyboard.prototype.setChinese = function(fixed) {
this.setMode(Window_Keyboard.MODE_Zh_cn, fixed);
}
Window_Keyboard.prototype.setBigEnglish = function(fixed) {
this.setMode(Window_Keyboard.MODE_BE, fixed);
}
Window_Keyboard.prototype.setSmallEnglish = function(fixed) {
this.setMode(Window_Keyboard.MODE_SE, fixed);
}
/**
* 设置为数字输入模式
* @param fixed 锁定输入模式 true/false
* @param integer 是否限制输入整数
* @param intLength 最大长度,用于限定最大值
*/
Window_Keyboard.prototype.setNumber = function(fixed, integer, intLength) {
this._integer = integer || this._integer;
this._maxInt = Math.pow(10, intLength || 9) - 1;
this.setMode(Window_Keyboard.MODE_N, fixed);
}
在输入法窗体开始活动起来进行使用的时候,设置默认输入法并创建各区域的数据;
/**输入窗体开始活动时 */
Window_Keyboard.prototype.activate = function() {
Window_Base.prototype.activate.call(this);
this.show();
if(!this._modeFixed){ //没有锁定输入法,则打开时默认为中文输入法
this.setChinese();
}
this.createButtonData();
this.createKeysData();
this.refresh();
}
//刷新的时候清空当前窗体的所有数据,重新绘制各区域的数据到窗体上
Window_Keyboard.prototype.refresh = function() {
this.createContents();
this.drawHelpText();
this.drawAllKeyItem();
this.drawPhonetic();
this.drawInputText();
}
//输入提示语文本的绘制
Window_Keyboard.prototype.drawHelpText = function() {
let rect = new Rectangle();
let standardPadding = this.standardPadding();
let lineHeight = this.lineHeight();
rect.x += standardPadding;
rect.y += standardPadding;
if(this._helpText !== ""){
this.drawText(this._helpText, rect.x, rect.y, rect.width);
}
if(this._integer){
rect.y += lineHeight;
this.drawText("最大值:"+ this._maxInt, rect.x, rect.y, rect.width);
}
}
//获取所有已经配置好矩形的区域数据,文本居中显示
Window_Keyboard.prototype.drawAllKeyItem = function() {
let list = this.allKeys();
let obj = list.filter(l => { return l.key === this._oldKey; }, this)[0];
list.forEach(rect => {
this.drawText(rect.text || rect.key,
rect.x, rect.y,
rect.width, "center");
}, this);
//设置光标在该按键闪烁
if(obj){
this.setCursorRect(obj.x, obj.y, obj.width, obj.height);
}
}
Window_Keyboard.prototype.allKeys = function() {
let list = this._words.concat(this._buttons).concat(this._keys);
return list;
}
//拼音矩形区域
Window_Keyboard.prototype.drawPhonetic = function() {
let rect = this.phoneticRect();
this.drawText(rect.key, rect.x, rect.y, rect.width, "center");
}
//绘制输入框的数据
Window_Keyboard.prototype.drawInputText = function() {
let rect = this.inputRect();
//绘制该区域背景
this.contents.fillRect(rect.x, rect.y, rect.width, rect.height, this.gaugeBackColor());
this.drawText(this._text, rect.x, rect.y, rect.width, "center");
}
2.5 窗体展示
当前输入法窗体展示效果由标题场景改造(文件rpg_scenes.js
标题场景Scene_Title
)。由于使用简单,所以你可以在任意合适的场景按照下面的例子加入即可。
2.5.1:场景搭建时在最后加入输入法窗体this.createKeyboardWindow();
Scene_Title.prototype.create = function() {
Scene_Base.prototype.create.call(this);
this.createBackground();
this.createForeground();
this.createWindowLayer();
this.createCommandWindow();
this.createKeyboardWindow();//<-这里
};
Scene_Title.prototype.createKeyboardWindow = function() {
let win = new Window_Keyboard();
win.setChinese();//创建时设为中文输入模式
win.name = "keyboardWindow";
this._keyboardWin = win;
this.addChild(win);
}
2.5.2:在命令窗体创建时的函数createCommandWindow
中加入一条指令
this._commandWindow.setHandler('input', this.commandInput.bind(this));
,并实现其绑定的commandInput
函数, 也就是在点击该指令时的行为处理:
Scene_Title.prototype.createCommandWindow = function() {
this._commandWindow = new Window_TitleCommand();
this._commandWindow.setHandler('newGame', this.commandNewGame.bind(this));
this._commandWindow.setHandler('continue', this.commandContinue.bind(this));
//打开输入法窗体的指令'input'
this._commandWindow.setHandler('input', this.commandInput.bind(this));
this._commandWindow.setHandler('options', this.commandOptions.bind(this));
this.addWindow(this._commandWindow);
};
Scene_Title.prototype.commandInput = function() {
win = this._keyboardWin;
win.setChinese();
win.setCallWindow(this._commandWindow);
win.setText("测试文字");
win.setCallFun(this.keyboardOk.bind(this));
};
Scene_Title.prototype.keyboardOk = function(text) {
//控制台打印输入法窗体返回的文本;
console.log(text);
};
2.5.3:指令在窗体中的文本显示。
找到指令窗体(rpg_windows.js
文件Window_TitleCommand
窗体),在命令列表制作函数中加入数据:
Window_TitleCommand.prototype.makeCommandList = function() {
this.addCommand(TextManager.newGame, 'newGame');
this.addCommand(TextManager.continue_, 'continue', this.isContinueEnabled());
//显示的文本,指令需要和场景的一致为input
this.addCommand('测试内置键盘', 'input');
this.addCommand(TextManager.options, 'options');
};
2.5.4:界面样式(按键功能未实现版)
完成以上步骤后,就可以启动游戏查看效果了。
点击指令“测试内置键盘效果”,由于键盘是采用addChild
加入到场景的,所以是独立的一层,不会覆盖抹除下层的指令窗体,因此能透过键盘看到下层的窗体
2.6 按键的功能实现
这里只实现鼠标点击效果,实体键盘操作效果和按钮操作效果可用同样的方式实现;
2.6.1: 确认鼠标为有效操作,在update
中加入点击处理,缺少的函数isOpenAndActive
,isTouchedInsideFrame
,isContentsArea
可以在Window_Selectable中找到并且直接copy过来使用;
Window_Keyboard.prototype.update = function() {
Window_Base.prototype.update.call(this);
//处理鼠标点击
this.processTouch();
}
Window_Keyboard.prototype.processTouch = function() {
//当前窗体打开并活跃状态的校验
if (!this.isOpenAndActive()) return;
//进行了鼠标点击并且在窗体区域内
if (TouchInput.isTriggered() && this.isTouchedInsideFrame()) {
this.onTouch();
} else if (TouchInput.isCancelled()) {
this.terminate();
}
};
Window_Keyboard.prototype.onTouch = function() {
let x = this.canvasToLocalX(TouchInput.x)- this.padding,
y = this.canvasToLocalY(TouchInput.y)- this.padding;
let obj = null;
let rect = null;
if (this.isContentsArea(x, y)) {
let list = this.allKeys();
for (var i = 0; i < list.length; i++) {
rect = list[i];
if (x >= rect.x && y >= rect.y && x < rect.x2 && y < rect.y2) {
//找到点击的区域
obj = rect;
break;
}
}
}
if(obj){
SoundManager.playCursor();
//执行对应区域的效果
this.execKey(obj);
}
};
//右键处理关闭该窗体
Window_Keyboard.prototype.terminate = function(isCallBack) {
//当窗体点击确认按钮并且有回调函数时将当前窗体的文本返回;
if(isCallBack){
if(this._callFun){
this._callFun(this._text);
}
}
//当前窗体由其它窗体唤醒时,关闭当前窗体的同时唤醒那个窗体
if(this._callWindow){
this._callWindow.activate();
this._callWindow.show();
}
this.hide();
this.deactivate();
this.initMember();
this.setCursorRect(0,0,0,0);
}
2.6.2:确定当前点击为有效操作后找到对应的点击区域
按键处理分为三个部分:1.点击候选字区域,2。点击功能按钮区域,3.点击字母/数字按键区域;
Window_Keyboard.prototype.execKey = function(obj) {
//记录当前按下的键
this._oldKey = obj.key;
if(obj.isWord){//处理候选字
this.exec_Word(obj);
}else if(obj.isKey){//处理按键
this.exec_Key(obj);
}else if(obj.isButton){//处理按钮
this.exec_Button(obj);
}
//执行完后刷新窗体布局
this.refresh();
}
候选字区域:当候选字点击后,需要将选中的文本追加到输入框区域中,然后清空当前拼音输入
Window_Keyboard.prototype.exec_Word = function(obj) {
this._text += obj.key; //追加文本
this._phonetic = ""; //清空拼音
}
功能按钮区域:功能按键需要逐个处理
Window_Keyboard.prototype.exec_Button = function(obj) {
switch(obj.key){
case "切换":
//点击该按钮顺序切换输入模式
this.setMode((this._modeIndex + 1) % Window_Keyboard.BASEMODE.length);
break;
case "上一页":
//点击该按钮将候选字分页索引往前减少
this._wordsIndex -= 1;
this.updateWordData();
break;
case "下一页":
//点击该按钮将候选字分页索引往后增加
this._wordsIndex += 1;
this.updateWordData();
break;
case "删除":
//点击该按钮将如果有拼音就抹除拼音最后一个字符,没有拼音就抹除输入框最后一个字符
let text = this._phonetic;
if(text.length > 0){
this._phonetic = text.substring(0, text.length - 1);
this.createWordData();
}else if(this._text){
let text = this._text;
this._text = text.substring(0, text.length - 1);
}
break;
case "清空":
this._text = "";
this.clear();
break;
//以下按钮点击后都将关闭输入框窗体,传入一个参数用于后续操作
case "确认": this.terminate(true) ; break;
case "撤销": this.terminate(false); break;
}
}
字母/数字按键区域:当窗体的输入模式是中文时,此时的按键应追加到拼音变量中,同时去字典管理器获取匹配的所有文本数据,其它情况仅需将按键的文本值直接追加到输入框中即可
Window_Keyboard.prototype.exec_Key = function(obj) {
//是中文模式,并且当前点击的不是空格
if(this._modeIndex == Window_Keyboard.MODE_Zh_cn && obj.key !== " "){
this._phonetic += obj.key; //更新拼音
this.createWordData(); //刷新候选字集合
}
//是数字模式,并且锁定不可更改为其它模式
else if(this._modeIndex == Window_Keyboard.MODE_N && this._modeFixed) {
let re = "";
let newText = this._text + obj.key;
if(this._integer){
re = /^[0-9]*$/;
}else{
re = /^(-|+)?d+(.d+)?$/;
}
if(!re.test(newText)){//校验输入的文本是否合法
return;
}
this._text += obj.key;
this._text = Number(this._text).clamp(0, this._maxInt).toString();
}else{
this._text += obj.key;
}
}
3.结尾
到这里整个输入法窗体就完成了,在任意场景制作一个选项或者按钮按照步骤2.5的方式就能完成简单的调用。
暂无评论内容