[Windows]Use Electron to deploy output RPG Maker MV/MZ games

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

This article was translated using "DeepL Translator".

This article will teach you to deploy your RPG Maker MV/MZ games using Electron.
This tutorial will use Windows 10 (1909) operating system.

Electron does not currently support Windows Vista and older versions of operating systems.

Update Info

2022/05/03 Update part of the article.
2022/05/01 Fix some contents of package.json.
2022/01/04 Add block the feature of capturing screen.
2021/11/12 Added encrypted content about asar files.

1.First, download Node.js for Windows OS from the following URL and install it on your computer.

https://nodejs.org/en/download/

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

2.After the installation is complete, type CMD at the beginning to run the "Command Prompt", and type node -v and npm -v respectively to check if the installation is successful.

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

3.After creating a folder, and add a new file named “package.json” and edit the file to add the following syntax content.

After adding the following syntax, remember to remove // and the comments that follow.

{ "name": "RPG_Game", //The project name used for the output. //It must be alphanumeric and not contain any half-space. "version": "1.0.0", //Version number. //The format must be in the numeric format X.X.X. "description": "This is the Electron Deployment Test.", //File description. "main": "index.js", //[RPG Maker MZ] "chromium-args": "--force-color-profile=srgb", //[RPG Maker MV] "js-flags": "--expose-gc", "build": { "appId": "com.rpgmaker.game", //Application ID. //*You can usually enter com.xxxxx.yyyyy in alphanumeric format. //xxxxx is the English name of your game title. //yyyyy is the author's/team's name. "productName": "Game", //Game Title. "asar": true, //It is recommended to set this item to true if you need to seal the game content... "afterPack": "./myAfterPackHook.js", // * If you need to use the "asarmor" encryption suite, please make sure to include this parameter. "win": { "icon": "icon/icon.ico", //Game icon, the image format is ico format. //* Use the png to ico online conversion site and place the ico format image in the icon folder. "target": { "target": "dir" } } }, "scripts": { "start": "electron .", "pack": "electron-builder --dir", "dist": "electron-builder" }, "author": "Mirai", //The author's/team's name. "copyright": "Copyright © 2021 ${author} All rights reserved.", //Copyright Notice. "devDependencies": { "electron": "^11.5.0", "electron-builder": "^22.5.1", "asarmor": "^2.0.0" // * If you need to use the "asarmor" encryption suite... } }

If you need to use the "asarmor" encryption suite, create a file called "myAfterPackHook.js" and put the following code into this file and save it in this output folder.

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); } };

[Demo]

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Then use the cd command on the "Command Prompt" to specify the path to the folder, and enter the npm install command on the "Command Prompt" to install the required package.

Remember that folder names can only be alphanumeric and do not contain a half-line margin. If you need to use a half-line margin, please replace it with a ( _ ).

4.Open the RPG Maker MV/MZ game project and click "File" → "Deployment", select "Web Browsers / Android / iOS".
When using RPG Maker MV, select "Web Browsers" and then click "OK" to wait for the deployment to complete.

[RPG Maker MZ]

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

[RPG Maker MV]

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

5.Create an "index.js" file ,and enter the following:

const { app, BrowserWindow, ipcMain, shell } = require('electron'); function createWindow() { const win = new BrowserWindow({ width: 816, //Screen Width height: 624, //Screen Height icon: 'icon/icon.png', useContentSize: true, autoHideMenuBar: true, backgroundColor: '#000000', webPreferences: { nodeIntegration: true, contextIsolation: false } }) const isTest = false; //Whether it is a test mode. 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); }); //* Required with NekoGakuen_BlockedCapture.js plugin. 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.Go to the output folder ,and copy all the files except "package.json" and paste them into the output folder for this of Electron.

[RPG Maker MZ]

[RPG Maker MV]

7.Next, we will divide it into two projects and modify some of the contents in the js folder.

[RPG Maker MZ]

Modify "main.js", "rmmz_core.js" and "rmmz_managers.js" to the following.

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") ); }

Replace..

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"; };

Replace..

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; };

Replace..

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/"); };

Replace..

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

Replace..

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]

Modify “rpg_core.js” and “rpg_managers.js” to the following.

rpg_core.js

Utils.isNwjs = function() { return typeof require === 'function' && typeof process === 'object'; };

Replace..

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; };

Replace..

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

Replace..

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/'); };

Replace..

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

Replace..

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; } } };

Replace..

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; } } };

If the above is very complicated, you can also download quick start template directly from the following GitLab.
https://gitlab.com/mmmm748748/rpg_maker_mv_and_mz_electron/-/tree/en_US/

8.Go back to "Command Prompt" and type npm start to check if the game can run normally. If the game runs OK, type npm run dist to run the deployment output.

9.Finally, after the deployment is finished, you can find the contents of the deployment in the "dist/win-unpacked/" folder and run Game.exe.

Reference Material

https://www.electronjs.org

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.electron.build

https://www.npmjs.com/package/asarmor

Mirai - Patreon:
https://www.patreon.com/MiraiDiary

Mirai - Twitter(X):
https://twitter.com/Mirai_so_Sad

Mirai - itch.io
https://miraisosad.itch.io

Mirai - Instagram
https://www.instagram.com/miraisosad/

tags: 貓咪學園 NekoGakuen RPG Maker RPG Maker MV RPG Maker MZ