仙剑主菜单中设置菜单
在新仙剑1中主菜单中包含物品菜单和设置菜单,其中设置菜单主要是处理音乐和音效及存档、读取及退出的功能的。
音乐及音效
在RPG Maker MV中是分为BGM、BGS、EM、ES这四类的。
- BGM(Back Ground Music)背景音乐。
可以在一个场景中播放的音频文件,通常,BGM为一段提前设计好的音乐作品; - BGS(Back Ground Sound)背景声音。
类似于BGM,一般由一个通常很短的音效重复制作而成,循环播放。 - SE(Sound Effect)音效。
只播放一次的简短音频。如开门声、坠落声等; - ME(Musical Effect)音乐效果。
一个只播放一次的短音频文件,一般是一小段音乐,不代表一个特定的动作。如转场音乐、战斗胜利后的气氛烘托。
BGM和BGS的区别:BGM主要用于情感基调的渲染,持续时间一般较长,作品比较长的情况,BGM可以根据实际需求分为章节和段落等;BGS主要用于环境的渲染,持续时间小于等于BGM,一般由可重复的音效搭配组成,如打雷声、风声、鸟叫等。
在实际应用过程中,SE和ME有时不会做特别区分,如转场,可用音效也可以用音乐,以上四个概念可为后期制作提供一个分类依据。
以上内容引用自:百度中 天歌有声 的 后期制作中BGM、BGS、SE、ME的含义 的文章中,这样的解释其实是挺精准的,比RPG Maker的文档还精确。
代码及效果
function Window_MenuOptionsCommand() {
//初始化
this.initialize.apply(this, arguments);
}
//复制原型链
Window_MenuOptionsCommand.prototype = Object.create(Window_Command.prototype);
Window_MenuOptionsCommand.prototype.constructor = Window_MenuOptionsCommand;
//初始化内容
Window_MenuOptionsCommand.prototype.initialize = function(x, y) {
Window_Command.prototype.initialize.call(this, x, y);
//this.hide();
this.deactivate();
this.deselect();
this.refresh();
this.bgms=0;
this.mse=0;
}
//底部命令符号
Window_MenuOptionsCommand._lastCommandSymbol = null;
//初始命令
Window_MenuOptionsCommand.initCommandPosition = function() {
this._lastCommandSymblo = null;
}
//窗口宽度
Window_MenuOptionsCommand.prototype.windowWidth = function() {
return 200;
};
//可见行数
Window_MenuOptionsCommand.prototype.numVisibleRows = function() {
return this.maxItems();
};
//创建命令列表
Window_MenuOptionsCommand.prototype.makeCommandList = function() {
this.initCommand();
this.addSystemCommand();
};
//添加系统命令
Window_MenuOptionsCommand.prototype.addSystemCommand = function() {
this.addSaveCommand();
this.addLoadCommand();
this.addBGSMCommand();
this.addSMeCommand();
//this.addOptionsCommand();
this.addGameEndCommand();
}
//初始化命令
Window_MenuOptionsCommand.prototype.initCommand = function(){
this.changeValue("alwaysDash", true);
this.changeValue("commandRemember", true);
}
//保存
Window_MenuOptionsCommand.prototype.addSaveCommand = function() {
this.addCommand(TextManager.save, 'save');
};
//读取
Window_MenuOptionsCommand.prototype.addLoadCommand = function() {
this.addCommand(TextManager.continue_, 'continue');
};
//音乐
Window_MenuOptionsCommand.prototype.addBGSMCommand = function() {
this.addCommand("背景音乐及环境音效", 'bgmsVolume');
};
//音效
Window_MenuOptionsCommand.prototype.addSMeCommand = function() {
this.addCommand("按键音效及事件音效", 'mseVolume');
};
//游戏结束
Window_MenuOptionsCommand.prototype.addGameEndCommand = function() {
this.addCommand(TextManager.gameEnd, 'gameEnd');
};
//确定触发
Window_MenuOptionsCommand.prototype.processOk = function() {
Window_MenuOptionsCommand._lastCommandSymbol = this.currentSymbol();
Window_Command.prototype.processOk.call(this);
};
//光标向右(键盘Right)
Window_MenuOptionsCommand.prototype.cursorRight = function(wrap) {
var index = this.index();
var symbol = this.commandSymbol(index);
if(symbol==="bgmsVolume"){
var value = this.getConfigValue("bgmVolume");
value += this.volumeOffset();
value = value.clamp(0, 100);
this.changeValue("bgmVolume", value);
this.changeValue("bgsVolume", value);
this.bgms=value;
}else if(symbol==="mseVolume"){
var value = this.getConfigValue("meVolume");
value += this.volumeOffset();
value = value.clamp(0, 100);
this.changeValue("meVolume", value);
this.changeValue("seVolume", value);
this.mse=value;
}
};
//光标向左(键盘Left)
Window_MenuOptionsCommand.prototype.cursorLeft = function(wrap) {
var index = this.index();
var symbol = this.commandSymbol(index);
if(symbol==="bgmsVolume"){
var value = this.getConfigValue("bgmVolume");
value -= this.volumeOffset();
value = value.clamp(0, 100);
this.changeValue("bgmVolume", value);
this.changeValue("bgsVolume", value);
this.bgms=value;
}else if(symbol==="mseVolume"){
var value = this.getConfigValue("meVolume");
value -= this.volumeOffset();
value = value.clamp(0, 100);
this.changeValue("meVolume", value);
this.changeValue("seVolume", value);
this.mse=value;
}
};
//选择列表
Window_MenuOptionsCommand.prototype.selectLast = function() {
this.selectSymbol(Window_MenuOptionsCommand._lastCommandSymbol);
};
Window_MenuOptionsCommand.prototype.setup = function() {
this.clearCommandList();
this.makeCommandList();
this.refresh();
this.show();
this.select(0);
this.activate();
this.open();
};
//获取配置的值
Window_MenuOptionsCommand.prototype.getConfigValue = function(symbol) {
return ConfigManager[symbol];
};
//设置配置的值
Window_MenuOptionsCommand.prototype.setConfigValue = function(symbol, volume) {
ConfigManager[symbol] = volume;
};
//改变量 每次10
Window_MenuOptionsCommand.prototype.volumeOffset = function() {
return 10;
};
//改变值
Window_MenuOptionsCommand.prototype.changeValue = function(symbol, value) {
var lastValue = this.getConfigValue(symbol);
if (lastValue !== value) {
this.setConfigValue(symbol, value);
this.redrawItem(this.findSymbol(symbol));
SoundManager.playCursor();
}
};
这里面是定义了一个设置窗口的类和里面的具体功能,通过注释是可以知道里面的具体的功能的,这里面,我将BGM和BGS看做一体,ME和SE看做一体来操作,这样比较符合仙剑的音量控制的模块操作。同时类中定义了两个变量,用于存储修改后的音量值。
Scene_Menu.prototype.loadingOptionsVolumeSprite=function(){
var sprite = new Sprite();
this.addChild(sprite);
sprite.bitmap = new Bitmap(192,224);
sprite.setFrame(0,0,192,224);
sprite.move(71,49);
sprite.visible=false;
}
Scene_Menu.prototype.commandOptionsWindowImageUpdate = function() {
this.children[13].bitmap.clear();
//系统子菜单更新
var scmdOptionsButtons= this._OptionsButtons;
for(var i in scmdOptionsButtons){
this._OptionsButtons[i].visible=false;
}
if(this._OptionsButtons[this._optionsCommandWindow.index()]){
this._OptionsButtons[this._optionsCommandWindow.index()].visible=true;
}
this.children[13].bitmap.blt(ImageManager.loadMenu("Volume"),0,0,24,24,82+64*this._optionsCommandWindow.bgms*0.01,101);
this.children[13].bitmap.blt(ImageManager.loadMenu("Volume"),0,0,24,24,82+64*this._optionsCommandWindow.mse*0.01,133);
};
主菜单场景中,添加了一个新的精灵,这个精灵宽高和设置菜单一样,这样方便里面内容绘制定位,然后将x,y定义和设置菜单一样,并进行隐藏。
在update方法中由于调用了commandOptionsWindowImageUpdate 方法更新精灵显示和隐藏,因此在这个方法中获取了新添加的精灵,并清理绘制的内容。然后在最下面绘制新的按钮,并设置好对应的位置,其中x的值是初始位置+需要移动的距离每次修改的值0.01,这样可以算出移动的精确的百分比,当然小数点这里暂时不进行处理。
通过这个图,至少这方面还挺满意的。
退出
现在为什么不先做存储和读取呢?
- 存储和读取两个操作是完全一样的界面。
- 存储和读取本质上是比退出要难得;得先易后难嘛。
- 做好这个后可以考虑去做标题菜单的对应操作了。
退出操作是在退出的场景中进行的,因此现在得先找到这个场景并提取出退出菜单,放到主菜单场景中,好了开始操作。
代码及效果
function Pal_Window_GameEnd() {
this.initialize.apply(this, arguments);
}
Pal_Window_GameEnd.prototype = Object.create(Window_Command.prototype);
Pal_Window_GameEnd.prototype.constructor = Pal_Window_GameEnd;
Pal_Window_GameEnd.prototype.initialize = function(x,y,width,height,spacing) {
Window_Command.prototype.initialize.call(this, x, y);
this.width=width;
this.height=height;
this.margin=0;
this.spacing=spacing;
this.isTrue=ImageManager.loadSystem("istrue");
this.updatePlacement();
this.deactivate();
this.deselect();
};
Pal_Window_GameEnd.prototype.standardPadding = function() {
return 0;
};
Pal_Window_GameEnd.prototype.maxCols = function() {
return 2;
};
Pal_Window_GameEnd.prototype.spacing = function() {
return 5;
};
Pal_Window_GameEnd.prototype.itemRect = function(index) {
var rect = new Rectangle();
var maxCols = this.maxCols();
rect.width = this.itemWidth();
rect.height = this.itemHeight();
rect.x = index % maxCols * (rect.width + this.spacing) - this._scrollX;
rect.y = Math.floor(index / maxCols) * rect.height - this._scrollY;
return rect;
};
Pal_Window_GameEnd.prototype.itemWidth = function() {
return Math.floor((this.width - this.padding * 2 +
this.spacing) / this.maxCols() - this.spacing);
};
Pal_Window_GameEnd.prototype.updatePlacement = function() {
if(this.x==0&&this.y==0){
this.x = (Graphics.boxWidth - this.width) / 2;
this.y = (Graphics.boxHeight - this.height) / 2;
}
};
Pal_Window_GameEnd.prototype.makeCommandList = function() {
this.addCommand('', 'toTitle');
this.addCommand('', 'cancel');
};
Pal_Window_GameEnd.prototype._refreshCursor = function() {
var pad = this._padding;
var x = this._cursorRect.x + pad - this.origin.x;
var y = this._cursorRect.y + pad - this.origin.y;
var w = this._cursorRect.width;
var h = this._cursorRect.height;
var x2 = Math.max(x, pad);
var y2 = Math.max(y, pad);
var bitmap = new Bitmap(this.width, 80);
this._windowCursorSprite.bitmap = bitmap;
this._windowCursorSprite.setFrame(0, 0, this.width, 80);
if (w > 0 && h > 0 && this.isTrue) {
var skin = this.isTrue;
if(this.index()==1){
bitmap.blt(skin, 80, 0, 80, 80, 0, 0, 80, 80);
bitmap.blt(skin, 0, 80, 80, 80, this.width-80, 0, 80, 80);
}else{
bitmap.blt(skin, 0, 0, 80, 80, 0, 0, 80, 80);
bitmap.blt(skin, 80, 80, 80, 80, this.width-80, 0, 80, 80);
}
}
};
Pal_Window_GameEnd.prototype._updateCursor = function() {
};
统一说明下,更新光标代码由于不需要光标动态效果因此去掉对应多余代码。创建光标代码修改为在指定区域绘制两个图片,游戏中对应就是根据选中状态显示是否。创建对应绘制矩形框和宽度代码中直接使用外部传递过来的间隔的值。创建命令选项部分,去除了文字的显示。更新x,y轴部分确定是不是0,不是就用原值,是就直接居中。之后初始化是将内外边距设置为0,透明度设置为0,获取外部的宽高,获取光标绘制的图片。由于场景之中的创建和跳转代码千篇一律,因此就不放出来了。
存取档
存取档,通过查看源代码,发现就使用了一个帮助菜单和一个存档的菜单。在file场景中进行创建,之后Save和Load场景继承了file场景进行各自的操作,因此现在需要做的就是将存取档的场景功能融合进主菜单场景中,并将存取档UI进行修改。
此存取档的UI操作可以对应到标题界面的读档,一样的UI可以直接复用,创建菜单。
现在阶段存取档的操作只需要针对它的全局保存信息存档操作即可。
代码及效果
Pal_Window_SavefileList.prototype.initialize = function(x, y, width, height) {
Window_Selectable.prototype.initialize.call(this, x, y, width, height);
this.activate();
this.margin=0;
this.saveAndLoaded=ImageManager.loadSystem("SaveAndLoaded");
this.slNumber=ImageManager.loadSystem("59-1");
this._mode = null;
this.deactivate();
this.deselect();
this.hide();
this._windowCursorSprite.opacity =100;
this._windowCursorSprite.setFrame(0,0,400,480);
this._windowCursorSprite.bitmap=new Bitmap(400,480);
};
//设置模式
Pal_Window_SavefileList.prototype.setMode = function(mode) {
this._mode = mode;
};
//最大项目
Pal_Window_SavefileList.prototype.maxItems = function() {
return DataManager.maxSavefiles();
};
//最大可见项目
Pal_Window_SavefileList.prototype.maxVisibleItems = function() {
return 5;
};
Pal_Window_SavefileList.prototype.standardPadding = function() {
return 0;
};
//文字内边距
Pal_Window_SavefileList.prototype.textPadding = function() {
return 0;
};
//项目高度
Pal_Window_SavefileList.prototype.itemHeight = function() {
var innerHeight = this.height - this.padding * 2;
return Math.floor(innerHeight / this.maxVisibleItems());
};
Pal_Window_SavefileList.prototype.drawItem = function(index) {
var id = index + 1;
var valid = DataManager.isThisGameFile(id);//判断是不是游戏文件
var info = DataManager.loadSavefileInfo(id);//加载保存文件信息
var rect = this.itemRectForText(index);//项目矩形文本
if (info) {
this.drawItemBackground(1,rect.x, rect.y);
this.drawFileId(id, rect.x, rect.y);
this.drawContents(info, rect, valid);
}else{
this.drawItemBackground(2,rect.x, rect.y);
this.drawFileId(id, rect.x, rect.y);
}
};
//绘制项目背景
Pal_Window_SavefileList.prototype.drawItemBackground = function(index, x, y) {
this.contents.blt(this.saveAndLoaded, 0, index*96, 400, 96, x, y,400,95);
};
//绘制文件ID
Pal_Window_SavefileList.prototype.drawFileId = function(id, x, y) {
this.contents.blt(this.slNumber, 0, 0, 60, 32, 331, 14+y,60,31);
this.drawFileIdText(id,y);
};
//绘制光标遮罩
Pal_Window_SavefileList.prototype.drawItemShade = function(index, x, y) {
this._windowCursorSprite.bitmap.blt(this.saveAndLoaded, 0, index*96, 400, 96, x, y,400,95);
};
//绘制文件ID文字
Pal_Window_SavefileList.prototype.drawFileIdText = function(id, y) {
this.contents.outlineWidth = 1;
this.contents.fontSize=12;
this.contents.fontFace="sans-serif";
this.changeTextColor(this.palTextColor(14));
this.drawText(id-1, 350, 13+y, 22,'center');
};
//绘制内容
Pal_Window_SavefileList.prototype.drawContents = function(info, rect, valid) {
this.drawMapImage(info,rect.x,rect.y);
this.drawMapName(info,rect.x,rect.y,82);
this.drawPartyFaces(info,rect.x,rect.y);
this.drawGameInfo(info,rect.x+300,rect.y);
};
//绘制地图场景
Pal_Window_SavefileList.prototype.drawMapImage = function(info, x, y) {
if (info.mapSprite) {
var mapImg=Bitmap.load(info.mapSprite);
if(!mapImg.isReady()){
setTimeout(()=>{
this.contents.blt(mapImg,0,0,80,60,x+37,y+14);
},1);
}else{
this.contents.blt(mapImg,0,0,80,60,x+37,y+14);
}
}
};
//绘制地图名称
Pal_Window_SavefileList.prototype.drawMapName = function(info, x, y, width) {
if (info.mapName) {
this.contents.fontFace="宋体";
this.contents.fontSize=12;
this.changeTextColor(this.palTextColor(-1));
this.drawText(info.mapName, x+37, y+67, width,'center');
}
};
//绘制人物头像
Pal_Window_SavefileList.prototype.drawPartyFaces = function(info, x, y) {
if (info.characters) {
for (var i = 0; i < info.characters.length; i++) {
var data = info.characters[i];
this.drawPalFace(data[0],x+i*49,y);
}
}
};
Pal_Window_SavefileList.prototype.drawPalFace = function(characterName,x,y) {
var bitmap = ImageManager.loadSystem(characterName);
this.contents.blt(bitmap, 0, 0, 48, 60, x+126, y+16);
};
//绘制游戏信息
Pal_Window_SavefileList.prototype.drawGameInfo = function(info, x, y) {
this.contents.fontSize=13;
this.contents.outlineColor=this.palTextColor(-1);
this.drawText(info.No1Level, x, y+8, 20, 'left');
this.drawText(info.gold, x, y+31, 66, 'left');
this.drawText(info.playtime, x, y+54, 66, 'left');
};
//游戏确认音效
Pal_Window_SavefileList.prototype.playOkSound = function() {
};
Pal_Window_SavefileList.prototype._refreshCursor = function() {
this._windowCursorSprite.bitmap.clear();
for (var i = 0; i < 100; i++) {
if(i==this.index()){
continue;
}else{
var rect = this.itemRectForText(i);//项目矩形文本
this._windowCursorSprite.bitmap.blt(this.saveAndLoaded, 0, 0*96, 400, 96, rect.x, rect.y,400,95);
}
}
};
Pal_Window_SavefileList.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);
};
Pal_Window_SavefileList.prototype._updateCursor = function() {
};
Pal_Window_SavefileList.prototype._refreshAllParts = function() {
this._refreshBack();
this._refreshFrame();//更新框架
//this._refreshCursor();//更新光标
this._refreshContents();
this._refreshPauseSign();
};
存档窗口的代码,其中更新光标这部分注释掉了,由于每次操作时会自动调用,因此功能不会出现问题,这里的注释掉是不让在窗口创建前调用。
//最大游戏文件
DataManager.maxSavefiles = function() {
return 100;
};
//创建保存文件信息
DataManager.makeSavefileInfo = function() {
var info = {};
info.globalId = this._globalId;
info.title = $dataSystem.gameTitle;
info.mapName = $dataMap.displayName;
info.characters = $gameParty.charactersForSavefile();
info.No1Level = $gameParty.targetActor()._level;
info.gold = $gameParty.gold();
info.faces = $gameParty.facesForSavefile();
info.playtime = $gameSystem.playtimeText();//游戏时长
info.timestamp = Date.now();//时间戳
var mapBitmap = new Bitmap(80,60);
mapBitmap.blt(SceneManager.backgroundBitmap(),0,0,640,480,0,0,80,60);
info.mapSprite =mapBitmap.canvas.toDataURL();
return info;
};
SceneManager.snapForBackground = function() {
this._backgroundBitmap = this.snap();
};
SceneManager.backgroundBitmap = function() {
return this._backgroundBitmap;
};
这边在工具类重订了存档的数量,处理全局保存信息的数据,截图了需要保存的场景地图场景并存储,这里设定了截图不用进行模糊化处理。
Scene_Menu.prototype.createFileListWindow = function() {
var width =400;
var height = Graphics.boxHeight;
this._listWindow = new Pal_Window_SavefileList(Graphics.boxWidth-width, 0, width, height);
this._listWindow.setHandler('ok', this.onSavefileOk.bind(this));
this._listWindow.setHandler('cancel', this.onFileCommandCancel.bind(this));
this._listWindow.select(0);
this._listWindow.setTopRow(0);
this._listWindow.setMode(null);
this._listWindow.changeTransparent();
this._listWindow.refresh();
this.addWindow(this._listWindow);
};
Scene_Menu.prototype.commandOptionsWindowImageUpdate = function() {
...
if(this._OptionsButtons[this._optionsCommandWindow.index()]){
this._OptionsButtons[this._optionsCommandWindow.index()].visible=true;
if(this._listWindow.visible){
this._OptionsButtons[this._optionsCommandWindow.index()].move(25,254);
this.children[13].move(25,254);
}else{
this._OptionsButtons[this._optionsCommandWindow.index()].move(71,49);
this.children[13].move(71,49);
}
}
...
};
Scene_Menu.prototype.commandSave = function() {
this._optionsCommandWindow.reselect();
this._listWindow.select(DataManager.lastAccessedSavefileId() - 1);
this._listWindow.setTopRow(DataManager.lastAccessedSavefileId() - 3);
this._listWindow.setMode("save");
this._listWindow.show();
this._listWindow.activate();
};
//打开读取菜单
Scene_Menu.prototype.commandContinue = function() {
this._optionsCommandWindow.reselect();
this._listWindow.select(DataManager.latestSavefileId() - 1);
this._listWindow.setTopRow(DataManager.latestSavefileId() - 3);
this._listWindow.setMode("load");
this._listWindow.show();
this._listWindow.activate();
};
Scene_Menu.prototype.onFileCommandCancel = function() {
this._listWindow.setMode(null);
this._listWindow.deselect();
this._listWindow.deactivate();
this._listWindow.hide();
this._optionsCommandWindow.activate();
this._optionsCommandWindow.selectLast();
};
具体的存档和读档的操作后面会进行编写,直接复制粘贴过来就行,这部分暂时不用管;现在这里是创建了存取档的菜单,并在打开时确定是存档还是读档,每次操作时设置菜单和对应的音量按钮会移动,关闭时再移动回去。
可以看到现在的UI已经可以按照预想的要求进行显示了。
下期会进行装备界面的制作。
觉得这篇文章好的话,麻烦点赞收藏噢!!!
有什么疑问可以评论讨论。