主菜单及物品栏效果
浅语
RPG Maker MV中UI展示总是在对应的场景中进行操作及绘制的,通过UI和命令操作实现各种华丽的效果;今天带大家看看RPG Maker MV进行的仿仙剑主菜单UI显示和物品栏UI及功能。(后期大概率是会积极使用各种大神的插件实现需要的效果的!!!)
主菜单场景
各位可以看到现在的菜单有什么不和谐的地方对吧!下面多了一个窗口,且左边还有一个透明绿色的小方块;这是什么呢?
答案是选择框这就与我们对应的UI效果背道而驰了!所以下面将会对这里进行处理,优化下!
主菜单跳状态场景
原版RPG Maker MV中是不能直接跳转的,需要进行操作显示,需要进行操作哪个角色,选择后按空格或回车键跳转到对应人物的状态场景窗口,当然新仙剑1不是这样操作,我们仿其功能自然也要相像了;后期会根据自己的理解和需要添加自己的功能和效果,但那是很久的事了!!!
新的效果如图,有一点是比较坑的,是直接跳转是看不到对应的操作的。
还好通过调试能够窥见一二;具体的就不详细说说了,直接来看代码吧!
Scene_Menu.prototype.commandPersonal = function() {
SceneManager.push(Scene_Status);
//下面是需要被注释掉的区域
this._statusWindow.setFormationMode(false);
this._statusWindow.selectLast();
this._statusWindow.activate();
this._statusWindow.setHandler('ok', this.onPersonalOk.bind(this));
this._statusWindow.setHandler('cancel', this.onPersonalCancel.bind(this));
};
commandPersonal方法中是的第一行代码:SceneManager.push(Scene_Status);是将Scene_Status场景添加到场景管理器中,也就是进行场景的跳转;this._statusWindow.setFormationMode(false);是设置队形模式的;this._statusWindow.selectLast();是选择到默认选项,列表最后一个选项;this._statusWindow.activate();是激活状态菜单,毕竟只是显示是不够的,需要进行操作才行;this._statusWindow.setHandler(‘ok’, this.onPersonalOk.bind(this)); this._statusWindow.setHandler(‘cancel’, this.onPersonalCancel.bind(this));这两项是状态菜单的确认和取消操作,可以看做成上一步和下一步操作。由于我们只需要我们添加的SceneManager.push(Scene_Status);功能,因此其他功能项就可以注释或删除掉了。
当然这个还有点BUG就是由于是修改了这个方法的原因,其他的法术和装备操作也进行这个跳转,这是不对的,解决方式是什么呢,是新建一个方法将对应的操作指向这个方法去进行操作了!
物品场景
RPG Maker MV中原版中物品场景的菜单是这样的:
最上面一层是帮助信息菜单,显示的物品的介绍及功能,即物品的注释,告诉玩家该物品时做什么的。
第二层是物品种类选择菜单,可以从中选择查看及使用对应的物品,且有详细的分类。
第三层是物品菜单,即最终物品的使用及操作菜单,可以看到里面的物品有哪些,数量是多少等等。
隐藏菜单是人物的状态栏选项菜单,可以看到现在的人物简单信息,即操作选中人物使用物品。
由于显示问题的缘故可以看到最终的UI,图片和原来代码不对应导致的显示错乱。
背景处理
Scene_Item.prototype.createBackground = function() {
var ssbit= ImageManager.loadMenu(“Item_Skill”);//获取背景图片
this._backgroundSprite=new Sprite(ssbit);//将图片精灵给背景
this.addChild(this._backgroundSprite);//将图片给菜单场景子对象
};
这段代码是将原来的主菜单背景代码修改了场景名称和里面背景图片名称得到的。
使用的图片是这样的,现在就暂时不用截图游戏中的内容了,物品场景加上背景的样子大家应该也想象的到。
融合物品选择栏直接显示所有物品
原版的RPG Maker MV中是四个窗口,现在发现要是用默认(自动)命令,效果不好,因此去掉选择物品的类型的窗口,直接进入物品窗口这样可以省很多麻烦,后期若是需要和现代游戏UI风格看齐是可以重新添加的!
融合物品选择栏
Pal_Scene_Item.prototype.create = function() {
Scene_ItemBase.prototype.create.call(this);
this.createHelpWindow();
this.createActorWindow();
this.createItemWindow();
};
物品的场景就精简了一个窗口,但是就这样是不行的,this.createItemWindow()方法中有对createCategoryWindow 创建的窗口的调用,因此需要修改!
Pal_Scene_Item.prototype.createItemWindow = function() {
var wy = 180;//Y轴是暂时定义的
var wh = Graphics.boxHeight - wy;
this._itemWindow = new Window_ItemList(0, 100, Graphics.boxWidth, wh);
this._itemWindow.setHelpWindow(this._helpWindow);
this._itemWindow.setHandler('ok', this.onItemOk.bind(this));
this._itemWindow.setHandler('cancel', this.popScene.bind(this));
this._itemWindow.setCategory('item');
this._itemWindow.activate();
this._itemWindow.selectLast();
this.addWindow(this._itemWindow);
};
setHandler(‘cancel’, this.popScene.bind(this))方法是确定可以直接退出该场景的!setCategory(‘item’)方法是设置现在默认使用的选项,这样才会正常显示物品。activate()激活窗口。selectLast()默认选择第一个物体。
显示所有物品
现在可以看到显示了所有的物品!
Window_ItemList.prototype.makeItemList = function() {
this._data = $gameParty.allItems();
if (this.includes(null)) {
this._data.push(null);
}
};
原版的代码进行了物品的筛选过滤,用以确认物品确实在对应的类别中!精简后就直接给到数据就行!$gameParty.allItems()底层是用了一个list进行连接三个数组,并返回出来,这样就直接得到我们需要的东西了!
物品菜单及说明菜单的UI美化
菜单的美化还是挺重要的,得赏心悦目才行…
说明菜单美化
美化的说明菜单,说明菜单的显示文字的背景使它贴近右边和上边,这样既能较完整的显示底图,又不会太过占据位置;文字部分现在的文字黑字不太适应,暂时用这个蓝字,后期会用偏黑的蓝字,大体和新仙剑看齐。
Window_ItemSkill_Help.prototype.refresh = function() {
this.contents.clear();
this.drawHelpBackground();
this.contents.fontSize=16;
this.changeTextColor(this.textColor(22));
this.contents.outlineColor=this.textColor(22);
this.contents.outlineWidth = 0;
var lineHeight=18;var ss=this._item.description;
for(var i=0;i<Math.ceil(ss.length/19);i++){
this.drawText(ss.slice(19*i,19*i+19), 195, 35+i*18, 300, 'left');
}
this.drawItemImage();
};
Window_ItemSkill_Help.prototype.drawHelpBackground = function() {
var bitmap = this.itemAndSillkHelp;var sw = 640;
var sh = 192;var dx = 13;var dy = -20;
var sx = 0;var sy = 0;
this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy);
};
Window_ItemSkill_Help.prototype.drawItemImage = function() {
var bitmap = null;var sw = 236;var sh = 148;
var dx = -45;var dy = 2;var sx = 0;var sy = 0;
if(DataManager.isItem(this._item)){
bitmap= this.itemsImage;
sx = this._item.iconIndex % 14 * sw;
sy = Math.floor(this._item.iconIndex / 14) * sh;
}else if(DataManager.isWeapon(this._item)){
bitmap= this.weaponsImage;
sx = this._item.iconIndex % 7 * sw;
sy = Math.floor(this._item.iconIndex / 7) * sh;
}
else if(DataManager.isArmor(this._item)){
bitmap= this.armorsImage;
sx = this._item.iconIndex % 14 * sw;
sy = Math.floor(this._item.iconIndex / 14) * sh;
}
this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy);
};
原版代码中文字换行是直接用19个字符数量来界定的(初始做的自动换行,原游戏中是没有自动换行的,手动换行后再写入也是没有换行操作的),但弊端很显然宽度不一致,导致文字会很出戏,毕竟这不是在写word文档;图片加载由于引擎自身的缺陷也是有时加载不上。
现在通过网上各位大佬的作业,抄下来现在解决了该问题(后期可能进行再优化执行效率)。
图片加载:使用了B站UP主 “似阿波” 的代码思路,由于RPG Maker MV和MZ大部分代码是相似的(兄弟软件),因此很多地方是可以找到相同代码逻辑并执行的。
提前加载:【RPG Maker MZ】学UI代码跟做菜一样 #4 主菜单
延迟加载:【RPG Maker MZ】边打星铁边做游戏 #5 背包格子
文字自动换行功能:
var lineHeight=18;//文字高度
var helpText=this._item.description;//读取的物品信息
var boxmaxWidth=304;//容器的宽度
var contentWidth=0;//每段文本宽度
var lastStrIndex=0;//每次开始截取的字符串的索引
//简单自动换行 自己先前写的自动换行
/*for(var i=0;i<Math.ceil(ss.length/19);i++){
this.drawText(ss.slice(19*i,19*i+19), 195, 35+i*18, 300, 'left');
}*/
if(this.textWidth(helpText)<=boxmaxWidth){
this.drawText(helpText, 195, 35, 300, 'left');
}else{
var line=0;
for(var i=0;i<helpText.length;i++){
contentWidth+=this.textWidth(helpText[i]);
if(contentWidth>boxmaxWidth){
this.drawText(helpText.substring(lastStrIndex,i), 195, 35+line*lineHeight, 300, 'left');
line++;
contentWidth=0;
lastStrIndex=i;
}
if(i==helpText.length-1){
this.drawText(helpText.substring(lastStrIndex,i+1), 195, 35+line*lineHeight, 300, 'left');
}
}
}
引用自 : fengzhiguanS 文章中的内容 canvas之文字换行
物品菜单美化
现在的物品菜单还是挺简陋的,不管是背景图还是其他都没有因此需要弄出来。
当然这些都得分步骤前来弄!
1. 确定帮助菜单中显示技能消耗值的位置
根据原版游戏位置可以得到该信息,因此先进这部分的美化
bitmap=ImageManager.loadSystem('ItemAndSillkNum');
var dw=165;var dh=85;
sw=152;sh=76;dx=Graphics.boxWidth-dw;dy=127;
//由于加载问题因此需要使用延迟加载图片
if(!bitmap.isReady()){
setTimeout(()=>{this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy,dw,dh)},15);
}else{
this.contents.blt(bitmap, sx, sy, sw, sh, dx, dy,dw,dh);
}
这部分代码是在文字背景中绘制的,相当于是另一个背景,同时也扩展了帮助菜单的高度,为了显示不起冲突因此将在场景中的添加到window中变成添加到场景子项中。
2. 给物品菜单添加上背景
由于为了符合原游戏的层级和操作,且处理窗口绘制时背景加载问题,因此将技能消耗值的背景放在了场景背景中绘制。
和原版游戏有区别很正常,因为精灵不能进行缩放因此和原游戏有区别,而我又不想修改图片文件。
var itemBackground= ImageManager.loadSystem("ItemAndSkillBg");//获取背景图片
this._backgroundSprite=new Sprite(itemBackground);//将图片精灵给背景
this._backgroundSprite.x=(640-624)/2;
this._backgroundSprite.y=118;
this._backgroundSprite.setFrame(0, 0, 624, 320);
this.addChild(this._backgroundSprite);//将图片给菜单场景子对象
var itemHelpBackground= ImageManager.loadSystem("ItemAndSillkNum");//获取背景图片
this._backgroundSprite=new Sprite(itemHelpBackground);//将图片精灵给背景
this._backgroundSprite.x=640-152;
this._backgroundSprite.y=115;
this._backgroundSprite.setFrame(0, 0, 152, 76);
this.addChild(this._backgroundSprite);//将图片给菜单场景子对象
现在的物品栏它的背景没有更新,不管如何动,始终修改的只是现在显示的箭头,Windows图片上的箭头上我添加了蓝色和红色小方块加以区分,现在需要进行这部分的修改。
Window_Selectable.prototype.updateArrows = function() {
var topRow = this.topRow();
var maxTopRow = this.maxTopRow();
this.downArrowVisible = maxTopRow > 0 && topRow < maxTopRow;
this.upArrowVisible = topRow > 0;
};
这里是窗口更新箭头的方法,里面的downArrowVisible属性和upArrowVisible 属性由Window类创建,方法的作用是确定上下箭头是否显示的。
父类中有个同名加_的方法作用是赋值给父类的属性。
最后用refresh方法刷新箭头显示,这里将Window改成了Window_ItemLis,使只在它的里面使用然后注释掉this._refreshArrows()方法。
Window_ItemList.prototype._refreshAllParts = function() {
this._refreshBack();
this._refreshFrame();//更新框架
this._refreshCursor();//更新光标
this._refreshContents();
//this._refreshArrows();更新箭头
this._refreshPauseSign();
};
这三张图片显示的就是最终的效果,需要在场景中修改图片的位置参数。
Pal_Scene_Item.prototype.update = function() {
Scene_ItemBase.prototype.update.call(this);
if(this._itemWindow.downArrowVisible==true&&this._itemWindow.upArrowVisible==true){
this._itemBackgroundSprite.setFrame(0, 960, 624, 320);
}else if(this._itemWindow.downArrowVisible==true&&this._itemWindow.upArrowVisible==false){
this._itemBackgroundSprite.setFrame(0, 640, 624, 320);
}else if(this._itemWindow.downArrowVisible==false&&this._itemWindow.upArrowVisible==true){
this._itemBackgroundSprite.setFrame(0, 320, 624, 320);
}else{
this._itemBackgroundSprite.setFrame(0, 0, 624, 320);
}
};
里面的判断条件就是上下箭头是否显示,由于源代码中其实已经有了这个判断箭头显示的代码,因此借用它的值就行。做下整体的判断修改图片中获取部分的y轴的值即可。
3. 设置调整物品栏文字操作显示
经过不懈的努力这个效果是出来了!!!
经过这个效果的开发,确定了还是得对底层的代码多熟悉才行,不然很难达到需要的效果。
效果截图不分先后直接欣赏即可。对应的代码是哪些呢?
Window.prototype._createAllParts = function() {
this._windowSpriteContainer = new PIXI.Container();
this._windowBackSprite = new Sprite();
this._windowCursorSprite = new Sprite();//光标
this._windowFrameSprite = new Sprite();//框架
this._windowContentsSprite = new Sprite();//内容
this._downArrowSprite = new Sprite();//向下箭头
this._upArrowSprite = new Sprite();//向上箭头
this._windowPauseSignSprite = new Sprite();//暂停标志
this._windowBackSprite.bitmap = new Bitmap(1, 1);
this._windowBackSprite.alpha = 192 / 255;
this.addChild(this._windowSpriteContainer);
this._windowSpriteContainer.addChild(this._windowBackSprite);
this._windowSpriteContainer.addChild(this._windowFrameSprite);
this.addChild(this._windowContentsSprite);//内容
this.addChild(this._windowCursorSprite);//光标
this.addChild(this._downArrowSprite);
this.addChild(this._upArrowSprite);
this.addChild(this._windowPauseSignSprite);
};
这是RPG Maker MV中的源代码,这里面最重要的是精灵的顺序,精灵在Window的数组中,按顺序进行绘制的,这里我将我需要的窗体部分的内容和光标顺序进行了调换。
Window_ItemList.prototype._refreshCursor = function() {
var pad = this._padding;
var x = this._cursorRect.x;
var y = this._cursorRect.y;
var w = this._cursorRect.width;
var h = this._cursorRect.height;
var bitmap = new Bitmap(w, h);
this._windowCursorSprite.bitmap = bitmap;
this._windowCursorSprite.setFrame(0, 0, w, h);
this._windowCursorSprite.move(x+pad, y+pad);
if (w > 0 && h > 0) {
this.drawChangeItem();
}
};
这是进行光标的创建和刷新的,相比源代码进行了一些变更,直接创建一个位图,并放到对应的位置。
Window_ItemList.prototype.drawChangeItem = function() {
this._windowCursorSprite.bitmap.clear();
var item = this._data[this.index()];
if (item) {
var numberWidth = this.numberWidth();
this._windowCursorSprite.bitmap.fontSize=20;
this._windowCursorSprite.bitmap.outlineWidth = 1;
this._windowCursorSprite.bitmap.fontFace="宋体";
this.changePaintColor2(this.isEnabled(item));
this._windowCursorSprite.bitmap.drawText(item.name, 0, 0-1, 172- numberWidth, this.lineHeight());
if (this.needsNumber()) {
this._windowCursorSprite.bitmap.drawText('x', 0, 0-1, this._width - this.textWidth('000'), 'right');
this._windowCursorSprite.bitmap.drawText($gameParty.numItems(item), 0, 0-1, this._width, 'right');
}
}
};
Window_ItemList.prototype.changePaintColor = function(enabled) {
var textColor= enabled ? this.palTextColor(4) : this.palTextColor(6);
this.contents.outlineColor =textColor;
this.changeTextColor(textColor);
};
Window_ItemList.prototype.changePaintColor2 = function(enabled) {
var textColor= enabled ? this.palTextColor(7): this.palTextColor(5);
this._windowCursorSprite.bitmap.outlineColor =textColor;
this._windowCursorSprite.bitmap.textColor=textColor;
};
这里新修改了changePaintColor方法的具体操作,并以此新建了一个changePaintColor2方法,用来处理文字的颜色。
drawChangeItem方法是用来绘制对应光标的,其和文字绘制是一样的原理,同时由于是一个位图,对其进行操作时,里面内容的是以位图为主体进行定位的。
Window_ItemList.prototype._updateCursor = function() {
this._windowCursorSprite.bitmap.clear();
var blinkCount = this._animationCount % 80;
var item = this._data[this.index()];
var textColor=this._windowCursorSprite.bitmap.outlineColor;
var numberWidth = this.numberWidth();
if (this.active) {
if (this.isEnabled(item)) {
switch(Math.floor(blinkCount/20)){
case 0:
textColor=this.palTextColor(7);
break;
case 1:
textColor=this.palTextColor(9);
break;
case 2:
textColor=this.palTextColor(7);
break;
case 3:
textColor=this.palTextColor(8);
break;
default:
textColor=this.palTextColor(4);
break;
}
} else {
textColor=this.palTextColor(5);
}
}
this._windowCursorSprite.bitmap.outlineColor =textColor;
this._windowCursorSprite.bitmap.textColor=textColor;
this._windowCursorSprite.bitmap.drawText(item.name, 0, 0-1, 172- numberWidth, this.lineHeight());
if (this.needsNumber()) {
this._windowCursorSprite.bitmap.drawText('x', 0, 0-1, this._width - this.textWidth('000'), 'right');
this._windowCursorSprite.bitmap.drawText($gameParty.numItems(item), 0, 0-1, this._width, 'right');
}
this._windowCursorSprite.visible = this.isOpen();
};
在光标的更新中,清理之前绘制的内容,并计算每隔一定时间进行的处理操作,并修改光标文字颜色,使其能够进行变化。
本篇暂时结束,总的内容太多,太长了,阅读效果不太好,下一篇是 主菜单及物品栏效果展示二,展示物品菜单中人物部分显示和特效操作。