累计送礼: 0 个 累计收礼: 0 个 TA的每日心情 开心 昨天 17:34
签到天数: 112 天
连续签到: 2 天
[LV.6]常住居民II
管理员
VIP
6
卡币
10541
OK点
16
推广点
0
同能卷
0
积分 13299
游戏跑在nw.js上。nw.js可以让DOM能够直接调用Node.js的模块。这样就可以用HTML,CSS和JS来写应用程序。RPG Maker 这么做是一是为了简化各平台上的分发,二是可以用web技术来开发整个引擎。
NW.js (previously known as node-webkit) lets you call all Node.js modules directly from DOM and enables a new way of writing applications with all Web technologies.
JAVASCRIPT 代码
const main = new Main(); main.run(); 复制代码
Main.run() 中一共有四步
JAVASCRIPT 代码
run(){ this.showLoadingSpinner(); this.testXhr(); this.hookNwjsClose(); this.loadMainScripts(); } 复制代码
第一步是显示一个读取界面的spinner动画,这是用css实现的
JAVASCRIPT 代码
showLoadingSpinner(){ const loadingSpinner = document.createElement("div"); const loadingSpinnerImage = document.createElement("div"); loadingSpinner.id = "loadingSpinner"; loadingSpinnerImage.id = "loadingSpinnerImage"; loadingSpinner.appendChild(loadingSpinnerImage); document.body.appendChild(loadingSpinner); } 复制代码
对应的css可以在css/game.css 中找到
CSS 代码
#loadingSpinner{ margin: auto; position: absolute; top: 0px; left: 0px; right: 0px; bottom: 0px; width: 120px; height: 120px; z-index: 10; } #loadingSpinnerImage{ margin: 0px; padding: 0px; border-radius: 50%; width: 96px; height: 96px; border: 12pxsolid rgba(255, 255, 255, 0.25); border-top: 12pxsolid rgba(255, 255, 255, 1); animation: fadein 2s ease, spin 1.5s linear infinite; } 复制代码
读取动画的效果:
第二步会测试XHR。XHR就是XMLHttpRequest,是微软在IE5发布的时候第一次引入的功能,允许js脚本向服务器发起HTTP的请求,是AJAX技术的核心。这里测试的方法就是发送一个XMLHttpRequest的GET请求,获取当前运行的脚本。要注意这里的测试是异步的,因为测试的结果暂时还没有用到。
JAVASCRIPT 代码
testXhr(){ const xhr = new XMLHttpRequest(); xhr.open("GET", document.currentScript.src); //initializes a newly-created request, or re-initializes an existing one - open(method, url) xhr.onload = () => (this.xhrSucceeded = true); xhr.send(); //sends the request to the server. } 复制代码
第三步是个补丁,注释说关闭窗口的时候nw.js进程可能有时候不会被终止,所以这里强制把窗口关闭事件和退出app绑定在一起。
JAVASCRIPT 代码
hookNwjsClose(){ // [Note] When closing the window, the NW.js process sometimes does // not terminate properly. This code is a workaround for that. if(typeof nw === "object"){ nw.Window.get().on("close", () => nw.App.quit()); } } 复制代码
第四步才是整个过程的核心,把每一个script都放上DOM来load。这些script都用defer但不是async的方法来读取,也就是在后台异步下载,然后在DOM ready的时候按顺序执行。每个script在load完成之后会更新loadCount,最后一个script读取完之后,PluginManager会开始设置插件。在window load完之后,这时候才来检查之前的XHR是不是成功。然后检查是不是游戏在/private/var里面运行,不然的话可能存不了档,这一段不是太重要就不贴了。然后会通过调取initEffekseerRuntime() 来初始化Effekseer这个用来实现粒子效果的工具。
JAVASCRIPT 代码
const scriptUrls = [ "js/libs/pixi.js", "js/libs/pako.min.js", "js/libs/localforage.min.js", "js/libs/effekseer.min.js", "js/libs/vorbisdecoder.js", "js/rmmz_core.js", "js/rmmz_managers.js", "js/rmmz_objects.js", "js/rmmz_scenes.js", "js/rmmz_sprites.js", "js/rmmz_windows.js", "js/plugins.js" ]; loadMainScripts(){ for(const url of scriptUrls){ const script = document.createElement("script"); script.type = "text/javascript"; script.src = url; script.async = false; script.defer = true; script.onload = this.onScriptLoad.bind(this); script.onerror = this.onScriptError.bind(this); script._url = url; document.body.appendChild(script); } this.numScripts = scriptUrls.length; window.addEventListener("load", this.onWindowLoad.bind(this)); window.addEventListener("error", this.onWindowError.bind(this)); } onScriptLoad(){ if(++this.loadCount === this.numScripts){ PluginManager.setup($plugins); } } onScriptError(e){ this.printError("Failed to load", e.target._url); } onWindowLoad(){ if(!this.xhrSucceeded){ const message = "Your browser does not allow to read local files."; this.printError("Error", message); }elseif(this.isPathRandomized()){ const message = "Please move the Game.app to a different folder."; this.printError("Error", message); }elseif(this.error){ this.printError(this.error.name, this.error.message); }else{ this.initEffekseerRuntime(); } } onWindowError(event){ if(!this.error){ this.error = event.error; } } 复制代码
这里在初始化Effekseer之后就把读取的spinner给擦除了,然后我们会就可以读取Scene_Boot这个场景来进入到第一个画面。
JAVASCRIPT 代码
const effekseerWasmUrl = "js/libs/effekseer.wasm"; initEffekseerRuntime(){ constonLoad = this.onEffekseerLoad.bind(this); constonError = this.onEffekseerError.bind(this); effekseer.initRuntime(effekseerWasmUrl, onLoad, onError); } onEffekseerLoad(){ this.eraseLoadingSpinner(); SceneManager.run(Scene_Boot); } onEffekseerError(){ this.printError("Failed to load", effekseerWasmUrl); } 复制代码
顺便来研究一下这里显示错误信息的方式。错误有名称和消息。如果遇到了错误的话,首先会把读取动画给停止,然后插入一个errorPrinter的div来分别显示错误的名称和消息。
JAVASCRIPT 代码
printError(name, message){ this.eraseLoadingSpinner(); if(!document.getElementById("errorPrinter")){ const errorPrinter = document.createElement("div"); errorPrinter.id = "errorPrinter"; errorPrinter.innerHTML = this.makeErrorHtml(name, message); document.body.appendChild(errorPrinter); } } makeErrorHtml(name, message){ const nameDiv = document.createElement("div"); const messageDiv = document.createElement("div"); nameDiv.id = "errorName"; messageDiv.id = "errorMessage"; nameDiv.innerHTML = name; messageDiv.innerHTML = message; return nameDiv.outerHTML + messageDiv.outerHTML; } 复制代码
显示出的错误如下:
下一章节将会贴出关于场景管理器的研究。
本帖来自P1论坛作者etherstalker,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:
https://rpg.blue/forum.php?mod=viewthread&tid=493141 若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x