把 NES 模拟器搬到鸿蒙PC,再连个蓝牙手柄找回童年的感觉
最近晚上闲来无事把手头的 MateBook Pro 翻出来折腾。HarmonyOS 的应用市场逛了一圈没找到好用的 NES 模拟器。想连个蓝牙手柄找回童年的感觉。为什么要干这事事情是这样的翻了翻应用市场现成的 NES 模拟器不好用且无论是虚拟按键还是键盘体验都是不好想支持下接入蓝牙手柄可定制放大屏幕没有源码就没法搞。那就自己搞一个吧。FCEUX 是我比较熟悉的模拟器代码质量高核心部分极其纯净——几乎不依赖操作系统 API纯标准 C 就能编译。于是就有了这个项目ohos_nes_fceux一个跑在 HarmonyOS 上的红白机模拟器基于经典的 FCEUX 的老牌NES模拟器核心。更多交流学习欢迎加入开源鸿蒙PC社区https://harmonypc.csdn.net/欢迎在PC社区平台申请新建项目https://atomgit.com/OpenHarmonyPCDeveloper项目开源地址https://gitcode.com/qq8864/ohos_nes_fceux当然这个移植借助了AI的能力如果你做过 AI 应用或自动化脚本多半遇到过同一种疲惫每家厂商一套账号、一套密钥、一套计费口径想在项目里换个模型常常不是「改一行参数」这么简单而是「再集成一遍」。如果你想体验国外厉害的大模型能力却总是被禁或者服务不稳定。推荐下taotoken这个是csdn官方推出的产品速度流畅稳定可靠。 关键是很便宜性价比不错。taotoken尝鲜入口https://taotoken.net/?uinv_faxm8m42tg11a06futm_sourceinviteTaotoken 的方向很直白把「多模型」收敛成「一条统一网关」。它是 CSDN 生态里的 AI 聚合与分发能力载体——面向开发者常见的调用路径做网关侧的路由与协议适配让你更少折腾基建更多时间花在产品与效果上。谐音梗“掏token”名字起的不错。以后AI时代token就是食粮越来越重要了。详细移植过程参见猫哥的博客把 FCEUX 移植到HarmonyOS鸿蒙PC一个 NES 模拟器的移植笔记使用atomcode deepseekdevcli鸿蒙知识库辅助。推荐atomcde太强了关于AtomCode参见小模型也能写出大工程——AtomCodeClaudeCode国产替代 的介绍及使用先看下最终效果左边方向键、中间游戏画面、右边 AB 键布局参考了横版红白机手柄的样式。顶部一排可以切换 xBRZ / HQ2x / HQ3x 等像素缩放滤镜。娃玩的不亦乐乎眼神里似乎想童年的自己两眼发光的感觉太好玩了。这项目是怎么一回事简单说就是把有 20 多年历史的 FCEUX 模拟器移植到了 HarmonyOS 上。FCEUX 是目前最活跃的 NES 模拟器之一代码质量很高核心部分6502 CPU、PPU 渲染、APU 音频、230 多种卡带映射器几乎不依赖操作系统 API。移植的核心思路是模拟器核心基本不动只重写驱动层和 UI。整体架构分四层ArkTS UI (Index.ets) ↓ NAPI 桥 C Native Layer (NAPI Module) ↓ 函数调用 HarmonyOS 驱动层 (ohos_driver) ↓ FCEUX 核心 API FCEUX 核心 (6502/PPU/APU/230 Mapper)视频用 XComponent Surface OH_NativeWindow 原生渲染像素数据直接从 C 写入缓冲区不走 Canvas音频OH_AudioRenderer NDK 原生播放APU 生成的 PCM 数据通过环形缓冲消费输入ArkTS 的onKeyEvent捕获按键 虚拟手柄触摸事件转成 8 位掩码传给核心蓝牙手柄到底能不能用这是个好问题。项目原本只写了键盘映射A/B/S/T 方向键和触屏虚拟手柄蓝牙手柄的支持其实是个半成品——D-Pad 方向键能用但右侧的 A/B/X/Y 功能键一律没反应。原因是不同的蓝牙手柄发送的按键码keyCode不一样代码里只硬编码了 PS4 手柄的几个按键码其他手柄比如 Switch Pro、Xbox、各种杂牌蓝牙手柄的按键码都没有匹配。手柄的接入很简单其实还是监听的onKeyEvent.onKeyEvent((event:KeyEvent){this.lastKeyTextevent.keyText;this.lastKeyCodeevent.keyCode;this.lastKeyTypeevent.type;letkt:stringevent.keyText;letkc:numberevent.keyCode;letisDown:boolean(event.type0);letbit:number-1;// keyText detection (letters arrow key names)if(ktkt.length0){lettkt.toUpperCase();if(tA||tKEYCODE_A)bit0;elseif(tB||tKEYCODE_B)bit1;elseif(tS||tKEYCODE_S)bit2;elseif(tT||tKEYCODE_T)bit3;elseif(tKEYCODE_DPAD_UP)bit4;elseif(tKEYCODE_DPAD_DOWN)bit5;elseif(tKEYCODE_DPAD_LEFT)bit6;elseif(tKEYCODE_DPAD_RIGHT)bit7;}// keyCode fallback (keyboard PS4 gamepad)if(bit0){if(kc2012)bit4;// Keyboard Upelseif(kc2013)bit5;// Keyboard Downelseif(kc2014)bit6;// Keyboard Leftelseif(kc2015)bit7;// Keyboard Right// PS4 gamepadelseif(kc2301)bit0;// × (Cross) → NES Aelseif(kc2302)bit0;// ○ (Circle) → NES Belseif(kc2311)bit2;// SHARE → NES Selectelseif(kc2312)bit3;// OPTIONS → NES Startelseif(kc19)bit4;// D-Pad Upelseif(kc20)bit5;// D-Pad Downelseif(kc21)bit6;// D-Pad Leftelseif(kc22)bit7;// D-Pad Rightelseif(kc2303)bit0;// □ (Square) → NES A (alt)elseif(kc2304)bit1;// △ (Triangle) → NES B (alt)elseif(kc2307)bit2;// L1 → NES Select (alt)elseif(kc2308)bit3;// R1 → NES Start (alt)}if(bit0){if(isDown)this.padState|(1bit)elsethis.padState~(1bit)}})从某多多上花三十块大洋就买到一个不错的蓝牙手柄。手柄首次蓝牙接入方法参见你买的手柄提供的说明书。怎么调试手柄键值我在底部加了一个调试显示条格式是这样的Key: 按键名 [code键值 type0按下/1松开]打开游戏后连上蓝牙手柄按右侧的功能键底部的绿色文字会实时显示对应的 keyCode。比如说你按了手柄的 A 键底部显示Key: [code2301 type0]那 2301 就是这个手柄的 A 键码。Type0 表示按下Type1 表示松开。怎么把自己的手柄键值加进去找到entry/src/main/ets/pages/Index.ets文件在onKeyEvent处理函数里有一段 keyCode 匹配的代码// keyCode fallbackif(bit0){// ... 原有映射elseif(kc2301)bit0;// × (Cross) → NES Aelseif(kc2302)bit1;// ○ (Circle) → NES Belseif(kc2311)bit2;// SHARE → NES Selectelseif(kc2312)bit3;// OPTIONS → NES Start// ... 更多映射}NES 手柄的 8 个键对应的比特位是BitNES 按键0A1B2Select3Start4↑ (上)5↓ (下)6← (左)7→ (右)假设你的蓝牙手柄按 A 键显示 code2301那添加一行else if (kc 2301) bit 0;就能把那个键映射到 NES 的 A 键。同理B 键是 bit1Select 是 bit2Start 是 bit3。加完之后重新编译安装手柄的按键就能正常玩游戏啦。踩过的几个坑坑 1顶部滤镜按钮拦截手柄事件一开始发现手柄的方向键能用但功能键老是触发顶部的滤镜切换。查了一下原来是顶部按钮获得了焦点手柄按键激活了按钮的onClick。解决给所有顶栏按钮加.focusable(false)让它们不参与焦点导航手柄事件直接穿透到游戏的onKeyEvent处理器。坑 2按键响应慢每次按键都通过 NAPIJS ↔ C 桥调用一次setPadState频繁的跨语言调用开销不小。解决改成帧循环模式——所有按键只更新 ArkTS 侧的位掩码纯 JS 操作帧循环每 16ms 一次统一把状态同步到 C 层。NAPI 调用从每次按键都触发变成每帧最多一次。坑 3音频没声音FCEUX 的音频数据是 int32 格式但值域其实在 int16 范围内。直接(int16_t)sample转就行但我不小心多写了个 8结果声音衰减到 1/256差不多静音。查了大半天 hilog 日志才发现。性能表现在 MateBook Pro 上实测帧率稳定 60fps内存 ~50MBCPU 占用 ~15%单核HAP 包 6.6MB后续想加的功能存档 / 读档FCEUX 核心支持完整缺个 UI自定义按键映射在界面上可视化配置不用改代码金手指 Cheat 码输入开源项目代码在 gitcode上有兴趣的朋友可以直接拿去编译玩玩https://gitcode.com/qq8864/ohos_nes_fceux欢迎 PR尤其是各种蓝牙手柄的按键码——收集齐了就能做一个通用的手柄映射库大家都不用重复踩坑了。

相关新闻

SerpBase vs Bright Data:小而精的 SERP API 对上企业级 scraping 巨头,差在哪

SerpBase vs Bright Data:小而精的 SERP API 对上企业级 scraping 巨头,差在哪

测评结论: 这不是同一类产品的对比。Bright Data 是「能做任何 scraping 的大平台」,SerpBase 是「Google SERP 专项」。用错方向会多花 3-5 倍钱。 大企业数据中台选 Bright Data;中小团队和 AI 项目选 SerpBase。评分表维度SerpBaseBright …

2026/6/25 12:37:18阅读更多 →
物理安全控制、规划、政策和措施指南

物理安全控制、规划、政策和措施指南

制定实体安全规划可能让人感到不知所措,而且很难知道从何入手。如今,许多公司将精力集中在网络安全上——毕竟,现代企业在日常运营中高度依赖数据和IT基础设施。然而,实体安全计划同样至关重要。平衡线上和线下安全措施有助于全方…

2026/6/25 12:32:18阅读更多 →
RTOS vs Linux性能实测:Zephyr在i.MX RT1050上实现百倍优势

RTOS vs Linux性能实测:Zephyr在i.MX RT1050上实现百倍优势

1. 项目概述与核心价值在嵌入式开发领域,选对操作系统(OS)往往是项目成败的关键一步。很多工程师都面临过这样的抉择:是选择功能全面但相对“臃肿”的Linux,还是选择轻量、实时但生态可能稍逊的实时操作系统&#xff0…

2026/6/25 12:32:18阅读更多 →
系统级工具链开发:Cargo 工作区管理与并发安全的工程实践

系统级工具链开发:Cargo 工作区管理与并发安全的工程实践

系统级工具链开发:Cargo 工作区管理与并发安全的工程实践一、工具链项目的复杂度陷阱:为什么需要工作区 当项目从一个单文件工具演进为包含 CLI、核心库、插件系统和配置管理的工具链时,Cargo 的单包结构会暴露三个核心问题: 编译…

2026/6/25 14:03:11阅读更多 →
LangChain和LangGraph是什么

LangChain和LangGraph是什么

🧠 1. LangChain 是什么? LangChain 是一个大模型应用开发框架,核心作用是帮你把 LLM(如 GPT)和外部能力“拼装起来”。 它主要提供: Prompt 管理 RAG(检索增强生成) 工具调用&a…

2026/6/25 14:03:11阅读更多 →
故障确认——跨越“条件跟随”与“系统边界”的陷阱

故障确认——跨越“条件跟随”与“系统边界”的陷阱

严谨的交叉验证与系统边界确认: 绝对不接没有做过严谨A-B-A Swap的案子,但同时要警惕“个案跟芯”的迷惑性。在认定芯片失效前,用高带宽示波器排查客户板上的电源上电时序、热插拔浪涌、地弹噪声。这颗芯片可能并非主动失效,而是对…

2026/6/25 14:03:11阅读更多 →
Genie 3世界模型:从AI生成到可交互物理模拟的范式跃迁

Genie 3世界模型:从AI生成到可交互物理模拟的范式跃迁

1. 这不是视频生成器,而是一扇通往可交互数字世界的门你有没有试过对着一张风景照发呆,心里想着“要是能走进去走一圈该多好”?或者在设计一个游戏关卡时,反复调整3D建模软件里的地形参数,花掉一整个下午却只改出几棵树…

2026/6/25 14:03:11阅读更多 →
全网视频免费下载工具,1080P/4K高清一键解析,Videdown安装包下载

全网视频免费下载工具,1080P/4K高清一键解析,Videdown安装包下载

刷到一个不错的剪辑教程想存下来反复看,结果平台提示要开会员才能下载——这种事我碰到不止一次了。后来朋友给我推了个叫Videdown的小工具,开源免费,没广告没弹窗,用了一段时间觉得确实靠谱。一千多个平台通吃这玩意儿支持的平台…

2026/6/25 14:03:11阅读更多 →
Token(词元),5分钟彻底搞懂

Token(词元),5分钟彻底搞懂

如果你习惯看视频,就看《4. Token(词元),看会动画敲下代码,就彻底搞懂了》,喜欢看文章就接着往下看。 Token的优化过程如下 大模型单次调用的总消耗 Token 由两部分组成:总消耗 Token 输入 Token 输出 Token。其中&a…

2026/6/25 13:58:10阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/25 9:39:54阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/25 2:52:24阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/25 9:01:34阅读更多 →
面试辅助工具横评:我试了5款AI面试工具,最后留下了OfferGo

面试辅助工具横评:我试了5款AI面试工具,最后留下了OfferGo

上半年跳槽,面了十几家公司。说句实话,不是能力不行,是面试现场太容易崩了。 明明准备了一周,面试官换个问法脑子就一片白。面完之后那个懊悔——其实我会的。 后来开始试市面上的AI面试辅助工具。前前后后装了5款,踩…

2026/6/25 11:52:11阅读更多 →
Claude Code 提示词设计:从塑造“人格”到建立“状态机”

Claude Code 提示词设计:从塑造“人格”到建立“状态机”

当前 AI Agent 设计的核心痛点在于:大模型不缺写代码的能力,缺的是克制力、边界感和验证逻辑。Prompt 不再是用来塑造“人格”的,而是用来建立“状态机(State Machine)”和“行为门禁(Guardrails&#xff0…

2026/6/25 11:52:11阅读更多 →
MC-037 | 自定义 Skill 开发:创建你的AI能力模块

MC-037 | 自定义 Skill 开发:创建你的AI能力模块

MONKEYCODE 教程系列 MonkeyCode教程及推广系列 MC-037 自定义 Skill 开发:创建你的AI能力模块 >官网链接注册更放心哦https://monkeycode-ai.com/?ic019e0aed-c823-783c-b08a-4f030f891e4e 系列: 不爱土豆唯爱马铃薯 MonkeyCode 教程系列 字数: 约 1400 字…

2026/6/25 11:52:11阅读更多 →