一、NearPlay项目定位与愿景
1.1 我们要做什么
NearPlay是一款运行在HarmonyOS上的附近社交游戏平台。它将"桌游"“附近的人”"语音社交"三个场景融合为一,让用户在物理空间中通过手机就能找到附近的桌游玩伴、加入实时游戏房间、通过语音发言完成游戏流程,最终实现从线上匹配到线下见面交友的完整闭环。
传统的桌游场景存在三个核心痛点:第一,组织成本高——想玩一局狼人杀需要六到十二人,但身边随时凑得出这么多人并不容易;第二,规则学习成本高——每一款新桌游都有一套规则体系,新手往往需要老玩家口述规则、担任法官,入门门槛陡峭;第三,社交连接弱——即便在桌游吧认识了几位有趣的人,游戏结束后各走各路,缺少持续互动的载体。
NearPlay的解法是:用附近匹配引擎解决人数问题——打开App即可看到1-10公里内所有在线玩家,一键发起匹配,系统自动从近到远逐步扩大搜索半径直至凑齐;用内置裁判+流程驱动解决规则问题——每款游戏的阶段推进、倒计时、角色行动判定全部由代码自动完成,无需真人法官;用活动系统+即时聊天+RunAdvisor运动联动解决社交连接问题——游戏结束后可直接进入聊天、报名线下活动,甚至查看对方的运动计划和食谱,找到共同兴趣点。
1.2 目标用户画像
核心用户:18-35岁的都市年轻人,主要覆盖以下细分群体:
- 桌游爱好者:经常出入桌游吧,但苦于"缺人"或"想玩但不想出门"的用户。他们需要的是随时可匹配的线上桌游替代品。
- 社交破冰需求者:刚到新城市的大学生、新入职的职场人,希望用"一起玩游戏"这种低压力方式认识附近的人。
- 运动社交双重需求者:既爱跑步又爱社交的群体,RunAdvisor功能让他们在游戏中发现运动同好。
- 语音社交偏好者:不喜欢打字、更爱说话表达的用户,语音发言和语音输入让游戏体验更接近真实桌游。
用户的一天可能是这样:下班后打开NearPlay,看到附近800米内有个正在玩狼人杀的房间缺2人,一键加入;游戏中用语音发言辩论,发现自己和另一位玩家都喜欢跑步;游戏结束后查看对方的RunAdvisor运动计划,发现大家都是5公里标准跑爱好者,约了周末一起晨跑。
1.3 竞品分析:桌游小程序 vs NearPlay
当前市场上与NearPlay最接近的产品形态是微信桌游小程序(如"谁是卧底Online""狼人杀官方"等)。我们从七个维度进行对比:
| 维度 | 微信桌游小程序 | NearPlay |
|---|---|---|
| 平台 | 微信小程序,运行在WebView中 | HarmonyOS原生ArkTS,直接调用系统API |
| 附近发现 | 无,纯线上随机匹配 | 基于位置的附近匹配,1-10km渐扩搜索 |
| 游戏驱动 | 依赖真人法官或简化规则 | 内置全自动裁判,阶段推进+倒计时+角色判定 |
| 语音交互 | 部分支持语音房,但延迟高 | 原生CoreSpeechKit语音识别+语音发言 |
| 社交延续 | 游戏结束即散,无后续 | 聊天+活动系统+RunAdvisor运动联动 |
| 内容导入 | 固定题库,不可自定义 | 剧本杀支持外部.txt文件导入自定义剧本 |
| 隐私保护 | 无拉黑过滤机制 | 完整BlockModel,拉黑后全App过滤 |
微信小程序的优势是流量入口和跨平台,但本质上是"线上随机匹配"模式,无法解决"附近真实社交"这个核心需求。NearPlay选择HarmonyOS原生开发,虽然覆盖范围暂时局限于鸿蒙设备,但获得了三样小程序永远无法获得的能力:位置服务深度集成(APPROXIMATELY_LOCATION权限+距离排序)、原生语音识别(CoreSpeechKit回调式引擎)、系统级UI体验(ArkUI声明式渲染,60fps动画)。
二、核心功能全景图
2.1 ASCII功能全景图
┌────────────────────────────────── NearPlay ──────────────────────────────────┐
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 首页Tab │ │ 游戏Tab │ │ 活动Tab │ │ 消息Tab │ │ 我的Tab │ │
│ │ 附近在线 │ │ 游戏大厅 │ │ 线下活动 │ │ 通知+聊天 │ │ 个人+黑名单 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │ │
│ ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ ┌──────┴──────┐ │
│ │ 附近匹配引擎 │ │ GameRoom │ │ ActivityPub │ │ ChatPage │ │ BlockModel │ │
│ │ MatchEngine │ │ 匹配→路由 │ │ ActivityDet │ │ 文字/语音 │ │ 拉黑/取消 │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └─────────────┘ │
│ │ │ │ │ │
│ ┌──────┴────────────────┴────────────────┴────────────────┴──────┐ │
│ │ 6 款 桌 游 子 系 统 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │🐺狼人杀 │ │📜剧本杀 │ │🕵卧底 │ │🎨画猜 │ │💬真心话 │ │🔔反应快 │ │ │
│ │ │6-12人 │ │4-8人 │ │4-10人 │ │2-8人 │ │2-10人 │ │2-6人 │ │ │
│ │ │可导入× │ │可导入✓ │ │可导入× │ │可导入× │ │可导入× │ │可导入× │ │ │
│ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │
│ └───────┼───────────┼───────────┼───────────┼───────────┼───────────┼──────────┘ │
│ │ │ │ │ │ │ │
│ ┌───────┴───────────┴───────────┴───────────┴───────────┴───────────┴──────────┐ │
│ │ 共 享 基 础 设 施 │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │GameNetwork│ │VoiceInput│ │GameChat │ │Permission│ │RunAdvisor│ │ │
│ │ │WebSocket │ │Helper │ │Panel │ │Model │ │Model │ │ │
│ │ │@NetworkKit│ │CoreSpeech│ │文字+语音 │ │权限引导 │ │运动联动 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────── Kit 依 赖 层 ───────────────────────────────────┐ │
│ │ @kit.NetworkKit @kit.CoreSpeechKit @kit.CoreFileKit @kit.AbilityKit │ │
│ │ @kit.BasicServicesKit @kit.PerformanceAnalysisKit @kit.ArkUI │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
2.2 六款桌游详解
NearPlay内置六款风格各异的桌游,覆盖推理、表演、休闲、社交四大品类。每款游戏均配备自动裁判、阶段倒计时、语音发言集成和完整的流程驱动。
狼人杀(WerewolfGame):经典6-12人推理游戏。支持6种角色(村民/狼人/预言家/女巫/猎人/守卫),11个游戏阶段(角色分配→天黑→狼人行动→预言家查验→女巫用药→守卫守护→天亮→自由讨论→投票放逐→投票结果→胜负判定)。白天讨论阶段采用逐人限时发言制,每人30秒语音发言,轮到当前玩家时VoiceInput组件自动激活,其他玩家处于禁言状态。投票阶段10秒倒计时,超时视为弃权。胜负判定自动执行:狼人全灭→好人胜,狼人数量≥好人数→狼人胜。
剧本杀(ScriptKillGame):4-8人沉浸式推理游戏,最大特色是可导入外部剧本。内置"消失的画家"剧本(4角色+NPC管家+2幕剧情),同时支持通过@kit.CoreFileKit的DocumentViewPicker选择.txt文件导入自定义剧本内容。NPC台词采用模拟TTS朗读(按字数×200ms+1500ms间隔逐句显示),增强沉浸感。流程包括9个阶段:选择剧本→角色分配→幕介绍→NPC发言→自由讨论→线索公开→幕间过渡→最终投票→真相揭晓。
谁是卧底(UndercoverGame):4-10人语言推理游戏。8组词对(火锅/麻辣烫、包子/饺子、咖啡/奶茶等),支持三种角色类型(平民/卧底/白板)。描述阶段每人30秒,投票阶段30秒,淘汰后自动揭示角色身份。平民词和卧底词通过WordPair模型精确区分。
你画我猜(DrawGuessGame):2-8人休闲游戏,使用Canvas绘图。内置12个词库(苹果/太阳/房子/猫/飞机等),60秒/轮倒计时,支持触摸绘图(DrawPoint模型记录坐标+笔画+颜色+线宽)。猜词记录GuessRecord追踪每位玩家的猜测和正确性。
真心话大冒险(TruthOrDareGame):2-10人破冰游戏,独特的众包出题机制:当某位玩家选择真心话或大冒险后,其他玩家有8秒时间提交题目(crowdSource=8s),系统从提交中随机抽取一道。内置6道真心话题("你最近一次说谎是什么时候?"等)和6道大冒险题("给你最近联系的人发’我想你了’"等)作为保底题库。回答阶段20秒/轮。
看谁反应快(QuickReactGame):2-6人手速竞技游戏,改编自桌游"德国心脏病"。5种水果(香蕉/苹果/橙子/葡萄/草莓),5张桌牌,当桌面上某水果总数恰好为5时抢拍。每局自动生成60张洗牌牌组(每种水果12张),reactWindow阶段计时反应速度,误拍惩罚机制。
2.3 活动系统
活动系统是NearPlay将线上社交延伸到线下的核心桥梁。用户可以发布线下活动(ActivityPublish页),填写标题、描述、地点、游戏内容、最大参与人数。已发布的活动在活动Tab展示,显示距离、参与人数、报名状态。活动详情页(ActivityDetail)展示完整信息、已报名参与者列表,如果是发起人还能看到报名管理通知。报名后显示"查看定位"按钮,引导线下见面。
2.4 即时聊天与拉黑过滤
聊天系统(ChatPage)支持三种消息类型:文字消息(ChatMsgType.TEXT)、语音消息(ChatMsgType.VOICE,记录时长)、图片消息(ChatMsgType.IMAGE,通过@kit.CoreFileKit的PhotoViewPicker选择)。聊天页顶部显示对方头像和昵称,右上角"⋯"菜单可触发拉黑确认弹窗。拉黑后输入栏替换为"你已拉黑对方,无法发送消息"提示,并显示"取消拉黑"按钮。
拉黑过滤是全局性的——BlockModel采用模块级let变量+导出函数模式(而非单例类),在Index.ets的aboutToAppear中初始化,拉黑/取消拉黑操作通过syncBlockState()同步到所有数据列表:附近用户列表过滤掉被拉黑者,通知列表过滤掉被拉黑者的通知,聊天会话列表过滤掉被拉黑者的对话。这种设计确保一旦拉黑,用户在全App范围内都不会再看到对方的任何信息。
2.5 附近匹配与MatchEngine

附近匹配是NearPlay的差异化核心。GameRoom.ets实现了渐进式匹配:从1km开始,每1.5秒扩大1km搜索半径,直至找到4名在线玩家或达到10km上限。匹配进度通过Progress组件实时可视化。
匹配完成后进入游戏,GameRoom通过gameRoutes Record路由表分发到正确的游戏页面:
'1' → pages/game/WerewolfGame
'2' → pages/game/ScriptKillGame
'3' → pages/game/UndercoverGame
'4' → pages/game/DrawGuessGame
'5' → pages/game/TruthOrDareGame
'6' → pages/game/QuickReactGame
匹配时还可发起位置共享请求(LocationShareRequest),对方在弹窗中选择同意/拒绝(ShareStatus枚举:PENDING/ACCEPTED/REJECTED),双方同意后共享位置方便线下见面。
MatchEngine计算匹配分数基于两个维度:音乐匹配(同类型音乐+20分)和使用习惯匹配(App使用类别重叠最高80分),总分上限100分。分数映射为标签:≥80→"匹配度·极高",≥60→"匹配度·高",≥40→"匹配度·中",≥20→"匹配度·低",其余不显示。音乐匹配通过MusicModel的classifyGenre函数实现,内置10种音乐类型关键词分类器;使用习惯通过UsageModel的UsageProfile.fromRecords聚合App使用记录,提取top3类别后与对方对比。
2.6 RunAdvisor跨应用联动
RunAdvisor是NearPlay的特色功能,将社交从游戏延伸到运动领域。从附近用户列表点击"🏃"按钮进入UserRunProfilePage,查看对方的运动计划(RunPlan,3种类型:轻松跑/标准跑/耐力跑)和食谱(FoodRecipe,带getCalories()方法计算卡路里)。用户可以对运动计划和食谱点赞(LikeStore模块级likeMap + toggleLike/isLiked/getLikeCount),通过likeRefresh++ @State计数器触发UI刷新。
MockRunAdvisorData预置了8个附近用户的运动档案,每人包含1-2个运动计划和3个食谱,覆盖鸡胸肉/鱼肉/牛肉/虾仁/羊肉/猪瘦肉/三文鱼等蛋白质来源,西兰花/生菜/菠菜/黄瓜等蔬菜,米饭/燕麦/红薯/馒头/土豆等主食,以及各类水果。
2.7 语音识别与VoiceInputHelper
语音识别是NearPlay游戏体验的关键一环。VoiceInputHelper采用plain class(非@Component)设计,封装@kit.CoreSpeechKit的speechRecognizer.createEngine回调式API。初始化参数:language=‘zh-CN’、online=1(在线模式)、recognizerMode=‘short’(短语音识别),音频参数:PCM 16kHz单声道16bit。
VoiceInput组件(@Component UI包装器)通过200ms间隔的refreshTimer轮询VoiceInputHelper的状态(isListening/recognizedText/listenDuration),在UI上实时展示录音状态和识别文本。录音时长上限60秒,超时自动停止。识别完成后通过onVoiceResult回调将文本传回宿主游戏页面。
每款游戏的讨论/发言阶段都集成了VoiceInput:狼人杀在当前发言者是自己时显示、剧本杀在自由讨论时显示、卧底在描述阶段显示。这种"按需激活"设计避免了非发言者的误操作,同时禁言状态通过canSpeak @Prop传递控制。
三、技术选型深度论证
3.1 ArkTS vs TypeScript:30+条关键差异
ArkTS是HarmonyOS的专用开发语言,基于TypeScript但施加了大量严格限制以保障运行时性能和类型安全。以下从语法约束、类型系统、运行时差异三个维度列出30余条关键差异:
| 编号 | 差异项 | TypeScript | ArkTS | NearPlay中的应对 |
|---|---|---|---|---|
| 1 | 结构体声明 | class可用任意方式 | 必须用struct + @Component装饰器 | 所有页面组件均用struct |
| 2 | 状态管理 | 无内置响应式 | @State/@Prop/@Link/@Provide/@Consume | Index.ets用@State管理所有列表数据 |
| 3 | UI构建 | JSX/TSX | 声明式build() + @Builder | 所有页面用build()+@Builder模式 |
| 4 | 动态属性访问 | obj[dynamicKey] | 禁止arkts-no-dynamic-prop-access | gameRoutes用Record<string,string>固定key |
| 5 | Object.keys() | 可用 | 禁止arkts-no-obj-keys | 用已知key数组迭代 |
| 6 | for…in | 可用 | 禁止arkts-no-for-in | 改用for循环+已知key数组 |
| 7 | as类型断言 | 宽松 | 严格,仅限兼容类型 | getParams() as GameRoomParams等 |
| 8 | 联合类型 | 广泛支持 | 有限制,建议用enum | 所有游戏阶段用enum(WerewolfPhase等) |
| 9 | 泛型 | 完整支持 | 受限,部分场景需显式标注 | ForEach显式标注元素类型 |
| 10 | 闭包捕获 | var/let/均可 | 箭头函数捕获this | onClick用箭头函数 |
| 11 | 空值安全 | 可选链?. | 严格null检查 | params?.gameId ?? ‘’ |
| 12 | 静态方法 | class static | static of()工厂模式 | 所有Model类用static of()构造 |
| 13 | 单例模式 | 经典单例 | 不推荐class单例 | BlockModel用模块级let+导出函数 |
| 14 | 模块导出 | export default | 建议命名导出 | 所有类/函数用export显式导出 |
| 15 | 装饰器 | 实验性 | 一等公民@Component/@Entry | 每个页面@Entry+@Component |
| 16 | 数组操作 | 任意方法 | 部分受限,建议展开运算 | this.messages=[…this.messages,msg] |
| 17 | setTimeout | 返回number | 返回number | setInterval返回timerId |
| 18 | setInterval | 返回number | 返回number | speakerTimerId: number=-1 |
| 19 | Promise | 完整 | 支持async/await | requestPermission用async |
| 20 | Map/Set | 完整 | 受限,建议Record/数组 | likeMap用Record<string,number> |
| 21 | JSON.parse | 返回any | 必须as指定类型 | GameMessage.fromJson用Record<string,string |
| 22 | 展开运算 | 对象/数组均可 | 数组展开为主 | blockedList=[…blockedList,user] |
| 23 | 可选属性 | interface {x?: T} | class {x: T=‘’} | 所有属性给默认值而非可选 |
| 24 | interface | 广泛使用 | 受限,建议class | RouteParams用interface,ChatPageParams用class |
| 25 | 条件渲染 | if/else JSX | if/else在build()内 | WerewolfGame用if…else if阶段分发 |
| 26 | 列表渲染 | map() | ForEach() | 所有列表用ForEach+key生成器 |
| 27 | 组件传参 | props | @Prop/@Link | VoiceInput用@Prop canSpeak |
| 28 | 事件处理 | onClick={fn} | .onClick(()=>{}) | 所有事件用箭头函数 |
| 29 | 样式系统 | CSS | 链式调用属性 | .width(‘100%’).padding(12) |
| 30 | 生命周期 | useEffect等 | aboutToAppear/aboutToDisappear | 初始化在aboutToAppear,清理在aboutToDisappear |
| 31 | this引用 | 任意 | struct内this | this.getUIContext().getRouter() |
| 32 | 全局状态 | Redux等 | AppStorage/PersistentStorage | 当前用@State组件内管理 |
| 33 | 导入路径 | 相对/node_modules | 相对路径+@kit | import from ‘…/model/xxx’ |
| 34 | 枚举 | number/string enum | 均支持 | WerewolfRole等用number enum |
3.2 HarmonyOS SDK版本选择
NearPlay选择API 24 / SDK 6.1.1 (compileSdkVersion: 60101024),这一决策基于以下考量:
- API 24是HarmonyOS NEXT的稳定基线:低于此版本的SDK缺少CoreSpeechKit、CoreFileKit等关键Kit的完整支持。
- speechRecognizer.createEngine的回调式API在API 24中才成熟稳定,低于此版本可能仅支持Promise式创建,与我们的回调式VoiceInputHelper设计不兼容。
- **abilityAccessCtrl.createAtManager().requestPermissionsFromUser()**在API 24中返回PermissionRequestResult.authResults数组,低版本API结构不同。
- **@kit.NetworkKit的webSocket.createWebSocket()**在API 24中提供完整的事件回调模型(on(‘open’)/on(‘message’)/on(‘close’)/on(‘error’)),这是GameNetwork自动重连机制的基础。
- build-profile.json5中compatibleSdkVersion与targetSdkVersion均设为"6.1.1(24)",确保运行时兼容性。
3.3 WebSocket vs 分布式软总线
NearPlay选择WebSocket(@kit.NetworkKit)而非HarmonyOS特有的分布式软总线作为游戏通信方案,原因如下:
分布式软总线的限制:分布式软总线是HarmonyOS设备间近场通信的方案,理论上延迟更低、无需中心服务器。但它要求两端均为HarmonyOS设备且登录同一华为账号,且当前主要面向1对1的设备协同场景(如手机-平板-手表互联),不适合多玩家游戏房间这种N对N广播场景。
WebSocket的优势:WebSocket是互联网标准协议,支持跨平台客户端,可通过中心服务器实现房间管理、消息广播、状态同步。GameNetwork.ets封装了完整的WebSocket生命周期:connect→on(‘open’)→on(‘message’)→on(‘close’)→自动重连(最多5次,每次3秒间隔)。GameMessage模型定义了4种消息类型(ACTION/STATE/SYSTEM/PRIVATE),通过JSON序列化在WebSocket上传输。
当前状态:GameNetwork已完整实现但游戏页面尚未实际调用(使用Mock数据驱动),这为后续接入真实WebSocket服务端预留了干净的接口。
3.4 Mock策略
NearPlay采用深度Mock策略,所有外部数据源均通过Mock类提供静态数据,确保应用可在无后端、无网络的环境下完整运行。Mock设计遵循以下原则:
-
每个Model文件配套MockData类:MockUserData(8个附近用户)、MockGameData(6款游戏)、MockActivityData(4个活动)、MockChatData(4个会话+6条消息)、MockNotifyData(5条通知)、MockBlockData(1个初始拉黑用户)、MockMusicData(1首当前播放+8首附近播放)、MockUsageData(5条自身记录+8组附近记录)、MockRunAdvisorData(8个运动档案)、MockWerewolfData(8个玩家含6种角色)、MockScriptData(1个完整4人剧本)、MockUndercoverData(8组词对+6个玩家)、MockDrawGuessData(12个词+4个玩家)、MockTruthOrDareData(6+6道题+4个玩家)、MockQuickReactData(动态生成60张洗牌牌组)。
-
系统API Mock:AVSession(读取当前播放音乐)和usageStatistics(读取应用使用记录)是系统级API,普通应用无法直接调用。NearPlay在PermissionModel中声明了ohos.permission.MANAGE_MEDIA_RESOURCES和ohos.permission.BUNDLE_ACTIVE_INFO权限,但实际数据通过MockMusicData和MockUsageData提供,PermissionPage中的权限请求使用try-catch包裹,失败后静默降级。
-
Mock计时器常量:describe阶段=10s、discuss阶段=30s每人、vote阶段=10s、crowdSource众包出题=8s、round回合=20s。这些常量使游戏流程在模拟器中可完整走完。
3.5 为什么不用React Native/Flutter
| 维度 | React Native | Flutter | ArkTS (NearPlay) |
|---|---|---|---|
| HarmonyOS支持 | 社区实验性适配,无官方Kit | 社区ohos_flutter,API覆盖不全 | 官方一等公民,完整Kit生态 |
| 语音识别 | 需原生桥接 | 需Platform Channel | CoreSpeechKit直接调用 |
| WebSocket | 第三方库 | 第三方库 | @kit.NetworkKit内置 |
| 权限管理 | 需原生桥接 | 需Platform Channel | @kit.AbilityKit直接调用 |
| 文件选择 | 需原生桥接 | 需Platform Channel | @kit.CoreFileKit直接调用 |
| UI性能 | JS Bridge瓶颈 | Skia渲染,60fps | ArkUI声明式,原生渲染60fps |
| 声明式UI | 需JSX+第三方库 | Widget树 | build()+@Builder原生支持 |
| 状态管理 | 需Redux/MobX | 需Provider/Riverpod | @State/@Prop/@Link原生支持 |
| 包体积 | JS引擎+Bridge | Skia引擎≈5MB | 无额外引擎 |
结论:NearPlay的核心能力(语音识别、WebSocket、权限管理、文件选择、位置服务)全部深度依赖HarmonyOS Kit,跨平台框架的桥接成本远超收益,且无法保证Kit API的完整覆盖。
四、项目目录树逐文件解读
4.1 完整目录结构
NearPlay/
├── AppScope/
│ └── app.json5 # 应用级配置(bundleName等)
├── entry/
│ ├── build/ # 构建产物(自动生成)
│ │ └── default/generated/profile/
│ │ └── default/BuildProfile.ets # 构建配置生成文件
│ └── src/main/
│ ├── ets/
│ │ ├── entryability/
│ │ │ └── EntryAbility.ets # 应用入口Ability生命周期
│ │ ├── entrybackupability/
│ │ │ └── EntryBackupAbility.ets # 备份扩展Ability
│ │ ├── components/
│ │ │ ├── VoiceInput.ets # 语音输入UI组件
│ │ │ └── GameChatPanel.ets # 游戏内聊天面板组件
│ │ ├── model/
│ │ │ ├── ActivityModel.ets # 活动数据模型
│ │ │ ├── BlockModel.ets # 拉黑过滤模型(模块级let+导出函数)
│ │ │ ├── ChatModel.ets # 聊天会话与消息模型
│ │ │ ├── GameMessage.ets # 游戏网络消息协议
│ │ │ ├── GameModel.ets # 游戏项与房间模型
│ │ │ ├── GameNetwork.ets # WebSocket网络封装
│ │ │ ├── MatchEngine.ets # 附近匹配分数计算引擎
│ │ │ ├── MusicModel.ets # 音乐类型分类模型
│ │ │ ├── NotifyModel.ets # 通知消息模型
│ │ │ ├── PermissionModel.ets # 权限项定义模型
│ │ │ ├── RouteParams.ets # 页面路由参数接口
│ │ │ ├── RunAdvisorModel.ets # 运动计划+食谱+点赞模型
│ │ │ ├── UsageModel.ets # App使用记录与分类模型
│ │ │ ├── UserModel.ets # 附近用户与位置共享请求模型
│ │ │ ├── VoiceInputHelper.ets # 语音识别引擎封装(plain class)
│ │ │ └── game/
│ │ │ ├── DrawGuessModel.ets # 你画我猜数据模型
│ │ │ ├── QuickReactModel.ets # 看谁反应快数据模型
│ │ │ ├── ScriptKillModel.ets # 剧本杀数据模型
│ │ │ ├── TruthOrDareModel.ets # 真心话大冒险数据模型
│ │ │ ├── UndercoverModel.ets # 谁是卧底数据模型
│ │ │ └── WerewolfModel.ets # 狼人杀数据模型
│ │ └── pages/
│ │ ├── Index.ets # 主页(5-Tab:首页/游戏/活动/消息/我的)
│ │ ├── GameRoom.ets # 匹配房间页(渐扩匹配+路由分发)
│ │ ├── ChatPage.ets # 即时聊天页(文字/语音/图片+拉黑)
│ │ ├── ActivityDetail.ets # 活动详情页
│ │ ├── ActivityPublish.ets # 活动发布页
│ │ ├── PermissionPage.ets # 权限引导页(步进式授权)
│ │ ├── UserRunProfilePage.ets # 运动档案页(RunAdvisor联动)
│ │ └── game/
│ │ ├── WerewolfGame.ets # 狼人杀游戏页(11阶段+语音发言)
│ │ ├── ScriptKillGame.ets # 剧本杀游戏页(9阶段+文件导入+TTS)
│ │ ├── UndercoverGame.ets # 谁是卧底游戏页(5阶段+词对)
│ │ ├── DrawGuessGame.ets # 你画我猜游戏页(Canvas+4阶段)
│ │ ├── TruthOrDareGame.ets # 真心话大冒险游戏页(众包出题)
│ │ └── QuickReactGame.ets # 看谁反应快游戏页(水果牌+抢拍)
│ └── resources/
│ └── base/
│ ├── element/
│ │ ├── string.json # 字符串资源(EntryAbility_label="NearPlay")
│ │ ├── color.json # 颜色资源
│ │ └── float.json # 尺寸资源
│ ├── media/
│ │ ├── foreground.png # 前景图标
│ │ ├── background.png # 背景图标
│ │ ├── layered_image.json # 分层图标配置
│ │ └── startIcon.png # 启动画面图标
│ └── profile/
│ ├── main_pages.json # 页面路由注册表(13个页面)
│ └── backup_config.json # 备份配置
├── oh-package.json5 # 依赖声明(@ohos/hypium + @ohos/hamock)
├── build-profile.json5 # 构建配置(SDK版本+构建模式+模块定义)
├── hvigorfile.ts # Hvigor构建脚本入口
├── code-linter.json5 # 代码检查规则
└── docs/ # 项目文档
4.2 非.ets配置文件解读
module.json5:entry模块的核心配置文件。声明模块类型为"entry"(主模块)、设备类型为"phone"、入口Ability为"EntryAbility"、页面列表引用"$profile:main_pages"。requestPermissions数组声明三个运行时权限:ohos.permission.INTERNET(普通权限,自动授予)、ohos.permission.APPROXIMATELY_LOCATION(位置权限,inuse场景)、ohos.permission.MICROPHONE(麦克风权限,inuse场景)。后两个权限需要在PermissionPage中通过abilityAccessCtrl动态请求。
string.json:定义5个字符串资源。最关键的是EntryAbility_label值为"NearPlay"(应用名显示在桌面图标下方)、location_reason和microphone_reason是权限申请时向用户展示的理由文案。
main_pages.json:页面路由注册表,声明了13个页面路径。HarmonyOS只允许跳转到在此注册的页面。顺序决定了页面栈的默认行为,Index.ets作为第一个页面是应用启动页。
五、依赖与Kit引入
5.1 oh-package.json5分析
{
"modelVersion": "6.1.1",
"dependencies": {},
"devDependencies": {
"@ohos/hypium": "1.0.25",
"@ohos/hamock": "1.0.0"
}
}
modelVersion声明SDK模型版本为6.1.1,与build-profile.json5中的targetSdkVersion一致。dependencies为空——NearPlay的所有功能依赖均通过HarmonyOS Kit提供,无需第三方npm包。devDependencies中:
- @ohos/hypium 1.0.25:HarmonyOS单元测试框架,类似JUnit,支持assertEqual/assertTrue等断言方法。
- @ohos/hamock 1.0.0:HarmonyOS Mock框架,用于在测试中模拟模块依赖。
5.2 五个核心@kit.*引入详解
| Kit名称 | 引入位置 | 核心API | 引入原因 |
|---|---|---|---|
| @kit.NetworkKit | GameNetwork.ets | webSocket.createWebSocket() | WebSocket通信,支持游戏房间实时消息传输。注意:使用@kit.NetworkKit而非@kit.InternetKit,因为WebSocket API在NetworkKit中 |
| @kit.CoreSpeechKit | VoiceInputHelper.ets | speechRecognizer.createEngine() | 语音识别引擎,回调式创建,支持zh-CN在线短语音识别。同一Kit还提供TTS能力(ScriptKillGame中使用) |
| @kit.CoreFileKit | ChatPage.ets, ScriptKillGame.ets | picker.PhotoViewPicker(), picker.DocumentViewPicker(), fileIo | ChatPage用PhotoViewPicker选择图片发送,ScriptKillGame用DocumentViewPicker+fileIo导入外部剧本.txt文件 |
| @kit.AbilityKit | EntryAbility.ets, PermissionPage.ets, ChatPage.ets | UIAbility, abilityAccessCtrl.createAtManager(), common.UIAbilityContext | 应用入口生命周期管理、运行时权限请求、获取UIAbilityContext |
| @kit.BasicServicesKit | VoiceInputHelper.ets, GameNetwork.ets | BusinessError | 错误类型定义,WebSocket和语音识别的回调参数使用BusinessError |
此外还有两个辅助Kit:
- @kit.PerformanceAnalysisKit(EntryAbility.ets):hilog日志输出,DOMAIN=0x0000,标签’testTag’。
- @kit.ArkUI(EntryAbility.ets, DrawGuessGame.ets, QuickReactGame.ets):window窗口管理、display屏幕信息获取、CanvasRenderingContext2D绑画布绘图。
六、构建体系
6.1 hvigor构建系统
HarmonyOS使用hvigor作为构建工具,替代了传统的Gradle。hvigor基于TypeScript实现,配置文件为hvigorfile.ts。核心命令:
- hvigorw assembleHap:构建HAP包(HarmonyOS Ability Package),相当于Android的APK。
- hvigorw clean:清理构建产物,删除entry/build目录。
- hvigorw --mode module -p module=entry@default assembleHap:指定模块和target构建,用于增量编译。
6.2 build-profile.json5结构解析
build-profile.json5
├── app
│ ├── signingConfigs: [] # 签名配置(调试时为空)
│ ├── products
│ │ └── default
│ │ ├── name: "default"
│ │ ├── signingConfig: "default"
│ │ ├── targetSdkVersion: "6.1.1(24)"
│ │ ├── compatibleSdkVersion: "6.1.1(24)"
│ │ ├── runtimeOS: "HarmonyOS"
│ │ └── buildOption
│ │ └── strictMode
│ │ ├── caseSensitiveCheck: true
│ │ └── useNormalizedOHMUrl: true
│ └── buildModeSet
│ ├── { name: "debug" }
│ └── { name: "release" }
└── modules
└── entry
├── name: "entry"
├── srcPath: "./entry"
└── targets
└── default
├── name: "default"
└── applyToProducts: ["default"]
关键配置解读:
- strictMode.caseSensitiveCheck: true:启用大小写敏感检查,防止Windows/macOS路径大小写不一致导致的跨平台问题。这意味着import路径必须与文件系统实际大小写完全一致。
- strictMode.useNormalizedOHMUrl: true:使用标准化的OHM URL格式,确保模块引用路径统一。
- targetSdkVersion与compatibleSdkVersion相同:当前仅支持API 24+设备,不做低版本兼容。如果需要兼容更低版本,应将compatibleSdkVersion设为更低的API编号。
- buildModeSet:定义debug和release两种构建模式。debug模式不混淆、保留调试符号;release模式启用代码混淆和优化。
6.3 debug vs release
| 特性 | debug | release |
|---|---|---|
| 代码混淆 | 否 | 是(ProGuard规则) |
| 调试符号 | 保留 | 剥离 |
| SourceMap | 生成 | 不生成 |
| 日志输出 | hilog全部输出 | hilog.warn及以上 |
| 性能优化 | 无 | 内联/死代码消除 |
| HAP体积 | 较大 | 较小 |
| 签名 | 调试证书 | 发布证书 |
七、开发环境
7.1 DevEco Studio
NearPlay使用DevEco Studio作为IDE,这是HarmonyOS官方集成开发环境,基于IntelliJ IDEA。核心功能包括:
- ArkTS语言服务:实时代码检查、自动补全、arkts-*规则静态分析。
- 预览器(Previewer):实时预览ArkUI组件渲染效果,支持热重载。
- 模拟器管理:内置远程模拟器和本地模拟器管理界面。
- hvigor集成:菜单栏Build→Build Hap(s)/APP(s)直接触发hvigor构建。
- hdc工具集成:终端可直接使用hdc命令与设备交互。
7.2 SDK路径配置
HarmonyOS SDK安装路径需在DevEco Studio的File→Settings→HarmonyOS SDK中配置。SDK目录结构:
HarmonyOS SDK/
├── openharmony/ # OpenHarmony SDK(开源部分)
│ └── 24/ # API 24
│ ├── ets/ # ArkTS声明文件
│ └── native/ # Native开发头文件和库
└── harmonyos/ # HarmonyOS SDK(闭源部分)
└── 6.1.1/ # 版本6.1.1
├── ets/ # @kit.*声明文件
└── toolchains/ # 编译工具链
7.3 模拟器选择:Pura 90 vs Mate X7
NearPlay在两款模拟器上进行了测试验证:
Pura 90(推荐):正常启动,页面渲染流畅,WebSocket模拟连接成功,语音识别引擎创建成功(回调触发)。连接地址127.0.0.1:5555。此为日常开发推荐模拟器。
Mate X7:出现启动超时问题,疑似与模拟器GPU渲染模式或系统镜像版本有关。在Mate X7上测试时,App启动后停留在白屏阶段超过30秒,hilog显示Ability onCreate已执行但loadContent回调未触发。建议降级到Pura 90或使用真机调试。
hdc连接命令:
hdc tconn 127.0.0.1:5555 # 连接远程模拟器
hdc install entry-debug.hap # 安装HAP
hdc shell aa start -a EntryAbility -b com.example.nearplay # 启动应用
hdc shell hilog -t testTag # 查看应用日志
八、版本演进史
NearPlay从一个空白的HarmonyOS模板项目,经过10个主要迭代阶段,逐步演进为功能完整的附近社交游戏平台。以下是commit级别的版本叙事:
v0.1 — 空项目脚手架
初始创建HarmonyOS ArkTS项目,bundleName注册为com.example.nearplay。此时项目仅包含EntryAbility.ets(空白UIAbility)、Index.ets(空页面)、module.json5(基础配置,仅INTERNET权限)、oh-package.json5(空依赖)。build-profile.json5设定targetSdkVersion=“6.1.1(24)”。项目可编译通过但没有任何业务功能,只是DevEco Studio生成的标准模板。
v0.2 — 5-Tab主框架
在Index.ets中搭建Tabs容器,barPosition设为BarPosition.End(底部Tab栏),创建5个TabContent:首页、游戏、活动、消息、我的。每个Tab先用占位Text显示Tab名称。@State currentTab记录当前选中Tab,Tabs.onChange回调更新currentTab。这是整个应用的骨架,后续所有功能都在这5个Tab上生长。
v0.3 — 6款游戏模型与大厅
创建GameModel.ets,定义GameItem(id/name/icon/description/playerMin/playerMax/type/canImportContent)和GameType枚举(PARTY/WEREWOLF/SCRIPT/CASUAL)。MockGameData.getGames()返回6款游戏数据。游戏Tab的GameContent用List+GameDetailCard展示游戏列表,首页Tab的HomeContent用Grid+GameQuickCard展示热门游戏快捷入口。点击游戏卡片创建GameRoomParams参数对象,通过router.pushUrl跳转到GameRoom页。
同时创建6款游戏的基础Model文件:WerewolfModel(角色/阶段/夜晚行动/投票结果)、ScriptKillModel(剧本/角色/NPC/幕/台词)、UndercoverModel(玩家/角色类型/词对/阶段)、DrawGuessModel(玩家/绘图点/猜测记录)、TruthOrDareModel(玩家/选择类型/问题提交/阶段)、QuickReactModel(玩家/水果牌/水果类型/阶段)。每款游戏对应一个MockData类提供测试数据。
v0.4 — 游戏页面实现
逐一实现6款游戏页面。每款游戏页面遵循相同模式:@Entry+@Component装饰、@State管理阶段和玩家状态、aboutToAppear初始化数据、build()用if-else分发不同阶段的UI、GameChatPanel作为底部聊天组件。
WerewolfGame.ets实现了完整的11阶段流程,包括角色分配时点击查看身份、狼人选择击杀目标、预言家查验并显示结果、女巫使用解药/毒药、守卫选择守护对象、天亮显示死亡信息、白天逐人30秒发言、投票10秒倒计时、胜负判定。VoiceInput组件在当前发言者是自己时激活。
ScriptKillGame.ets实现了9阶段流程,包括选择剧本(内置+文件导入)、角色分配展示秘密、幕介绍、NPC台词模拟朗读、自由讨论、线索公开、幕间过渡、最终投票、真相揭晓。文件导入使用@kit.CoreFileKit的DocumentViewPicker + fileIo.readTextSync。
UndercoverGame.ets实现了5阶段流程:词语分配→描述(30s/人)→投票(30s)→淘汰揭示→最终揭示。8组词对覆盖火锅/麻辣烫等热门词组。
DrawGuessGame.ets实现了Canvas绘图+4阶段流程:回合开始→绘图(60s)→回合结果→游戏结束。DrawPoint模型记录触摸轨迹,CanvasRenderingContext2D渲染绘图。
TruthOrDareGame.ets实现了众包出题机制:选类型→其他玩家8s内提交题目→随机抽取→20s回答。6+6道保底题库。
QuickReactGame.ets实现了5种水果牌+5张桌牌+抢拍机制。60张洗牌牌组动态生成,误拍惩罚。
v0.5 — 即时聊天系统
创建ChatModel.ets,定义ChatConversation(id/type/name/avatar/targetId/lastMessage/unreadCount)、ChatMsg(工厂方法text/voice/image)、ChatType枚举(PRIVATE/GAME/ACTIVITY)、ChatMsgType枚举(TEXT/VOICE/IMAGE)。MockChatData提供4个会话和6条消息(含文字、语音、图片三种类型)。
ChatPage.ets实现完整聊天界面:顶部导航栏显示对方头像和昵称、消息气泡区分自己(绿色#DCF8C6)和对方(白色)、输入栏支持文字+语音录音+图片选择+更多面板。语音录音使用setInterval计时,最长60秒自动停止。图片选择使用@kit.CoreFileKit的PhotoViewPicker。消息发送后通过数组展开运算追加到messages列表。
消息Tab在Index.ets中分为通知区和聊天区:通知区显示NotifyItem(报名/系统/游戏邀请三种类型),未读通知用黄色背景#FFF8E1高亮+红点标记;聊天区显示会话列表,未读消息数用红色圆形徽标显示。
v0.6 — 拉黑过滤系统
创建BlockModel.ets,采用模块级let变量blockedList + 导出函数模式(而非单例class),这是ArkTS限制下的最佳实践。提供5个导出函数:initBlockList初始化、isUserBlocked判断、blockUser拉黑(去重检查+数组展开添加)、unblockUser取消(filter过滤)、getBlockedUsers返回副本。
在Index.ets中,aboutToAppear调用initBlockList初始化拉黑列表,refreshFilteredData在三个维度过滤:nearbyUsers过滤掉被拉黑者、notifications过滤掉被拉黑者的通知、chatConversations过滤掉被拉黑者的会话。doBlockUser和doUnblockUser操作后调用syncBlockState重新同步。
首页附近用户卡片的"🚫"按钮触发doBlockUser,ProfileContent的"黑名单管理"展开BlockListPanel显示完整拉黑列表,支持"取消拉黑"操作。ChatPage.ets中右上角菜单触发BlockConfirmDialog确认弹窗,拉黑后输入栏替换为BlockedBar提示。
v0.7 — 附近匹配与GameRoom
创建GameRoom.ets,实现渐进式匹配算法:startMatching将matchRadius设为1km,doMatchStep每1.5秒执行一次,filter出在matchRadius内的在线玩家,追加到matchedUsers。找到4人后停止,否则radius+1继续,最大10km。匹配进度通过Progress组件可视化(value=matchRadius, total=10)。
匹配完成后点击"进入游戏",enterGame通过gameRoutes Record路由表分发:键为gameId(‘1’-‘6’),值为对应游戏页面路径。这是ArkTS中替代switch-case的惯用模式,Record<string,string>类型安全且无需动态属性访问。
GameRoom还实现了位置共享请求机制(LocationShareRequest + ShareStatus枚举),匹配成功的玩家可"申请定位",对方在弹窗中选择同意/拒绝,实现双向位置共享。
v0.8 — 权限引导系统
创建PermissionModel.ets,定义3个权限项:ohos.permission.MANAGE_MEDIA_RESOURCES(读取正在播放音乐,可选)、ohos.permission.BUNDLE_ACTIVE_INFO(读取应用使用记录,可选)、ohos.permission.APPROXIMATELY_LOCATION(获取位置,必需)。
PermissionPage.ets实现步进式权限引导: currentIndex追踪当前权限项,grantAndNext调用abilityAccessCtrl.createAtManager().requestPermissionsFromUser(context, [permName])异步请求权限,返回PermissionRequestResult.authResults[0]===0表示授权成功。必需权限不允许跳过(不显示"暂不允许"按钮),可选权限允许跳过。全部完成后显示CompleteUI汇总所有权限状态,点击"进入NearPlay"用replaceUrl跳转到Index首页。
值得注意的是,MANAGE_MEDIA_RESOURCES和BUNDLE_ACTIVE_INFO是系统级API权限,普通应用通常无法获得授权,因此PermissionModel的权限请求使用try-catch包裹,失败后静默降级到Mock数据。
v0.9 — MatchEngine与RunAdvisor联动
创建MatchEngine.ets,实现computeMatch匹配分数计算。音乐匹配:computeMusicMatch比较双方NowPlayingSong的genre,同类型返回true+20分。使用习惯匹配:computeUsageMatch比较双方UsageProfile的topCategories,双top1命中+40分、单侧top1命中+25分、双方top2命中+20分、其他交叉+10分,上限80分。总分=使用习惯分数+音乐匹配分数,上限100分。
创建MusicModel.ets,定义MusicGenre枚举(10种类型+UNKNOWN)、NowPlayingSong模型、classifyGenre关键词分类器(10组关键词,每组5-6个中英文关键词)、getGenreIcon图标映射。MockMusicData提供1首当前播放(晴天-周杰伦-流行)和8首附近播放。
创建UsageModel.ets,定义AppCategory枚举(10种类别)、AppUsageRecord模型、classifyApp分类器(19个已知bundleName映射)、UsageProfile.fromRecords聚合器(按类别汇总使用时长→排序→取top3)。MockUsageData提供5条自身记录和8组附近记录。
创建RunAdvisorModel.ets,定义RunPlan(3种类型:轻松跑3km/标准跑5km/耐力跑10km)、FoodRecipe(带getCalories()方法=caloriesPer100g×defaultGrams/100取整)、UserRunProfile、LikeStore(模块级likeMap + toggleLike/isLiked/getLikeCount/initLikes)。MockRunAdvisorData预置8个附近用户运动档案。
UserRunProfilePage.ets展示运动计划和食谱,支持点赞(toggleLike触发likeRefresh++ @State计数器刷新UI),从Index.ets首页附近用户卡片的"🏃"按钮跳转,携带UserRunProfileParams参数。
v0.10 — 语音输入与VoiceInputHelper重构
初始版本中,语音输入逻辑直接散落在各游戏页面的aboutToAppear中,导致代码重复和难以维护。本次重构将语音识别逻辑提取为独立的VoiceInputHelper(plain class)和VoiceInput(@Component UI包装器)两层架构。
VoiceInputHelper.ets封装@kit.CoreSpeechKit的speechRecognizer.createEngine回调式API:startListening接收onResult回调,内部创建engine(zh-CN/online/short模式),设置RecognitionListener(onStart/onEvent/onResult/onComplete/onError),onResult中更新recognizedText并在isLast时自动stopListening,stopListening中调用engine.finish+engine.shutdown,recognizedText非空时触发onResultCallback。同时维护durationTimer(1秒间隔计时,60秒上限自动停止),destroy方法清理timer和engine。
VoiceInput.ets作为@Component UI包装器,持有VoiceInputHelper实例,通过200ms间隔的refreshTimer轮询helper状态更新@State变量(isListening/recognizedText/listenDuration)。build()渲染两种状态:录音中(显示"语音输入中"+时长+停止按钮)和待机(显示麦克风按钮+识别结果文本+禁言提示)。onVoiceResult回调将识别文本传回宿主游戏。
重构后所有6款游戏统一使用VoiceInput组件(讨论/发言阶段),VoiceInputHelper实例由各游戏页面持有,aboutToDisappear中调用helper.destroy()清理资源。这消除了6处重复的引擎创建/销毁代码,也确保了录音资源不会泄漏。
附录:关键数据流图
用户启动App
│
▼
EntryAbility.onCreate ─── setColorMode ─── hilog.info
│
▼
EntryAbility.onWindowStageCreate ─── windowStage.loadContent('pages/Index')
│
▼
Index.aboutToAppear
├── initBlockList(MockBlockData.getInitialBlocked())
├── games = MockGameData.getGames()
├── activities = MockActivityData.getActivities()
└── refreshFilteredData()
├── nearbyUsers = enrichUsersWithMatch(rawUsers)
│ ├── MockMusicData.getMyNowPlaying()
│ ├── UsageProfile.fromRecords(MockUsageData.getMyUsage())
│ ├── for each user: computeMatch(mySong, otherSong, myUsage, otherUsage)
│ └── user.matchScore / matchLabel / musicGenreLabel
├── notifications = filter(!blocked)
└── chatConversations = filter(!blocked)
用户点击游戏卡片
│
▼
router.pushUrl({url: 'pages/GameRoom', params: GameRoomParams})
│
▼
GameRoom.aboutToAppear
├── gameId / gameName from params
└── nearbyOnlineUsers = filter(online)
用户点击"开始匹配"
│
▼
startMatching() → doMatchStep() loop (1.5s interval)
├── radius 1→2→3→...→10 km
├── matchedUsers accumulates
└── ≥4 players → "匹配完成"
用户点击"进入游戏"
│
▼
enterGame() → gameRoutes[gameId] → router.pushUrl(targetPage)
│
▼
WerewolfGame / ScriptKillGame / ... aboutToAppear
├── MockXxxData初始化
└── 开始游戏流程(自动阶段推进)
语音发言流程
│
▼
VoiceInput.onClick → helper.startListening(onResult)
│
▼
speechRecognizer.createEngine(params, callback)
├── 成功 → engine.setListener → engine.startListening
│ ├── onResult → recognizedText = result.result
│ ├── isLast → stopListening()
│ └── onError → stopListening()
└── 失败 → isListening = false
stopListening()
├── clearInterval(durationTimer)
├── engine.finish(sessionId) + engine.shutdown()
├── onResultCallback(recognizedText)
└── isListening = false
VoiceInput.refreshTimer (200ms)
├── isListening = helper.isListening
├── recognizedText = helper.recognizedText
└── listenDuration = helper.listenDuration
九、6种游戏的社交功能定位
9.1 狼人杀——信息博弈型社交
狼人杀是NearPlay中最具深度社交博弈的游戏。8名玩家分为好人和狼人阵营,通过白天的讨论和投票、夜晚的行动和特殊技能进行信息博弈。社交价值体现在:
推理暴露性格:玩家的发言逻辑、投票选择、犹豫程度都会暴露其思维方式和性格特征。一个总是急于指认他人的玩家可能是冲动型人格,而一个谨慎观察再发言的玩家可能更理性。这种"通过游戏行为观察性格"的机制比直接聊天更真实。
语音社交体验:狼人杀的讨论阶段使用VoiceInputHelper语音发言,玩家的语气、停顿、语速都成为社交信号。相比纯文字聊天,语音沟通传递的信息量更大、情感更真实。
信任博弈:狼人需要伪装成好人,好人需要识别狼人。这种"谁在说谎"的信任博弈是极好的破冰场景——在游戏中被欺骗不会造成真实伤害,但可以快速建立"这个人是否值得信任"的初步判断。
9.2 剧本杀——角色扮演型社交
剧本杀让玩家扮演剧本中的角色,通过朗读台词、搜索线索、讨论推理来还原故事真相。社交价值体现在:
角色代入降低社交压力:玩家不是以"自己"的身份发言,而是以"剧本角色"的身份发言。这种角色代入极大地降低了社交焦虑——说错话是角色的错,不是自己的错。
NPC-TTS技术增强沉浸感:系统NPC台词通过textToSpeech引擎朗读,营造"现场有非玩家角色"的氛围。即使只有5名真人玩家,配合TTS播报也能感受到完整的多人互动场景。
剧本文件导入:支持从外部.txt文件导入自定义剧本,玩家可以创作自己的故事。这种UGC模式让游戏内容无限扩展,也为"编剧→分享→一起玩"的社交链条提供了可能。
9.3 谁是卧底——语言表达型社交
谁是卧底考验的是语言表达能力——如何在不说出词语本身的情况下描述它的特征。社交价值体现在:
描述风格反映思维:有人从功能描述(“可以用来喝水”),有人从场景描述(“办公室桌上都有”),有人从情感描述(“让人感到温暖”)。不同的描述风格反映不同的思维方式,是了解他人的窗口。
投票讨论中的推理:谁是卧底?谁的描述最不像正常词语?这些推理过程需要分析他人的表达逻辑,是另一种形式的社交观察。
10秒描述时间压力:短时间内的表达最能暴露一个人的思维特征——从容不迫说明准备充分、紧张犹豫说明缺乏安全感、滔滔不绝说明表达欲强。
9.4 你画我猜——非语言沟通型社交
你画我猜是最具创意互动性的游戏,画者只能绘图不能写字,猜者通过画面猜测答案。社交价值体现在:
非语言沟通:绘画是一种完全不同于文字的表达方式。画得歪歪扭扭反而更有趣,误解和搞笑猜测是天然的社交话题。这种"你说东我猜西"的沟通失败比成功更有社交价值。
创意展示:擅长绘画的玩家可以展示才华,不擅长绘画的玩家的"灵魂画作"同样引人注目。无论画技如何,都能获得社交关注。
语音猜词:VoiceInputHelper语音识别让猜词更自然,说话代替打字更接近真实桌游的喊答案体验。
9.5 真心话大冒险——隐私披露型社交
真心话大冒险直接触及个人隐私和勇气边界,是破冰效率最高的游戏。社交价值体现在:
众包出题的针对性:其他玩家了解被问者,可以出针对性的问题。"你最近一次说谎是什么时候"这种问题,陌生人出和好朋友出,答案的深度完全不同。
选择揭示性格:选真心话还是大冒险,本身就是性格的体现——选真心话的人可能更坦诚(或更有秘密),选大冒险的人可能更爱冒险(或更有不想说的秘密)。
语音回答的真实感:说话时的犹豫、停顿、笑声都是社交信息。文字回答可以精心措辞,语音回答更难掩饰真实情感。
9.6 看谁反应快——竞技娱乐型社交
看谁反应快是最轻量级的社交游戏——5张水果卡上出现5个相同水果就抢拍。社交价值体现在:
低门槛高刺激:规则极简,任何人都能立刻参与。但反应速度的竞争带来强烈的刺激感和即时反馈。
语音抢拍创新:喊"拍/抢/有"即可抢拍,结合VoiceInputHelper语音识别,让抢拍比点击按钮更自然(也更吵——这正是桌游的乐趣所在)。
误抢惩罚的社交趣味:判断失误时的-1惩罚不是惩罚,而是笑点。“怎么还没五个你就抢了哈哈哈”——这种善意的嘲笑是最好的社交粘合剂。
十、技术选型深度论证
10.1 为什么选ArkTS而非React Native/Flutter
React Native:JavaScript桥接层导致性能损耗,WebView渲染无法达到60fps流畅动画。更重要的是,React Native无法调用HarmonyOS系统级API(CoreSpeechKit、AVSession等),丧失了NearPlay最核心的差异化能力——原生语音识别和位置服务。
Flutter:虽然渲染性能优秀,但Flutter的Dart语言与HarmonyOS生态割裂。Flutter for HarmonyOS(ohos_flutter)仍处于实验阶段,不支持CoreSpeechKit等关键API。Flutter的自绘引擎也与ArkUI的声明式范式冲突,两套渲染体系并存会导致内存和性能问题。
ArkTS:HarmonyOS第一方开发语言,直接调用所有系统API,ArkUI声明式UI框架提供60fps原生渲染。虽然ArkTS有严格模式限制(禁止any/unknown/for…in等),但这些限制恰恰保证了代码质量和运行时安全性。选择ArkTS意味着选择平台原生能力而非跨平台兼容性——这是NearPlay"深度集成HarmonyOS"战略的正确决策。
10.2 为什么选WebSocket而非分布式软总线
HarmonyOS的分布式软总线(Distributed Soft Bus)可以在同一华为账号的设备间建立低延迟通信通道。但NearPlay的目标场景是"附近的陌生人"——他们使用不同的华为账号,无法建立分布式软总线连接。
WebSocket是互联网标准的全双工通信协议,不依赖特定账号体系,任何人都可以连接。代价是需要中心化服务器做中转(Host-adjudicator模式),但这也带来了裁判公正性的保证——服务器是游戏规则的权威执行者,客户端无法作弊。
10.3 为什么选Mock数据而非真实API
NearPlay的六种游戏、五个Tab页面、匹配引擎、聊天系统等都需要数据支撑。在最小可行产品阶段选择Mock数据而非真实后端接口,原因有三:第一是开发效率——Mock数据让前端开发完全独立于后端,无需等待接口设计、开发、联调,一个前端工程师可以在一天内完成整个游戏页面的开发和测试;第二是演示友好——Mock数据保证每次演示都有一致的体验,固定的附近用户列表、固定的游戏对局、固定的匹配结果,真实接口的数据可能因为在线人数少而导致空列表或匹配超时;第三是降级兼容——所有系统接口调用都使用异常捕获包裹,失败时自动降级到Mock数据,确保应用在任何设备上都能正常运行。
Mock数据的代价是数据不真实——匹配分数基于假设的偏好而非真实的使用习惯,聊天消息是本地生成的而非对方发送的。但这在原型验证阶段是可接受的——我们验证的是功能完整性和交互设计,不是数据准确性。当产品验证成功后,只需启动后端服务、修改连接地址即可切换到真实数据。
10.3 为什么选Mock数据而非真实API
NearPlay的6种游戏、5个Tab页面、匹配引擎、聊天系统等都需要数据支撑。在MVP阶段选择Mock数据而非真实后端API,原因有三:
开发效率:Mock数据让前端开发完全独立于后端,无需等待API设计、开发、联调。一个前端工程师可以在1天内完成整个游戏页面的开发和测试。
演示友好:Mock数据保证每次演示都有一致的体验——固定的附近用户列表、固定的游戏对局、固定的匹配结果。真实API的数据可能因为在线人数少而导致空列表或匹配超时。
降级兼容:所有系统API调用(AVSession、usageStatistics等)都使用try-catch包裹,失败时自动降级到Mock数据。这确保了应用在任何设备上都能正常运行,不受权限和API可用性限制。
十一、产品闭环:从线上到线下
11.1 完整用户旅程
打开App → 发现附近同好 → 匹配进入游戏 → 语音互动破冰
→ 游戏结束添加好友 → 聊天深化关系 → 报名线下活动 → 线下见面
→ RunAdvisor运动联动 → 持续互动 → 回到App发现更多同好
11.2 每个功能的闭环贡献
| 功能 | 闭环环节 | 贡献 |
|---|---|---|
| 附近匹配 | 发现→连接 | 解决"附近有谁"问题 |
| 6种游戏 | 连接→互动 | 解决"一起做什么"问题 |
| 语音发言 | 互动→信任 | 解决"如何真实交流"问题 |
| 聊天系统 | 互动→关系 | 解决"游戏后如何继续"问题 |
| 活动系统 | 关系→线下 | 解决"如何线下见面"问题 |
| RunAdvisor | 线下→持续 | 解决"见面后如何持续互动"问题 |
| 拉黑系统 | 全程安全 | 解决"不想见的人如何屏蔽"问题 |
-项目总览与技术选型&spm=1001.2101.3001.5002&articleId=163013554&d=1&t=3&u=1403439591f240d1820c2dff4d58c1f8)
1万+

被折叠的 条评论
为什么被折叠?



