打音游用到的上隐工具不太好用,决定自己写一个,最开始是 Android 原生,后来用 flutter 重写了一遍

在下落式音游中,有一部分音游 note 的下落速度在开头位置是逐渐加速的,上隐就是将开头的加速部分遮挡住,让屏幕上能看到的部分为匀速下落,这样打起来会简单很多

最开始因为 mygo 去玩了邦邦手游,发现音游打起来有点意思,后来经推荐去玩了 PJSK,因为手感要比邦好很多,再加上基本都是我喜欢的歌,沉迷了一段时间

再回到邦邦时,却感觉怎么打都不对劲,猜测可能是因为 PJSK 的 note 加速没那么严重,于是试了试贴吧看到的上隐,打起来果然舒服了很多

但当时能用的上隐工具就只有一个看起来很老的 DeemoCover ,用起来也不是很方便

正好我曾经有学过一点 Android 开发,所以就打算自己写个新的

Android 原生

上隐工具要实现的内容并不多,最基础的只要有一个可以覆盖其他 app 的悬浮窗,一个编辑悬浮窗样式的表单,以及悬浮窗样式的保存和选择即可

自己实现系统悬浮窗功能太过麻烦,所以在 GitHub 找了找相关的库,最终选定了 FloatingX

最后稍微复习一下 Android 开发相关的东西,就可以直接开写了

最初的版本没有什么复杂的逻辑,一个表单填写数据,点击按钮生成悬浮窗,再给悬浮窗加几个点击事件完事,甚至连方案保存功能都没有

开发过程基本靠 AI 辅助,中间还尝试了一下 Jetpack Compose,确实要比之前写 xml 方便多了

总共花了大概两个周末的时间来写,写完了发到贴吧,又花了一个下午用 SQLite 把方案保存和选择加上了

到这个时候,我自身的需求就已经基本满足了

不过后来有人回贴说想要 ios 版本,就打算去研究一下

ios 开发尝试

首先我没学过 ios 开发,所以打算找一个跨平台框架实现,在v站搜索了一圈之后,最终在 RN 和 flutter 中选择了后者

之后了解了一下才发现,ios 压根不支持在其他应用上显示悬浮窗,但有几个 app 用画中画的方式来实现悬浮窗,从 GitHub 找到了 fl_pip 能自定义悬浮窗内容,于是准备写个 demo 试试

在开始之前,还得先把环境搞好,要开发 ios app,必须要在 mac 上写,我没有苹果设备,只能去研究虚拟机,中间的过程记录在了 使用 VMware 创建 macOS 虚拟机

搞好虚拟机、账号、模拟器之后,终于把 demo 跑起来了,结果发现画中画不能居中,宽度也有限制,实现不了上隐

再加上虚拟机开发体验太差、ios app 分发困难、并且在B站找到了通过快捷方式实现 ios 上隐的方案等诸多原因,最终选择放弃,或许等以后 ios 支持悬浮窗了还能再来搞搞

flutter

虽然 ios 开发之路还没开始就结束了,但中间写 demo 的时候体验了一下 flutter,感觉比 Android 原生写起来舒服多了,所以打算将之前写好的程序用 flutter 重写一遍,顺便加点新功能

悬浮窗用的 flutter_overlay_window,写了个 demo 跑起来没问题,于是开始看文档学习 flutter

首先把 dart 的文档简单过了一遍,确实能看到很多其他语言的影子,但语法上又稍有不同,看着有点别扭,不过整体还是比较简单的

之后看 flutter 的文档,这个文档内容就有点多了,我也只能把最基础的几个章节看一遍,剩下的跳着看,等开发的时候遇到问题了再回过头来细看

就这样一边学一边写,折腾了好几周才算完成,中间来来回回重构过好几次

最开始的版本所有 widget 都是写在一起的,缩进能有快半个屏幕,整一个嵌套地狱

后来拆分 widget,又加上了 provider,看起来才舒服一些

不过整个开发流程体验还是很不错的,几个需要的功能和组件也基本都能在 pub.dev 找到

后来因为拖的时间实在太久,就没有再接着改,把最后的代码传到 GitHub(就是 BanGDemo),打好包发贴吧结束

待完善

目前还剩几项内容没搞,等之后什么时候有兴趣了再说

首先是一个奇怪的问题,我给悬浮窗 widget 用GestureDetector分别加了onTaponDoubleTaponLongPress,但当使用onLongPress来执行FlutterOverlayWindow.closeOverlay()时,第一次长按关闭悬浮窗正常,但重启悬浮窗之后,单击和长按事件都失效了,双击倒是还正常,最后只能先把长按去掉,留了单击、双击、三击供用户选择

另外我发现 flutter_overlay_windows 的悬浮窗位移时不会有动画效果,而是直接瞬移,感觉有动画的话会更好一点,之后可以给它加上

此外还有一个没尝试过的功能,目前各页面的跳转我是通过Navigator.push()Navigator.pop()来实现的,在文档中有看到过路由,不知道会不会更好一些

最后是和 SQLite 的交互,目前我都是拿到 Database 之后直接执行 SQL 的,貌似 flutter 有一些 orm,之后可以用用看