简介
小时候第一次接触到FC游戏,还是在一个小伙伴的家里,打开后插上卡,然后电视画面就变了,对我来说这看起来就像魔法一样,那么神奇,那么欢乐,一直陪伴了我整个童年。后来PC游戏,手机游戏的出现,画面越来越好玩法越来越多,但是始终没有童年的那个味道了。
jsnes
一次偶然的发现,我看到了jsnes(一款基于js的nes模拟),回忆起了童年,那时的回忆似乎可以在移动互联网时代得倒新生。
并且实际上存在许多基于jsnes的网站比如:http://fc.liflag.cn/
但是实际体验下来不是很好,另外需要手动导入游戏,无法做到想玩的时刻都能玩。
于是乎经过几周的奋战,终于做了一个在线的nes网站。将jsnes整合了下,核心代码如下。
import jsnes from '../open_source/jsnes' var SCREEN_WIDTH = 256; var SCREEN_HEIGHT = 240; var FRAMEBUFFER_SIZE = SCREEN_WIDTH * SCREEN_HEIGHT; var Simulator = function() { this.nes = null this.buffer = null this.framebuffer_u32 = null this.framebuffer_u8 = null this.audio = null this.romData = null this.saveData = null this.needUpdateFrame = false this.stopFrame = false } Simulator.prototype = { load: function(romData, saveData) { this.stop() this.romData = romData this.saveData = saveData this.buffer = new ArrayBuffer(FRAMEBUFFER_SIZE * 4); this.framebuffer_u8 = new Uint8ClampedArray(this.buffer); this.framebuffer_u32 = new Uint32Array(this.buffer); var that = this var nes = new jsnes.NES({ onFrame: function(framebuffer_24){ for(var i = 0; i < FRAMEBUFFER_SIZE; i++) { that.framebuffer_u32[i] = 0xFF000000 | framebuffer_24[i]; } that.needUpdateFrame = true }, onAudioSample: function(l, r){ that.audio(l, r) }, sampleRate: 44100, preferredFrameRate: 60 }); this.nes = nes if (null != saveData) { if (null == saveData.romData) { saveData.romData = romData } this.nes.fromJSON(saveData) } else { this.nes.loadROM(romData) } }, stop: function() { if (null != this.nes) { this.nes.reset() } this.nes = null this.audio = null this.romData = null this.saveData = null this.buffer = null this.framebuffer_u32 = null this.framebuffer_u8 = null this.needUpdateFrame = false this.stopFrame = false }, start: function(onAudio) { this.audio = onAudio // do frame first this.frame() }, reset: function(romData, saveData) { var audio = this.audio this.load(romData, saveData) this.start(audio) }, buttonDown: function(player, key) { this.nes.buttonDown(player, jsnes.Controller[key]); }, buttonUp: function(player, key) { this.nes.buttonUp(player, jsnes.Controller[key]); }, currentContext() { if (!this.start || null == this.nes) { return null } return this.nes.toJSON() }, frame() { if (!this.start || null == this.nes) { return } if (this.stopFrame) { return } this.nes.frame() }, frameBuffer() { var needUpade = this.needUpdateFrame this.needUpdateFrame = false return { render: needUpade, buffer: needUpade ? this.framebuffer_u8 : null } }, pause() { this.stopFrame = true }, resume() { this.stopFrame = false } } export { Simulator }
但实际上jsnes这个项目并不是很完整,可以看出内部逻辑可能也是参考了别的项目,代码非常丑陋(但是能用),甚至突然出现未定义的变量(根本不知道这变量哪来的,用来干嘛的)。以及仅仅支持了有限的格式(mapper类型,不同的游戏可能是不同的格式)。花了很大的精力增加了mapper,但是新增的mapper也不敢保证100%正确(能力有限)。
jsnes自带支持的mapper
Mappers[0]
Mappers[1]
Mappers[2]
Mappers[3]
Mappers[4]
Mappers[5]
Mappers[7]
Mappers[11]
Mappers[34]
Mappers[38]
Mappers[66]
Mappers[94]
Mappers[140]
Mappers[180]
新增的mapper(参考nesdev wiki)
Mappers[9]
Mappers[10]
Mappers[15]
Mappers[18]
Mappers[21]
Mappers[22]
Mappers[23]
Mappers[32]
Mappers[33]
Mappers[48]
Mappers[71]
Mappers[72]
Mappers[75]
Mappers[78]
Mappers[79]
Mappers[87]
Mappers[105]
Mappers[182]
node
当完成基本的游戏后,又一次小伙伴告知需要联机功能???于是连夜写了一个node服务,将游戏在node服务器中执行,然后每一帧都下发到浏览器上。
但是问题是显而易见的,一开始发现数据量很大(分辨率是512 * 480),每次都下发完整的rawdata带宽扛不住,后来用zip压缩后大概是1000-5000的长度,可以接受。然而所有的游戏都用我的服务器流量,钱包扛不住了。
所以使用node服务端执行游戏的方案实际上是行不通的。
webrtc
webrct是基于p2p的网络,通过coturn创建了stun和turn,并在同一台机器上起了node作为房间管理和rtc信令交换。
所有的运行在P1中,P2则被动接受游戏canvas生成的视频流,和audioContext产生的音频流。
目前存在的最大问题则是画面延迟,由于视频存在编解码的延迟,没有很好的方案解决。如果直接传递游戏帧的rawdata,则带宽扛不住(如果zip后再传,则p1直接卡死,性能太差)。
另外如果音视频流进行合并后会增加100ms的延迟,所以目前音视频流是单独处理用单独的组件播放的。
测试下来在30桢的录制下,P2和P1之间的延迟还是比较小的,大致50ms左右(2桢)
可以前往这里进行进入组队大厅体验(针对PC简单适配了下,PC可以通过键盘操作游戏)。
推荐本站淘宝优惠价购买喜欢的宝贝:
本文链接:https://hqyman.cn/post/5555.html 非本站原创文章欢迎转载,原创文章需保留本站地址!
休息一下~~