這篇主要教的內容是使用 Electron 來部署輸出你的 RPG Maker MV/MZ 的遊戲,
本次將使用 Windows 10 (1909) 作業系統來進行操作教學。
Electron 目前不支援 Windows Vista 及較早以前版本的作業系統。
更新履歷
2022/05/03 更新文章的部分內容。
2022/05/01 修正package.json的部分內容。
2022/01/04 新增禁止擷取畫面的功能部分。
2021/11/12 新增關於 asar 檔案的加密部分。
1.首先到以下網址下載對應 Windows 作業系統的 Node.js 並安裝到電腦。
https://nodejs.org/en/download/
2.安裝完成後,在開始功能表輸入CMD
執行「命令提示字元」,並分別輸入node -v
和npm -v
檢查是否有成功安裝。
3.建立一個資料夾之後,並新增一個名為「package.json」檔案,並編輯檔案加入以下的語法內容。
加入以下的語法內容之後,記得將 // 以及後面的註釋內容清除。
{
"name": "RPG_Game", //部署輸出的專案名稱,必須為英文數字且不含任何半形空白。
"version": "1.0.0", //版本號,格式必須為X.X.X的數字格式。
"description": "這是 Electron 部署測試", //檔案描述。
"main": "index.js",
//【RPG Maker MZ】
"chromium-args": "--force-color-profile=srgb",
//【RPG Maker MV】
"js-flags": "--expose-gc",
"build": {
"appId": "com.rpgmaker.game", //應用程式 ID。
//※通常輸入 com.(你的遊戲英文名).(你的英文署名) 這類英文數字的格式。
"productName": "Game",
"asar": true, //建議將此項設為 true ,如果你需要將遊戲內容進行加密封裝的話...
"afterPack": "./myAfterPackHook.js", // ※ 如果你有需要使用「asarmor」加密套件的話,請確保加入此項參數。
"win": {
"icon": "icon/icon.ico", //遊戲圖示,圖片格式為 ico 格式。
//※使用 png 轉 ico 的線上轉換圖片的網站,並將 ico 格式的圖檔放在 icon 資料夾
"target": {
"target": "dir"
}
}
},
"scripts": {
"start": "electron .",
"pack": "electron-builder --dir",
"dist": "electron-builder"
},
"author": "Mirai", //作者/團隊署名
"copyright": "Copyright © 2021 ${author} All rights reserved.", //版權宣告文字
"devDependencies": {
"electron": "^11.5.0",
"electron-builder": "^22.5.1",
"asarmor": "^2.0.0" // ※ 如果你有需要使用「asarmor」加密套件的話...
}
}
如果你有需要使用「asarmor」加密套件的話,請建立一個名為「myAfterPackHook.js」的檔案,並且將以下程式碼放進此檔案並保存在此輸出資料夾之中。
const asarmor = require('asarmor');
const { join } = require("path");
exports.default = async ({ appOutDir, packager }) => {
try {
const asarPath = join(packager.getResourcesDir(appOutDir), 'app.asar');
console.log(`applying asarmor patches to ${asarPath}`);
const archive = await asarmor.open(asarPath);
archive.patch(); // apply default patches
await archive.write(asarPath);
} catch (err) {
console.error(err);
}
};
【示範】
接著使用「命令提示字元」的cd
指令來指定資料夾的路徑,然後在「命令提示字元」輸入npm install
指令來安裝所需要的套件。
記得你的資料夾名稱只能英文數字且不含半行空白,如果有需要用到半形空白,請用半形底線( _ )替代半形空白。
4.開啟你的 RPG Maker MV/MZ 遊戲專案,並且按「檔案→部署…」,選擇「網頁瀏覽器 / Android / iOS」後,RPG Maker MV 為選擇「網頁瀏覽器」後,按下「OK」等待部署輸出完成。
【RPG Maker MZ】
【RPG Maker MV】
5.建立一個「index.js」的檔案,並輸入以下內容:
const { app, BrowserWindow, ipcMain, shell } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 816, //遊戲解析度寬度
height: 624, //遊戲解析度高度
icon: 'icon/icon.png',
useContentSize: true,
autoHideMenuBar: true,
backgroundColor: '#000000',
webPreferences: {
nodeIntegration: true,
contextIsolation: false
}
})
const isTest = false; //是否為測試模式
const testString = isTest ? '?test' : '';
app.allowRendererProcessReuse = false;
win.loadURL(`file://${__dirname}/index.html${testString}`);
if (process.platform !== 'darwin') {
win.setMenu(null);
}
require('electron').ipcMain.on('focusMainWindow', function (e) {
win.focus();
});
require('electron').ipcMain.on('openDevTools', function (e) {
win.webContents.openDevTools();
});
require('electron').ipcMain.on('openExternal', function (e, arg) {
shell.openExternal(arg);
});
//※需要搭配NekoGakuen_BlockedCapture.js的插件使用。
require('electron').ipcMain.on('setProtection', function (e, arg) {
win.setContentProtection(arg);
});
};
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
app.quit();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
6.在部署輸出後的資料夾,並且將除了「package.json」以外的所有檔案複製,並貼到這邊 Electron 用的輸出資料夾內。
【RPG Maker MZ】
【RPG Maker MV】
7.接下來將分為兩種專案並修改在 js 資料夾下的一些內容。
【RPG Maker MZ】
分別修改「main.js」、「rmmz_core.js」、「rmmz_managers.js」為以下內容。
isPathRandomized() {
// [Note] We cannot save the game properly when Gatekeeper Path
// Randomization is in effect.
return (
Utils.isNwjs() &&
process.mainModule.filename.startsWith("/private/var")
);
}
改成
isPathRandomized() {
// [Note] We cannot save the game properly when Gatekeeper Path
// Randomization is in effect.
if (Utils.isElectronjs()) {
return (__filename.startsWith("/private/var"));
} else {
return (Utils.isNwjs() &&
process.mainModule.filename.startsWith("/private/var"));
}
}
Utils.isNwjs = function() {
return typeof require === "function" && typeof process === "object";
};
改為
Utils.isNwjs = function() {
return typeof require === "function" && typeof process === "object";
};
Utils.isElectronjs = function () {
return window && window.process && window.process.versions && window.process.versions['electron'];
};
Utils.isOptionValid = function(name) {
const args = location.search.slice(1);
if (args.split("&").includes(name)) {
return true;
}
if (this.isNwjs() && nw.App.argv.length > 0) {
return nw.App.argv[0].split("&").includes(name);
}
return false;
};
改為
Utils.isOptionValid = function (name) {
const args = location.search.slice(1);
if (args.split("&").includes(name)) {
return true;
}
if (this.isElectronjs()) {
if (process.argv.length > 0) {
return process.argv[0].split("&").includes(name);
}
} else {
if (this.isNwjs() && nw.App.argv.length > 0) {
return nw.App.argv[0].split("&").includes(name);
}
}
return false;
};
StorageManager.fileDirectoryPath = function() {
const path = require("path");
const base = path.dirname(process.mainModule.filename);
return path.join(base, "save/");
};
改為
StorageManager.fileDirectoryPath = function () {
const path = require("path");
if (Utils.isElectronjs()) {
const base = path.dirname(__filename);
return Utils.isOptionValid('test') ? path.join(base, "save/") : path.join(base, '../../save/');
} else {
const base = path.dirname(process.mainModule.filename);
return path.join(base, "save/");
}
};
SceneManager.reloadGame = function () {
if (Utils.isNwjs()) {
chrome.runtime.reload();
}
};
SceneManager.showDevTools = function () {
if (Utils.isNwjs() && Utils.isOptionValid("test")) {
nw.Window.get().showDevTools();
}
};
改為
SceneManager.reloadGame = function () {
if (Utils.isElectronjs()) {
location.reload();
} else {
if (Utils.isNwjs()) {
chrome.runtime.reload();
}
}
};
SceneManager.showDevTools = function () {
if (Utils.isElectronjs()) {
if (Utils.isOptionValid("test")) {
require('electron').ipcRenderer.send('openDevTools');
}
} else {
if (Utils.isNwjs() && Utils.isOptionValid("test")) {
nw.Window.get().showDevTools();
}
}
};
【RPG Maker MV】
分別修改「rpg_core.js」、「rpg_managers.js」為以下內容。
Utils.isNwjs = function() {
return typeof require === 'function' && typeof process === 'object';
};
改為
Utils.isNwjs = function() {
return typeof require === 'function' && typeof process === 'object';
};
Utils.isElectronjs = function() {
return window && window.process && window.process.versions && window.process.versions['electron'];
};
Utils.isOptionValid = function (name) {
if (location.search.slice(1).split('&').contains(name)) { return 1; };
if (typeof nw !== "undefined" && nw.App.argv.length > 0 && nw.App.argv[0].split('&').contains(name)) { return 1; };
return 0;
};
改為
Utils.isOptionValid = function (name) {
if (location.search.slice(1).split('&').contains(name)) { return 1; };
if (this.isElectronjs()) {
if (this.isNwjs() && process.argv.length > 0 && process.argv[0].split('&').contains(name)) { return 1; };
} else {
if (typeof nw !== "undefined" && nw.App.argv.length > 0 && nw.App.argv[0].split('&').contains(name)) { return 1; };
}
return 0;
};
Input._wrapNwjsAlert = function() {
if (Utils.isNwjs()) {
var _alert = window.alert;
window.alert = function() {
var gui = require('nw.gui');
var win = gui.Window.get();
_alert.apply(this, arguments);
win.focus();
Input.clear();
};
}
};
改為
Input._wrapNwjsAlert = function () {
if (Utils.isElectronjs()) {
var _alert = window.alert;
window.alert = function () {
_alert.apply(this, arguments);
require('electron').ipcRenderer.send('focusMainWindow');
Input.clear();
};
} else {
if (Utils.isNwjs()) {
var _alert = window.alert;
window.alert = function () {
var gui = require('nw.gui');
var win = gui.Window.get();
_alert.apply(this, arguments);
win.focus();
Input.clear();
};
}
}
};
StorageManager.localFileDirectoryPath = function() {
var path = require('path');
var base = path.dirname(process.mainModule.filename);
return path.join(base, 'save/');
};
改為
StorageManager.localFileDirectoryPath = function () {
var path = require('path');
if (Utils.isElectronjs()) {
var base = path.dirname(__filename);
return Utils.isOptionValid('test') ? path.join(base, 'save/') : path.join(base, '../../save/');
} else {
var base = path.dirname(process.mainModule.filename);
return path.join(base, 'save/');
}
};
SceneManager.initialize = function() {
this.initGraphics();
this.checkFileAccess();
this.initAudio();
this.initInput();
this.initNwjs();
this.checkPluginErrors();
this.setupErrorHandlers();
};
改為
SceneManager.initialize = function () {
this.initGraphics();
this.checkFileAccess();
this.initAudio();
this.initInput();
if (!Utils.isElectronjs()) {
this.initNwjs();
}
this.checkPluginErrors();
this.setupErrorHandlers();
};
SceneManager.onKeyDown = function (event) {
if (!event.ctrlKey && !event.altKey) {
switch (event.keyCode) {
case 116: // F5
if (Utils.isNwjs()) {
location.reload();
}
break;
case 119: // F8
if (Utils.isNwjs() && Utils.isOptionValid('test')) {
require('nw.gui').Window.get().showDevTools();
}
break;
}
}
};
改為
SceneManager.onKeyDown = function (event) {
if (!event.ctrlKey && !event.altKey) {
switch (event.keyCode) {
case 116: // F5
if (Utils.isElectronjs()) {
location.reload();
} else {
if (Utils.isNwjs()) {
location.reload();
}
}
break;
case 119: // F8
if (Utils.isElectronjs()) {
if (Utils.isOptionValid('test')) {
require('electron').ipcRenderer.send('openDevTools');
}
} else {
if (Utils.isNwjs() && Utils.isOptionValid('test')) {
require('nw.gui').Window.get().showDevTools();
}
}
break;
}
}
};
如果上述內容覺得非常複雜的話,也可以到以下GitLab直接下載懶人包。
https://gitlab.com/MiraiSoSad/rpg_maker_mv_and_mz_electron/-/tree/zh_TW
8.回到「命令提示字元」並輸入npm start
檢查是否能正常執行遊戲,如果執行遊戲 OK 的話,輸入npm run dist
後就能執行部署輸出。
9.最後,部署輸出完成後,可以在「dist/win-unpacked/」資料夾中找到部署輸出好的內容,執行Game.exe即可。
https://github.com/electron/electron-quick-start
https://qiita.com/RaTTiE/items/63f2e351a93f81bc8039
https://a091234765.pixnet.net/blog/post/402450719-[electron學習筆記]electron安裝檔打包攻略補充_el
https://www.npmjs.com/package/asarmor
《Mirai》Patreon創作:
https://www.patreon.com/MiraiDiary
《Mirai》個人推特(X):
https://twitter.com/Mirai_so_Sad
《Mirai》itch.io頁面:
https://miraisosad.itch.io
《Mirai》個人Instagram
https://www.instagram.com/miraisosad/
貓咪學園
NekoGakuen
RPG 製作大師
RPG Maker MV
RPG Maker MZ