# 【macOS】使用 Electron 部署輸出 RPG Maker MV/MZ 遊戲 :::danger ![](https://i.imgur.com/xQoThD6.png) ::: 這篇主要教的內容是使用 Electron 來部署輸出你的 RPG Maker MV/MZ 的遊戲, 本次將使用 Intel 機種的 macOS 10.15.7 (Catalina) 作業系統來進行操作教學。 :::danger <i class="fa fa-times-circle" aria-hidden="true"></i> Electron 目前不支援 **OS X 10.10 (Yosemite)** 及較早以前版本的作業系統。 ::: :::info **<i class="fa fa-info-circle" aria-hidden="true"></i> 更新履歷** **2022/05/03** 更新文章的部分內容。 **2022/05/01** 修正package.json的部分內容。 **2022/01/04** 新增禁止擷取畫面的功能部分。 **2021/11/12** 新增關於 asar 檔案的加密部分。 ::: 1.首先到以下網址下載對應 macOS 作業系統的 Node.js 並安裝到電腦。 https://nodejs.org/en/download/ ![](https://i.imgur.com/yg5ZT3Y.png) 2.安裝完成後,在右上角的「Spotlight 搜尋」輸入並執行「終端機」,接著分別輸入`node -v`和`npm -v`檢查是否有成功安裝。 ![](https://i.imgur.com/egVSbJG.png) ![](https://i.imgur.com/8iGB6gM.png) :::info <i class="fa fa-info-circle" aria-hidden="true"></i> 然後,因為在macOS平台上部署遊戲時,需要用到「Xcode Command Lines Tool」,所以在「終端機」輸入`xcode-select --install`來下載安裝此工具。 ::: 3.建立一個檔案夾之後,並新增一個名為「package.json」檔案,並編輯檔案加入以下的語法內容。 :::warning <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> 加入以下的語法內容之後,記得將 **//** 以及後面的註釋內容清除。 ::: ```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」加密套件的話,請確保加入此項參數。 "mac": { "category": "public.app-category.role-playing-games", "icon": "icon/icon.icns", //遊戲圖示,圖片格式為 icns 格式。 //※使用 png 轉 icns 的線上轉換圖片的網站,並將 icns 格式的圖檔放在 icon 檔案夾裡。 "target": { "target": "dir", "arch": "universal" //指定 「universal」的情況時,將同時兼容 x64 (Intel 機種) 和 arm64 (Apple M1 機種)。 } } }, "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」加密套件的話... } } ``` :::info <i class="fa fa-info-circle" aria-hidden="true"></i> 如果你有需要使用「asarmor」加密套件的話,請建立一個名為「myAfterPackHook.js」的檔案,並且將以下程式碼放進此檔案並保存在此輸出檔案夾之中。 ```javascript= 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); } }; ``` ::: **【示範】** ![](https://i.imgur.com/1C8AO6H.png) 接著使用「終端機」的`cd`指令來指定檔案夾的路徑,然後在「終端機」輸入`npm install`指令來安裝所需要的套件。 :::warning <i class="fa fa-exclamation-triangle" aria-hidden="true"></i> 記得你的檔案夾名稱只能英文數字且不含半行空白,如果有需要用到半形空白,請用半形底線( _ )替代半形空白。 ::: 4.開啟你的 RPG Maker MV/MZ 遊戲專案,並且按「檔案→部署…」,選擇「網頁瀏覽器 / Android / iOS」後,RPG Maker MV 為選擇「網頁瀏覽器」後,按下「OK」等待部署輸出完成。 **【RPG Maker MZ】** ![](https://i.imgur.com/gVosPJM.png) **【RPG Maker MV】** ![](https://i.imgur.com/oN8KHEr.png) 5.建立一個「index.js」的檔案,並輸入以下內容: ```javascript= 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 gametitleString = "遊戲測試"; //遊戲標題 const isTest = false; //是否為測試模式 const testString = isTest ? '?test' : ''; app.allowRendererProcessReuse = false; win.loadURL(`file://${__dirname}/index.html${testString}`); if (process.platform !== 'darwin') { win.setMenu(null); } else { var { Menu } = require('electron'); var menu = Menu.buildFromTemplate([ { label: 'Electron', submenu: [ { label: `關於 ${gametitleString}`, selector: 'orderFrontStandardAboutPanel:' }, {type: 'separator'}, { label: `隱藏 ${gametitleString}`, accelerator: 'Command+H', selector: 'hide:' }, { label: '隱藏其他', accelerator: 'Command+Alt+H', selector: 'hideOtherApplications:' }, { label: '全部顯示', selector: 'unhideAllApplications:' }, { type: 'separator' }, { label: `結束 ${gametitleString}`, accelerator: 'Command+Q', click: function() { app.quit(); } } ] } ]); Menu.setApplicationMenu(menu); } 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】** ![](https://i.imgur.com/GGjsbq9.png) **【RPG Maker MV】** ![](https://i.imgur.com/qqnoc55.png) 7.接下來將分為兩種專案並修改在 js 檔案夾下的一些內容。 **【RPG Maker MZ】** 分別修改「main.js」、「rmmz_core.js」、「rmmz_managers.js」為以下內容。 ## main.js ```javascript= isPathRandomized() { // [Note] We cannot save the game properly when Gatekeeper Path // Randomization is in effect. return ( Utils.isNwjs() && process.mainModule.filename.startsWith("/private/var") ); } ``` 改成 ```javascript= 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")); } } ``` ## rmmz_core.js ```javascript= Utils.isNwjs = function() { return typeof require === "function" && typeof process === "object"; }; ``` 改為 ```javascript= Utils.isNwjs = function() { return typeof require === "function" && typeof process === "object"; }; Utils.isElectronjs = function () { return window && window.process && window.process.versions && window.process.versions['electron']; }; ``` --- ```javascript= 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; }; ``` 改為 ```javascript= 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; }; ``` ## rmmz_managers.js ```javascript= StorageManager.fileDirectoryPath = function() { const path = require("path"); const base = path.dirname(process.mainModule.filename); return path.join(base, "save/"); }; ``` 改為 ```javascript= 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/"); } }; ``` --- ```javascript= SceneManager.reloadGame = function () { if (Utils.isNwjs()) { chrome.runtime.reload(); } }; SceneManager.showDevTools = function () { if (Utils.isNwjs() && Utils.isOptionValid("test")) { nw.Window.get().showDevTools(); } }; ``` 改為 ```javascript= 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」為以下內容。 ## rpg_core.js ```javascript= Utils.isNwjs = function() { return typeof require === 'function' && typeof process === 'object'; }; ``` 改為 ```javascript= Utils.isNwjs = function() { return typeof require === 'function' && typeof process === 'object'; }; Utils.isElectronjs = function() { return window && window.process && window.process.versions && window.process.versions['electron']; }; ``` --- ```javascript= 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; }; ``` 改為 ```javascript= 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; }; ``` --- ```javascript= 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(); }; } }; ``` 改為 ```javascript= 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(); }; } } }; ``` ## rpg_managers.js ```javascript= StorageManager.localFileDirectoryPath = function() { var path = require('path'); var base = path.dirname(process.mainModule.filename); return path.join(base, 'save/'); }; ``` 改為 ```javascript= 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/'); } }; ``` --- ```javascript= SceneManager.initialize = function() { this.initGraphics(); this.checkFileAccess(); this.initAudio(); this.initInput(); this.initNwjs(); this.checkPluginErrors(); this.setupErrorHandlers(); }; ``` 改為 ```javascript= SceneManager.initialize = function () { this.initGraphics(); this.checkFileAccess(); this.initAudio(); this.initInput(); if (!Utils.isElectronjs()) { this.initNwjs(); } this.checkPluginErrors(); this.setupErrorHandlers(); }; ``` --- ```javascript= 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; } } }; ``` 改為 ```javascript= 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; } } }; ``` :::info <i class="fa fa-info-circle" aria-hidden="true"></i> 如果上述內容覺得非常複雜的話,也可以到以下GitLab直接下載懶人包。 https://gitlab.com/MiraiSoSad/rpg_maker_mv_and_mz_electron/-/tree/zh_TW ::: 8.回到「終端機」並輸入`npm start`檢查是否能正常執行遊戲,如果執行遊戲 OK 的話,輸入`npm run dist`後就能執行部署輸出。 9.最後,部署輸出完成後,可以在「dist/mac-universal/」檔案夾中找到部署輸出好的內容,執行Game.app即可。 :::info <i class="fa fa-info-circle" aria-hidden="true"></i> 如果你初次執行遊戲時,遊戲畫面跳出「Please move the Game.app to a different folder.」錯誤訊息,你可以建立一個檔案夾將 Gamp.app 移動到此檔案夾中並再次執行遊戲即可。 ::: ### <i class="fa fa-link" aria-hidden="true"></i> 參考來源 https://www.electronjs.org https://github.com/electron/electron-quick-start https://qiita.com/RaTTiE/items/63f2e351a93f81bc8039 https://a091234765.pixnet.net/blog/post/402450719-%5Belectron%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98%5Delectron%E5%AE%89%E8%A3%9D%E6%AA%94%E6%89%93%E5%8C%85%E6%94%BB%E7%95%A5%E8%A3%9C%E5%85%85_el https://www.electron.build 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/ ###### tags: `貓咪學園` `RPG 製作大師` `RPG Maker MV` `RPG Maker MZ`