# 以 Electronjs 部署將要上架到 Steam 的 RPG Maker MV/MZ 遊戲
:::danger
![](https://i.imgur.com/xQoThD6.png)
:::
## ■ Windows
---
### ◆ 下載並安裝 NekoGakuen_SteamworksAPI.js 插件
---
前往以下連結下載 NekoGakuen_SteamworksAPI.js 插件。
**【NekoGakuen_SteamworksAPI.js】**
https://rabbitteaparty.itch.io/nekogakuen-steamworksapi
將 NekoGakuen_SteamworksAPI.js 放在你的遊戲專案的「js/plugins」資料夾。
**【RPG Maker MV】**
![](https://i.imgur.com/sebLLNR.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/HGahQQB.png)
開啟你的遊戲專案,到「插件管理器」安裝 NekoGakuen_SteamworksAPI.js 插件,並設定相關的插件參數。
**【RPG Maker MV】**
![](https://i.imgur.com/HEN0B2r.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/rFMtNIR.png)
---
### ◆ 在遊戲專案中使用插件命令
---
在事件頁中新增「事件命令」,切換至頁面 3 後,選擇「插件命令…」,而插件命令的相關說明可在「插件管理器」的 NekoGakuen_SteamworksAPI.js 的幫助資訊中找到。
**【RPG Maker MV】**
![](https://i.imgur.com/uOnewKT.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/sF3BK6N.png)
---
### ◆ 部署你的遊戲
---
在左上角選擇「檔案」→「部署...」。
**【RPG Maker MV】**
![](https://i.imgur.com/lXpJZ0A.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/Ygeo8G3.png)
---
### ◆ 下載 Steamworks SDK
---
:::info
<i class="fa fa-info-circle" aria-hidden="true"></i> 需要有效的 Steamworks 開發人員身分的帳號。
:::
前往以下連結下載 Steamworks SDK 的檔案,而 SDK 版本至少要 1.5.0 以上。
**【Steamworks SDK】**
https://partner.steamgames.com/doc/sdk
![](https://i.imgur.com/Upa9Wi2.png)
---
### ◆ 下載 greenworks.js 檔案
---
前往以下連結後選擇「greenworks.js」。
**【greenworks.js】**
https://github.com/greenheartgames/greenworks/
![](https://i.imgur.com/rqm0qda.png)
![](https://i.imgur.com/L0tjqYi.png)
![](https://i.imgur.com/1BaWxnw.png)
---
### ◆ 下載 Node 的二進制檔案
---
:::info
<i class="fa fa-info-circle" aria-hidden="true"></i> 需要註冊 GitHub 帳號才能下載檔案。
:::
前往以下網址並在左邊的篩選條件之中,依序設定以下篩選條件:
**【Greenworks Prebuild】**
https://greenworks-prebuilds.armaldio.xyz
**Release Tag:**
「v0.6.0 - Steamworks v1.5.0」、
**OS:**
「Windows」
**Architecture:**
「64 bits」
**Runtime:**
「Electron」
**Versions:**
「v85」
然後勾選該項檔案後,在右上角選擇「DOWNLOAD」即可。
![](https://i.imgur.com/jW5I1hD.png)
:::info
<i class="fa fa-info-circle" aria-hidden="true"></i> 你也可以選擇其他版本,但需要跟之後部署的 Electron 版本完全一致,且必須在「v85」版本以前。
:::
---
### ◆ 下載並安裝 Node.js
---
前往以下網址下載 Node.js。
**【Node.js】**
https://nodejs.org/en/download/
![](https://i.imgur.com/2OEuO7c.png)
安裝完 Node.js,在開始功能表輸入`CMD`執行「命令提示字元」,並分別輸入`node -v`和`npm -v`檢查是否有成功安裝。
![](https://i.imgur.com/5Xc1hVn.png)
![](https://i.imgur.com/4NezzZ0.png)
---
### ◆ 配置 Electronjs 環境
---
建立一個資料夾後,新增一個名為「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」加密套件的話,請確保加入此項參數。
"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」加密套件的話...
}
}
```
:::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/F14E5fG.png)
建立一個「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 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();
}
});
```
將剛才部署後的除了「package.json」以外的所有遊戲內容複製到 Electronjs 資料夾。
**【RPG Maker MV】**
![](https://i.imgur.com/nFDZU8t.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/OgrgQPr.png)
接下來將分為兩種專案並修改在 js 資料夾下的一些內容。
**【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;
}
}
};
```
**【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();
}
}
};
```
:::info
<i class="fa fa-info-circle" aria-hidden="true"></i> 如果上述內容覺得非常複雜的話,也可以到以下GitLab直接下載懶人包。
https://gitlab.com/mmmm748748/rpg_maker_mv_and_mz_electron/-/tree/zh_TW/
:::
---
### ◆ 準備在 Electronjs 進行部署
---
在「index.html」檔案所在的路徑,建立一個「preload.js」的檔案,並輸入以下內容:
```javascript=
const electron_os = navigator.platform;
const greenworksCore = electron_os.indexOf("Win") > -1 ? require('../../greenworks') : require('../greenworks');
```
**【RPG Maker MV】**
![](https://i.imgur.com/nMGxXqv.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/YviKPIw.png)
修改「index.html」檔案內容,並在`</body>`之前,加入以下內容:
```html=
<script type="text/javascript" src="preload.js"></script>
```
**【RPG Maker MV】**
![](https://i.imgur.com/Asqo7td.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/cBx6PRP.png)
使用「命令提示字元」並輸入`npm run dist`後執行部署輸出。
---
### ◆ 配置 Steamworks SDK
---
部署輸出完成後,在「dist/win-unpacked/index.html」檔案所在的路徑,新增名為「lib」的資料夾。
**【RPG Maker MV】**
![](https://i.imgur.com/qfzLnI0.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/wr4gaYe.png)
將下載的 Steamworks SDK 壓縮檔解壓縮之後,從 sdk 資料夾裡取得一些特定檔案,並複製到「lib」資料夾,如以下的路徑所示:
「sdk \ redistributable_bin \ **steam_api.dll**」 → 「Electronjs \ dist \ win-unpacked \ lib \ **steam_api.dll**」
「sdk \ redistributable_bin \ **steam_api.lib**」 → 「Electronjs \ dist \ win-unpacked \ lib \ **steam_api.lib**」
「sdk \ redistributable_bin \ win64 \ **steam_api64.dll**」 → 「Electronjs \ dist \ win-unpacked \ lib \ **steam_api64.dll**」
「sdk \ redistributable_bin \ win64 \ **steam_api64.lib**」 → 「Electronjs \ dist \ win-unpacked \ lib \ **steam_api64.lib**」
「sdk \ public \ steam \ lib \ win64 \ **sdkencryptedappticket64.dll**」 → 「Electronjs \ dist \ win-unpacked \ lib \ **sdkencryptedappticket64.dll**」
「sdk\public\steam\lib\win64\sdkencryptedappticket64.lib」 → 「Electronjs \ dist \ win-unpacked \ lib \ **sdkencryptedappticket64.lib**」
**【RPG Maker MV】**
![](https://i.imgur.com/Oqtc97P.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/4VnUuVQ.png)
將下載的 Node 二進制檔案和 greenworks.js 分別放在以下路徑:
**greenworks-win64.node** → Electronjs \ dist \ win-unpacked \ lib \ **greenworks-win64.node**
**greenworks.js** → Electronjs \ dist \ win-unpacked \ **greenworks.js**
**【RPG Maker MV】**
![](https://i.imgur.com/8NCAba6.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/CamoeSe.png)
在 Electronjs 的 Game.exe 所在的資料夾,按滑鼠右鍵選擇「新增」→「文字文件」,並命名為「steam_appid.txt」,之後編輯steam_appid.txt內容,並輸入你在 Steam 平台上要發行遊戲的 App ID。
**【RPG Maker MV】**
![](https://i.imgur.com/JwzLYRh.png)
**【RPG Maker MZ】**
![](https://i.imgur.com/WW5dMH5.png)
---
### ◆ 配置 Steamworks 啟動選項
---
在 Steamworks 後台的「應用程式管理員」頁面,新增「啟動選項」並加入參數`--in-process-gpu --disable-direct-composition`。
![](https://i.imgur.com/tFHAwYw.png)
## ■ macOS
(準備中...)
《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: `貓咪學園` `NekoGakuen` `RPG 製作大師` `RPG Maker MV` `RPG Maker MZ`