我花了几个月的时间用最不时尚的方式构建了一个黑暗奇幻卡牌战斗游戏:纯JavaScript、HTML和CSS,包裹在Capacitor中以获取Android构建。没有Unity,没有Godot,甚至没有React。它现在已经发布了(Shadow Arena on Google Play)。

有两个原因写这篇文章。首先,写一下这个堆栈实际上是什么成本和哪里有回报。第二,我需要诚实的反馈:哪些东西有效,哪些东西让你烦恼,你会改变什么。谷歌的发现正在做一些奇怪的事情(搜索几乎找不到它,印象接近零),所以从其他开发者那里获得直接意见是目前唯一的信号。

这不是一个促销文章,以下的数字就显得很明显。

为什么没有引擎

我知道web堆栈很好,迭代速度快,而且卡牌游戏主要是UI。整个游戏都遵循一个模式:一个单一的状态对象和一个render()函数,重新构建当前屏幕的HTML并重新绑定处理程序。没有框架,没有虚拟DOM。对于15个屏幕来说,这足够了,而且调试非常简单,因为UI只有一个可能的变化方式。

比预期更好地工作的东西

  • 开始使用程序生成的SFX。整个开发过程中,每个声音效果都是用Web Audio在代码中合成的,没有音频文件在repo中,因此游戏在任何真正的音频出现之前就可以完全播放了。现在的发布包含小的OGGs(12个效果,战斗击中有5个变体),但是免费的占位音频证明了自己的价值。
  • 音乐也是自定义的:三个轨道(菜单、战斗、任务)都是专门为这个游戏写的,没有库循环。当一个带有自己的音频的卡片视频打开时,音乐会在1秒内淡出,并在关闭时淡入,这是一个小细节,使整个游戏感觉更不像一个网页。
  • 卡片存储在JSON文件中,一个构建脚本生成数据模块。添加一个卡片就是将一个包含艺术和JSON的文件夹添加到repo中。
  • 读取源代码作为文本的单元测试。随着我发布了一个包含AdMob测试广告的发布(一个硬编码的isTesting标志,所有调试构建都隐藏了它的设计),我添加了一个测试,检查配置文件是否包含发布不安全的值。它已经捕捉到了两次错误。

什么让我受伤

  • Android WebView不是Chrome。CSS滤镜发光效果在透明PNG上静默地渲染为Nothing,于是在设备上我将发光效果 bake 到PNG中。长按任何卡片图像会冻结所有触摸输入,包括三个游戏模式:native图像拖动会激活。修复方法是一个CSS行(-webkit-user-drag:none)后两天的混乱。
  • 手势导航边距。 在沉浸式全屏模式下,env(safe-area-inset-bottom)返回0,所以我的横幅会覆盖在某些三星上,而在其他地方看起来很好。最终,我读取了边距的原生值,使用getInsetsIgnoringVisibility并将其注入WebView作为一个CSS变量,带有重试循环,因为在冷启动时第一个读取发生在窗口知道其边距之前。
  • Gradle打包过时的APK。Capacitor的同步保留了文件时间戳,gradle决定没有变化,你安装一个没有你的修复的构建并想知道为什么这个错误是无限的。清洁构建解决了这个问题。失去的晚上不会回来。
  • 我在几个发布中将93 MB的原始编排资产打包在包中之前才注意到。现在的构建脚本包含一个排除列表,并且如果这个文件夹再次出现在输出中,测试将失败整个套件。
  • 游戏玩法:https://youtube.com/shorts/U8oqdnzi_Ns

Play Store的一半

新个人账户需要一个封闭测试,12个活跃的测试者在14天内没有关闭测试才能发布。由于我没有12个有Android手机的朋友,所以我从beta测试的子ред迪特和小型游戏开发者Discord中招募了陌生人。预算额外的人员,因为安装一次但从未打开应用的测试者不算作活跃。然后:30个字符的限制,6种语言的列表文本(游戏本身被本地化为6种语言,硬编码的字符预算,因为德语会打破每个按钮),以及内容问卷会随着添加广告或IAP而改变。

目前的状态

发布并稳定。下载接近零,因为发布的确实是易如反掌的一半。让任何人看到游戏而不花费广告预算是真正的boss战斗,我仍在尝试解决这个问题。一个东方:游戏会检查它正在运行的手机,三星的用户会在游戏中获得一个小惊喜,链接到Galaxy Store。是否有人会发现它,尚不清楚。如果你正在考虑这个堆栈:纯JS加上Capacitor对于卡牌、棋盘和谜题游戏完全可行。你得到即时迭代和一个代码库,但是你要支付WebView的奇怪行为。你的游戏需要粒子、3D或数百个移动的图像时,就用引擎。