主題

【macOS】使用 Electron 部署輸出 RPG Maker MV/MZ 遊戲

快閃小強 | 2021-03-14 15:32:57 | 巴幣 10 | 人氣 269

這篇主要教的內容是使用 Electron 來部署輸出你的 RPG Maker MV/MZ 的遊戲,
本次將使用 Intel 機種的 macOS 10.15.7 (Catalina) 作業系統來進行操作教學。

Electron 目前不支援 OS X 10.10 (Yosemite) 及較舊版本的作業系統。

另外也附上 HackMD 版本供大家方便瀏覽

1.首先到以下網址下載對應 macOS 作業系統的 Node.js 並安裝到電腦。


 

2.安裝完成後,在右上角的「Spotlight 搜尋」輸入並執行「終端機」,接著分別輸入node -vnpm -v檢查是否有成功安裝。



3.建立一個檔案夾,並使用「終端機」的cd指令來指定檔案夾的路徑,然後在「終端機」分別依序輸入以下指令:

npm init -y
npm i --save-dev electron
npm i --save-dev electron-builder

※ 記得你的檔案夾名稱只能英文數字且不含半行空白,如果有需要用到半形空白,請用半形底線( _ )替代半形空白。

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 gametitleString = "遊戲測試"; //遊戲標題    

const isTest = false; //是否為測試模式    
const testString = isTest ? '?test' : '';    

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 (event, arg) {        
shell.openExternal(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.著在 Electron 用的輸出檔案夾內開啟「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": "遊戲測試",  //遊戲名稱。
  "asar": false,
"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-builder": "^22.11.7"
   }
}


另外,如果你需要將遊戲內容進行加密封裝的話,只要在「package.json」中把 "asar": false, 改  為 "asar": true, 就好。


【示範】


※ 接下來將分為兩種專案並修改在 js 資料夾下的一些內容。

【RPG Maker MZ】
分別修改「main.js」、「rmmz_core.js」、「rmmz_managers.js」為以下內容。
 
main.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"));        
}    
}

rmmz_core.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) {    
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;
};

rmmz_managers.js

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」為以下內容。

rpg_core.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();            
};        
}    
}
};
};

rpg_managers.js

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, '../../www/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直接下載懶人包。

8.回到「終端機」並輸入npm start檢查是否能正常執行遊戲,如果執行遊戲 OK 的話,輸入npm run dist後就能執行部署輸出。

9.最後,部署輸出完成後,可以在「dist/mac-universal/」檔案夾中找到部署輸出好的內容,執行(遊戲名稱).app即可。

【RPG Maker MZ】

【RPG Maker MV】

※ 如果你是使用 RPG Maker MV 的情況,為了能夠正常使用存檔功能,請按照以下步驟即可。

在輸出好的(遊戲名稱).app按右鍵選擇「顯示套件內容」,並在「Contents」檔案夾裡面建立一個「www」的檔案夾,然後按右鍵選擇「取得資訊」後,將「共享與權限」都改為「讀取與寫入」即可。



參考來源






送禮物贊助創作者 !
0
留言

創作回應

相關創作

更多創作