一个显示图片的窗口插件
rpgmakermv吧
全部回复
仅看楼主
level 7
邪恶道具 楼主
效果如何,二楼贴上我翔一样的代码
2016年04月22日 14点04分 1
level 7
邪恶道具 楼主
过段时间我会优化可读性并附上一份写这样一个插件的教程
/*:
* @author Created by 邪恶道具 on 2016/3/5.
*
* @param 图片数量
* @default 15
*
* @param 选项名
* @default 日记
*
* @param 初始化标签
* @default 0
*
* @param 功能选择
* @default 0
*
* @plugindesc
* 初始化标签为1时,初始化时显示所有图片,为0时隐藏所有图片
* 功能选择:1是默认,2和3非别是全部设置隐藏或显示
*
* @help
*
*
* 将需要使用的图片放入img文件夹中的diary文件夹内
* 文件名按照这种格式编辑 "diary (x).png",其中x为一个数字,此数字对应相应标签
* 因此该数字最好是连续的,如:diary (1).png ,diary (2).png...
* Book.png,这种名称的图片为背景图片
* 日记选项中显示的标签,需要自己新建一个diary_name.rpgsave文件
* diary_name.rpgsave文件中的内容使用UTF-8字符,用英文逗号分割
* 该文件需要放入save文件夹中
*
*
*/
var params = PluginManager.parameters("diary_new");
(function(){
var tabName = String(params['选项名'] || "请输入" );
//console.log("tagNum:"+tabName);
//通过needsCommand指令添加Menu菜单中的Diary选项
var _Window_MenuCommand_addMainCommands = Window_MenuCommand.prototype.addMainCommands;
Window_MenuCommand.prototype.addMainCommands = function(){
_Window_MenuCommand_addMainCommands.call(this);
if (this.needsCommand('diary')) {
//第一个参数是选项的名称,第二个则是对应createCommandWindow里的字符
this.addCommand(tabName, 'diary', 1);//TextManager.diary的值为'日记'
}
};
//将选项添加进去
var _Scene_Menu_createCommandWindow = Scene_Menu.prototype.createCommandWindow;
Scene_Menu.prototype.createCommandWindow = function(){
_Scene_Menu_createCommandWindow.call(this);
this._commandWindow.setHandler('diary', diary_command.bind(this));
};
})();
var diary_command = function(){
this._commandWindow.deselect();
run();
};
var diary_command_activate = function(){
SceneManager.goto(Scene_Menu);
};
/**
* Created by 邪恶道具 on 2016/3/5.
* 日记系统绘制窗口及其功能代码
* 图片读取是在状态机执行的间隔中进行的
*/
var functionSelection = Number(params['功能选择'] || "2" );
var photoNum = Number(params['图片数量'] || 15);//需要载入的图片数量,从1开始,而不是0(也就是表示1-11(11个),而不是往常的0-11(12个))
var run = function(){
if(!document.getElementById("theCanvas")){
create_canvas();
}else{
var theCanvas = document.getElementById("theCanvas");
theCanvas.style.zIndex = 20;
}
fileWriteAndRead( functionSelection,photoNum,null, null);
diary(photoNum);
};
var diary = function(photoNum) {
var theCanvas = document.getElementById("theCanvas");
var context = theCanvas.getContext("2d");
//状态机参数
var state_photoLoad = 1;
var state_initList = 2;
var state_windowDraw = 3;
var state_drawFont = 4;
var state_nextPage = 5;
var state_backPage = 6;
var state_end = 7;
var currentState = state_photoLoad;
//参数
var displayText = new Object();
displayText.flag = new Array();//为0显示"???",为1显示text属性的内容
displayText.text = new Array();//日记名称
displayText.maxLine = 12;//最大行数
displayText.textCount = 0;
var photoLoadedCount = 0;//负责已载入图片的计数
var offset = 2;//边框偏移量
var line_x = 200;//分割线x坐标
var currentPage = 1;//记录当前的页数,从1开始
var rememberNameCount = 0;
var coordinate = new Object();//该对象负责存储定位选项与显示的必要数据
coordinate.backGround = null;
coordinate.photoCache = new Array();//存储日记图片的数组
coordinate.font_x = new Array();
coordinate.font_y = new Array();
coordinate.font_height = new Array();
coordinate.font_width = new Array();
//临时参数
var global_Count = 0;
var global_Flag = 0;
var tempFlag = 0;
//coordinate.photoCache,从第0个元素开始是日记图片
var photoLoad = function () {
var backGround = new Image();
backGround.src = "img/diary/book.png";
backGround.addEventListener("load", load_done, false);
coordinate.backGround = backGround;
window.console.log("loading");
for (var i = 0; i < photoNum; i++) {
var number = i + 1;
var temp = new Image();
temp.src = "img/diary/diary (" + number + ").png";
temp.addEventListener("load", load_done, false);
coordinate.photoCache.push(temp);
}
//window.console.log("coordinate.photoCache.length:"+coordinate.photoCache.length);
currentState = state_end;
};
var initList = function () {
var temp = loadFile();
var tempName = string_Change_strArray();
for (var i = 0; i < coordinate.photoCache.length; i++) {
displayText.text.push(tempName[i]);
}
for(var i = 0;i < temp.length;i++){
if(temp[i]==0||temp[i]==1){
displayText.flag.push(temp[i]);
}
}
currentState = state_windowDraw;
};
//将中文字符串转换成数组
var string_Change_strArray = function () {
var fs = require('fs');
var fileName = "diary_name.rpgsave";
var savePath = StorageManager.localFileDirectoryPath() + fileName;
var data = fs.readFileSync(savePath,"utf-8");
var temp = data.split(",");
var tempArray = new Array();
for(var i=0;i<temp.length;i++) {
if (temp[i] != ",") {
tempArray.push(temp[i]);
}
}
return tempArray;
};
var loadFile = function () {
var fs = require('fs');
var fileName = "diary.rpgsave";
var savePath = StorageManager.localFileDirectoryPath() + fileName;
var readData = null;
readData = fs.readFileSync(savePath);
readData = ""+readData;
return readData;
};
var load_done = function () {
photoLoadedCount++;
//coordinate.photoCache.length + 1,背景图加上日记图片的数量
if (photoLoadedCount == (coordinate.photoCache.length + 1)) {
coordinate.backGround.removeEventListener("load", load_done, false);
for (var i = 0; i < coordinate.photoCache.length; i++) {
coordinate.photoCache[i].removeEventListener("load", load_done, false);
}
currentState = state_initList;
coordinate.time = setInterval(stateMachine, 33);
console.log("loaded");
photoLoadedCount--;//这下才是日记图片数(-1是为了去掉背景的计数)
}
};
2016年04月22日 14点04分 2
level 7
邪恶道具 楼主
var windowDraw = function () {
var width = theCanvas.width - offset;
var height = theCanvas.height - offset;
context.drawImage(coordinate.backGround, 0, 0);
//给背景增加透明度
context.beginPath();
context.globalAlpha = 0.8;
context.fillStyle = "#000000";
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
//绘制边框
context.beginPath();
context.lineWidth = 4;
context.lineJoin = "round";
context.strokeStyle = "#FFFFFF";
context.moveTo(offset, offset);
context.lineTo(width, offset);
context.lineTo(width, height);
context.lineTo(offset, height);
context.closePath();
context.stroke();
//获取显示日记内容区域的信息
coordinate.diaryDisplayArea_minX = line_x;
coordinate.diaryDisplayArea_maxX = width;
coordinate.diaryDisplayArea_minY = offset;
coordinate.diaryDisplayArea_maxY = height;
context.beginPath();
context.moveTo(line_x, offset);
context.lineTo(line_x, height);
context.stroke();
currentState = state_drawFont;
};
var drawFont = function () {
var offset = 20;
var tempStr = null;
//var temp = displayText.maxLine + rememberNameCount;
if(photoNum==(displayText.maxLine + rememberNameCount - 2)){
rememberNameCount--;
currentPage--;
}
var length = displayText.maxLine + rememberNameCount;
for (var i = rememberNameCount; i < length; i++) {
if (displayText.flag[i] == 1) {
tempStr = 0;
}
if(displayText.flag[i] == 0){
tempStr = 1;
}
if ((i + 1) == length) {
if(global_Count == coordinate.photoCache.length-1 || (global_Count
== coordinate.photoCache.length && tempFlag == 0)){
tempStr = 4;
}else{
tempStr = 2;
}
}
if(currentPage>1&&i==rememberNameCount){
tempStr = 3;
}
switch(tempStr){
case 0:
tempStr = displayText.text[i];
if(global_Flag==0){
global_Count++;
}
break;
case 1:
tempStr = "???";
if(global_Flag==0){
global_Count++;
}
break;
case 2:
tempStr = "下一个";
break;
case 3:
tempStr = "上一个";
break;
case 4:
if(displayText.flag[i] == 1){
tempStr = displayText.text[i];
global_Count++;
break;
}else{
tempStr = "???";
global_Count++;
break;
}
case null:
tempStr = "";
window.console.log("无剩余图片");
break;
}
if(global_Count > coordinate.photoCache.length && tempFlag == 0){
global_Count = coordinate.photoCache.length;
}
var fontMaxWidth = line_x - offset;
var fontSize = 30;
var y = offset + fontSize;
context.beginPath();
context.font = "normal " + fontSize + "px Arial";
context.fillStyle = "#FFFFFF";
var str_width = Math.round(context.measureText(tempStr).width);
var x = ((line_x - offset) / 2) - Math.floor(str_width / 2) + 10;//使字
体居中
context.fillText(tempStr, x, y * (i - rememberNameCount + 1),
fontMaxWidth);
coordinate.font_x.push(x);
coordinate.font_y.push((y * (i- rememberNameCount + 1)) - fontSize);//
不减去fontSize,那么y会位于字体下方
coordinate.font_width.push(str_width);//压入字符串宽度
//绘制字体下划线
context.beginPath();
context.strokeStyle = "#FFFFFF";
var fontOffset = 10;
context.moveTo(x, y * (i- rememberNameCount + 1) + fontOffset);
context.lineTo(x + str_width, y * (i- rememberNameCount + 1) +
fontOffset);
coordinate.font_height.push(fontSize + fontOffset);//压入字符串高度
context.stroke();
coordinate.fontOffset = fontOffset;
}
currentState = state_end;
};
var game_end = function () {
clearInterval(coordinate.time);
};
var checkClick = function (event) {
if(event.button == 2 || exitKey(event)){
exitDiary(theCanvas);
}else{
var x = Math.round(event.pageX);
var y = Math.round(event.pageY);
//console.log("x:" + x);
//console.log("y:" + y);
for (var i = 0; i < coordinate.photoCache.length; i++) {
if (displayText.flag[i] == 1 || displayText.flag[i] == 0) {
//minBoundary和maxBoundary分别为选项的左右边界
var minX = coordinate.font_x[i];
var minY = coordinate.font_y[i];
var maxX = minX + Math.round(coordinate.font_width[i]);
var maxY = minY + Math.round(coordinate.font_height[i]);
var minBoundary = (x > minX && y > minY);
var maxBoundary = (x < maxX && y < maxY);
if (minBoundary && maxBoundary) {
theCanvas.width = theCanvas.width;
theCanvas.height = theCanvas.height;
global_Flag = 1;
windowDraw();
drawFont();
currentState = null;
//获取设置图片位置的必要信息
if(displayText.flag[i+rememberNameCount] == 1){
var centext_width =
coordinate.diaryDisplayArea_maxX - coordinate.diaryDisplayArea_minX;
var centext_height =
coordinate.diaryDisplayArea_maxY - coordinate.diaryDisplayArea_minY;
var centerPoint_x = (centext_width / 2 +
coordinate.diaryDisplayArea_minX);
var centerPoint_y = (centext_height / 2 +
coordinate.diaryDisplayArea_minY);
var photo_width = coordinate.photoCache[i
+rememberNameCount].width / 2;
var photo_height = coordinate.photoCache[i
+rememberNameCount].height / 2;
var place_x = centerPoint_x - photo_width;
var place_y = centerPoint_y - photo_height;
}
if ((i + 1) == displayText.maxLine && global_Count <
coordinate.photoCache.length) {
nextPage();
break;
}
if(i==0&¤tPage>1){
backPage();
break;
}
//日记内容显示部分
if(!(((i + 1) == displayText.maxLine)&&
(i==0&¤tPage>1))) {
if(displayText.flag[i+rememberNameCount] == 1){
2016年04月22日 14点04分 3
level 7
邪恶道具 楼主
context.drawImage(coordinate.photoCache[i
+rememberNameCount], place_x, place_y);
break;
}
}
}
}
}
}
};
//diary窗口的退出函数
var exitDiary = function(theCanvas){
theCanvas.removeEventListener("mouseup", checkClick,false);
document.removeEventListener("keyup", checkClick,false);
theCanvas.style.zIndex = -20;
diary_command_activate();
theCanvas.width = theCanvas.width;
theCanvas.height = theCanvas.height;
window.console.log("退出完成");
};
var exitKey = function(event){
switch (event.keyCode){
case 27:
return true;
break;
/* case 93:
return true;
break;*/
}
};
var nextPage = function () {
if(global_Count < photoNum){
tempFlag = 0;
}
global_Count++;
currentPage++;
rememberNameCount++;
theCanvas.width = theCanvas.width;
theCanvas.height = theCanvas.height;
windowDraw();
drawFont();
currentState = null;
};
var backPage = function () {
if(global_Count == photoNum && tempFlag == 0){
global_Count--;
}
tempFlag = 1;
global_Count--;
currentPage--;
rememberNameCount--;
theCanvas.width = theCanvas.width;
theCanvas.height = theCanvas.height;
windowDraw();
drawFont();
currentState = null;
};
var stateMachine = function () {
switch (currentState) {
case state_photoLoad:
photoLoad();
break;
case state_initList:
initList();
break;
case state_windowDraw:
windowDraw();
break;
case state_drawFont:
drawFont();
break;
case state_nextPage:
nextPage();
break;
case state_backPage:
backPage();
break;
case state_end:
game_end();
break;
case null:
//window.console.log("错误的状态值");
break;
}
};
document.addEventListener('keyup', checkClick,false);
theCanvas.addEventListener("mouseup", checkClick, false);
coordinate.time = setInterval(stateMachine, 33);
};
var create_canvas = function(){
var theCanvas = document.createElement("canvas");
if(!theCanvas){
document.write("no support canvas");
return ;
}
//mv的是816*624
theCanvas.width = 816;
theCanvas.height = 624;
theCanvas.style.position = "absolute";
theCanvas.style.margin = "auto";
theCanvas.style.top = 0 + "px";
theCanvas.style.left = 0 + "px";
theCanvas.style.right = 0 + "px";
theCanvas.style.bottom = 0 + "px";
theCanvas.style.zIndex = 20;
theCanvas.id = "theCanvas";
document.body.appendChild(theCanvas);
return theCanvas;
};
/**
* Created by 邪恶道具 on 16-4-19.
* 负责文件的读取与存储,使用node.js
*/
var fileDisplay = Number(params['初始化标签'] || "0" );
var _Game_Interpreter_pluginCommand =
Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args) {
_Game_Interpreter_pluginCommand.call(this, command, args);
console.log("functionSelection:"+functionSelection);
fileWriteAndRead( functionSelection,photoNum,command, args);
};
//第1个和3个参数决定运行那个函数,第2个参数表示有多少图片,第4个参数是插件变量
//第1个参数是手动指定
fileWriteAndRead = function(select,photoNum,command, args) {
var fs = require('fs');
var fileName = "diary.rpgsave";
var savePath = StorageManager.localFileDirectoryPath() + fileName;
var length = photoNum;//为图片数量
//intiData只在初始化时使用
var initData = null;
var readData = null;
console.log("select2:"+select);
var initFile = function () {
initData = new Array(length);
for (var i = 0; i < length; i++) {
initData[i] = fileDisplay;
}
saveFile(initData);
};
var saveFile = function (data) {
fs.writeFileSync(savePath, data,"utf-8");
};
var loadFile = function () {
readData = fs.readFileSync(savePath,"utf-8");
readData = string_Change_array(readData);
return readData;
};
//index为要修改的位置,flag的值为0和1
var changeFlag = function () {
readData = loadFile();
var count = 0;
for(var i=0;i<readData.length;i++){
if (count == args[0]) {
readData[count] = args[1];
break;
}
if((i % 2) == 0){
count++;
}
}
saveFile(readData);
};
//将数字字符串转换成数组
var string_Change_array = function (data) {
var temp = data.split("");
var tempArray = new Array();
for(var i=0;i<temp.length;i++){
if(temp[i]==1||temp[i]==0){
tempArray.push(temp[i]);
}
}
return tempArray;
};
//将中文字符串转换成数组
var string_Change_strArray = function () {
var fs = require('fs');
var fileName = "diary_name.rpgsave";
var savePath = StorageManager.localFileDirectoryPath() + fileName;
var data = fs.readFileSync(savePath,"utf-8");
var temp = data.split(",");
var tempArray = new Array();
for(var i=0;i<temp.length;i++) {
if (temp[i] != ",") {
tempArray.push(temp[i]);
}
}
return tempArray;
};
var exist = function () {
//为真表示文件已存在
if (!fs.existsSync(savePath)) {
initFile();
} else {
loadFile();
}
switch (command) {
case "changeFlag":
changeFlag();
break;
case "string_Change_strArray":
string_Change_strArray();
break;
case null:
//console.log("指令为空");
break;
}
};
var reInitFile = function(flag){
console.log("进入reInitFile");
var temp = null;
if(flag == 2){
temp = 0;
}else{
temp = 1;
}
readData = loadFile();
for(var i=0;i<readData.length;i++){
if (readData[i] != ",") {
readData[i] = temp;
}
}
console.log("reInitFile_readData:"+readData);
saveFile(readData);
};
var run = function(select) {
var temp = 2;
switch (select) {
case 1:
exist();
break;
case 2:
reInitFile(temp);
break;
case 3:
reInitFile(temp+1);
break;
}
};
run(select);
};
2016年04月22日 14点04分 4
level 15
虽然看不懂,但还是支持[真棒]
2016年04月22日 17点04分 5
level 7
邪恶道具 楼主
优化语义后的UML图,基本上就是把上面的部分代码封装到函数里,需要源代码的可以加群 515860452
2016年04月25日 08点04分 6
貌似帮助文档上没有提到怎么写脚本.....有教程么......求ing[泪]
2016年04月30日 12点04分
level 1
我一直在想为什么楼主写的代码和我认识的JS完全不一样......泪奔ing.........
2016年04月30日 12点04分 7
看js忍者秘籍,好让你理论上具有写插件的基本能力,然后看canvas开发详解,了解canvas,状态机和资源预载的方法,最后就是去看node.js中,文件系统的的API,我这个插件在窗口绘制上是使用的canvas来画,图片是通过资源预载来载入的,整个函数执行是通过状态机来切换的,数据保存是通过node
2016年04月30日 14点04分
level 7
邪恶道具 楼主
ps:这教程很久前就写好了,有人建议应该写的更通熟易懂,但水平有限修改过后也只有这样了
该教程的特点是对mv源码的依赖小,适合有js编程能力但对mv源码研究不深的人
理解这些技术后,就可以不受到mv本身的约束来写窗口和它所要展示的内容
1.首先,将选项添加进主菜单
var _Window_MenuCommand_addMainCommands = Window_MenuCommand.prototype.addMainCommands;
Window_MenuCommand.prototype.addMainCommands = function(){
_Window_MenuCommand_addMainCommands.call(this);
if (this.needsCommand('diary')) {
this.addCommand(tagName, 'diary', 1);
}
};
上面这几段代码,用于将你窗口的选项添加进主菜单中,其中tagName为该选项所显示的字符串
'diary'则是一个指令名,该指令名可以自行设置,但一定要是字符串
第三个选项用途不明,但填1并没有问题,所以写你自己的窗口时填1即可
2.然后给你的选项添加功能
var _Scene_Menu_createCommandWindow = Scene_Menu.prototype.createCommandWindow;
Scene_Menu.prototype.createCommandWindow = function(){
_Scene_Menu_createCommandWindow.call(this);
this._commandWindow.setHandler('diary', diary_command.bind(this));
};
this._commandWindow.setHandler('diary', diary_command.bind(this));
这段代码中的第一个参数是变量名,第二个参数用于给你添加的选项附加上逻辑功能
bind关键字与call类似,都是改变函数或变量的this指向,但不同的是,call是改变后立即执行,bind则是改变this的指向,但不立即执行
setHandler源码如下:
Window_Selectable.prototype.setHandler = function(symbol, method) {
this._handlers[symbol] = method;
};
关于上面代码使用call的意义可以参见我以前的帖子
https://tieba.baidu.com/p/4185225772?pid=79770073662&cid=0#79770073662
3.其他函数介绍
3.1 窗口跳转函数
var diary_command_activate = function(){
SceneManager.goto(Scene_Menu);
};
该函数在退出日记窗口时执行,用于切换回主菜单,根据给goto输入不同的参数,可以跳转到其他菜单
3,2 窗口逻辑函数
var diary_command = function(){
this._commandWindow.deselect();
diary_run();
};
this._commandWindow.deselect();用于冻结主菜单,使其不接受鼠标与键盘操作
diary_run();则包含了你编写的窗口的所有代码
现在关键的几个函数都介绍完毕了,实际上通过这几个函数已经可以开始编写窗口了
现在来介绍我写的插件中的几项关键技术:状态机,资源预载和本地文件读写
先来说说状态机
//状态机参数
var state_photoLoad = 1;
var state_initList = 2;
var state_windowDraw = 3;
var state_drawFont = 4;
var state_nextPage = 5;
var state_backPage = 6;
var state_end = 7;
var currentState = state_photoLoad;
首先设置一组状态变量,然后设置一个记录状态变量的变量,该变量记录下一个应该执行的函数的代码
设置一个状态机,然后设置一个计时器,我设置的是每33毫秒查询一次状态
在下述每个函数执行完时,状态变量currentState都会发生相应变化
这就是状态机了,简单而且能更有效的控制代码的执行,相当简单的技术
//计时器
coordinate.time = setInterval(stateMachine, 33);
//状态机
var stateMachine = function () {
switch (currentState) {
case state_photoLoad:
photoLoad();
break;
case state_initList:
initList();
break;
case state_windowDraw:
windowDraw();
break;
case state_drawFont:
drawFont();
break;
case state_nextPage:
nextPage();
break;
case state_backPage:
backPage();
break;
case state_end:
game_end();
break;
case null:
//window.console.log("错误的状态值");
break;
}
};
资源预载:
在我的源码是photoLoad和load_done这两个函数在负责这一功能
原理非常简单
var backGround = new Image();
backGround.src = "img/diary/book.png";
backGround.addEventListener("load", load_done, false);
coordinate.backGround = backGround;
window.console.log("loading");
在这段代码中,设置好正确的资源路径,然后加上一个监听函数来监听资源是否被载入
如果确认载入,则执行load_done函数来移除监听并计数,资源载入是在代码执行的空隙时进行的,所以photoLoad的末尾有这么一句代码currentState = state_end
用于停止轮询,等待载入
当load_done中的计数等于需要被载入的资源数量时,就表示资源被全部载入,此时再次启动轮询并指定新的状态
载入的资源此时位于内存中,所以会增加内存负担
此技术也是相当简单的
本地文件读写可以去看node.js的file system那一部分,说的比我好,具体实现可以参考我的代码
node存储的文件是一个字符串文件,所以要转换为数组时,需要自己写个转换函数,也可以参考我的源码
2016年05月10日 07点05分 8
level 13
留名~
2017年11月23日 09点11分 9
level 1
谢谢大大分享
2017年11月23日 12点11分 10
level 1
大佬,怎样才能联系上你啊
2022年06月17日 14点06分 11
1