竞技对抗小游戏《单挑篮球》开发历程 | Cocos技术派第12期


​本文来自于“Cocos 荣耀讲师”征稿活动第1期,最先发表于 Cocos 中文社区,作者 ID:蟹老板,2017年加入社区,文章作品包括《猎头专家的开发历程》等。

Cocos 技术派第12期,由蟹老板从立项,创意,技术、迭代四个方面来分享小游戏《单挑篮球》的开发历程。

“立项”

在小游戏平台上的篮球游戏,99%都是纯投篮类小游戏,没有一款真正意义上的竞技对抗型篮球游戏。我们参考了 Basketball Battle,这是一款轻度、有趣的1v1篮球对抗游戏,但存在游戏表现力和玩法深度不足的问题,比如:角色及动作太Q,AI 存在明显的 Bug,数值压制无力吐槽……

所以我们仅借鉴其 2D 横版的游戏表现方式,在角色形象、动作、操作方式上做了大量创新,代入原创的数值及 AI 系统,打造出了这样一个易上手,但又有足够深度的竞技小游戏。

你没看错!这是一款“横版”游戏。在微信小游戏平台上,做横版游戏代表提前“放弃”了一部分玩家。SAGITEAM 的第一款游戏《开心射手》也是横版。那时选择横版是因为不了解小游戏平台的玩法,而这次立项选择横版,是因为《单挑篮球》必须做成横版才能满足竞技游戏的操作需求。

竞技+篮球这个独特的小品类,与小游戏平台上爆款特征不沾边。SAGITEAM 希望不盲目追随市场爆点,专心打造一个优质的产品,在小游戏的下半场持续磨练团队和产品的竞争力。

《单挑篮球》目前已在四个平台上线,欢迎品鉴!

“创意”

场景

调整好篮框和角色的大小比例,球场大小可以控制在一屏显示完整,不需要移动镜头,这对于 1v1 的对抗表现完全够用。

角色

不同于大部分街篮游戏使用的 Q 版三头四头身风格,我们参考了《羽毛球高高手》的美术设定,使用五到六头身,外形更接近真实人身比例,这样角色的动作细节有更大的表现空间,每个角色有多至 26 个不同的全身动作,力求做到真实。

主界面

进入游戏主界面后,你会看到默认角色“超级新人“在训练,这就是在告诉你,这不是一款投篮机游戏,你看到的就是游戏中的真实效果。

新手引导

亲切的”西安教练”会告诉你游戏的基本操作,当然,只是基本操作,更高级的,就得你自己去摸索了……

操作

为了增强玩家的操作性,我们在游戏中加入了 ActionButton,实现防守时可抢断,进攻时可突破的功能,如果深度体验,你还会发现假动作、后仰跳投、盖火锅…… 下图标明了四个键的分布,同时,我们放大了按钮的触摸区,让玩家可以更好的盲操作。

数值及 AI

体育类游戏的数值实际上是有上限的,简单地说,就是角色的表现不能太离谱,比如跑动速度,弹跳力,投篮命中率,不然就会感觉很“假”,要避免这种情况,就不能纯粹使用数值来做压制,而是更多使用分级 AI 来帮助玩家成长,下面两张图就是初级 AI 与最高 AI 的行为树配置,可看出复杂程度差别还是比较大的。

道具系统

如果你想赢得轻松点,可以在赛前来点兴奋剂,但作为一个公平竞技游戏,还希望玩家提升硬实力才是。

“技术”

物理表现

我们抛弃了 Box2D,所有碰撞计算都是自行用代码实现,主要是基于两个原因:

  • Box2D 性能在小游戏平台上实在堪忧
  • 篮球游戏所用到的物理公式非常简单

不过我们依然使用了 cc.Intersection 来做碰撞检测。

游戏中的物理运动,无外乎移动,加速,重力、反弹、抛物线五种,网上教程很多,这里不详述,比较值得一说的是本游戏内的抛物线实现,主要运用在两个地方:1是球出手到触框(板)的飞行轨迹,2是扣篮时角色的起跳轨迹。

这两处抛物线最初使用的是比较常见的匀速线性公式,无法表现出球在飞行的初/中/末段的速度变化,也无法表现出角色在扣篮时的起跳爆发力及滞空效果,后来我改为使用三阶贝塞尔曲线,通过改变两个控制点的位置,较好地模拟了非线性抛物线效果,上图中的白点即为贝塞尔曲线在每帧绘制的坐标点,并附上三阶贝塞尔曲线的生成代码。

export default class Bezier {​  /**   * 获得贝塞尔曲线的点的具体坐标   * @param cps [[起点],[控制点1],[控制点2],[结束点]]   * @param t [0-1]   * @return [number, number]   */  public static getPoint (cps: number[][], t: number) {    let ax, bx, cx    let ay, by, cy    let tSquared, tCubed    let result = [0, 0]​    cx = 3.0 * (cps[1][0] - cps[0][0])    bx = 3.0 * (cps[2][0] - cps[1][0]) - cx    ax = cps[3][0] - cps[0][0] - cx - bx​    cy = 3.0 * (cps[1][1] - cps[0][1])    by = 3.0 * (cps[2][1] - cps[1][1]) - cy    ay = cps[3][1] - cps[0][1] - cy - by​    tSquared = t * t    tCubed = tSquared * t​    result[0] = (ax * tCubed) + (bx * tSquared) + (cx * t) + cps[0][0]    result[1] = (ay * tCubed) + (by * tSquared) + (cy * t) + cps[0][1]​    return result  }}

ECS 框架

本游戏使用了开源 ECS 框架:

https://github.com/darkoverlordofdata/entitas-ts

ECS 框架的好处不必多介绍,有兴趣的同学自行找资料学习吧,这里简单贴一下我设计的系统与组件。

export default class LogicSystem extends Systems {  constructor (context: GameContext, service: Service) {    super()    let pool: Pool = Pool.instance    this.add(pool.createSystem(new GameInitSystem(context, service)))    this.add(pool.createSystem(new EnergySystem(context, service)))    this.add(pool.createSystem(new BehaviorTickSystem(context, service)))    this.add(pool.createSystem(new PlayerInputSystem(context, service)))    this.add(pool.createSystem(new ActorMoveSystem(context, service)))    this.add(pool.createSystem(new BallMoveSystem(context, service)))    this.add(pool.createSystem(new CollisionFactorySystem(context, service)))    this.add(pool.createSystem(new CollisionCheckerSystem(context, service)))    this.add(pool.createSystem(new CollisionHandlerSystem(context, service)))    this.add(pool.createSystem(new StealSystem(context, service)))    this.add(pool.createSystem(new CatchSystem(context, service)))    this.add(pool.createSystem(new DunkSystem(context, service)))    this.add(pool.createSystem(new CloseSystem(context, service)))    this.add(pool.createSystem(new SkillCheckSystem(context, service)))    this.add(pool.createSystem(new TimerSystem(context, service)))  }}export enum ComponentIds {  Data,         // 数据组件  Render,       // 渲染组件  Player,       // 玩家  Robot,        // 机器人  Shadow,       // 投影  Actor,        // 角色  Ball,         // 球  Action,       // 动作组件  Parabola,     // 实现抛物线  ActorMove,    // 实现人移动(速度, 重力)  BallMove,     // 实现球移动(速度, 摩擦力, 空气阻力, 重力)  Collider,     // 碰撞组  Contact,      // 碰撞关系  Collision,    // 碰撞事件  Body,         // 坐标及体积  State,        // 角色状态  Timer,        // 定时器  Behavior,     // 行为器  totalComponents}

AI 行为树

本游戏使用 一个可视化的行为树编辑器:

https://github.com/behavior3/behavior3editor

要聊起行为树,篇幅就不够了,所以,就此打住……,这里仅奉上行为树节点的名称供参考。

UI 设计

本游戏 UI 使用的是:

http://www.fairygui.com/

这主要是为了方便美术同学,因为美术同学觉得 fgui 比 Creator 好用……你要问我 fgui 好不好,我只能说有好有坏吧,可以省掉开发同学一些拼界面的工作量,但坑也是有一些的。

性能调优

由于小游戏平台的限制,对游戏包体的大小和运行性能要求都比较严格,我从以下几个方面做了优化:

  • 砍掉没有使用的模块,可以将 cocos2d-js-min.js 由 1.6mb降低至 900kb。
  • 将 main.scene 所需的 logo 及白底图放在首包内,使其启动即渲染,避免黑屏。
  • 拆分 fgui 生成的包资源,做一个小的 loading 包,在 logo 及健康忠告的掩护下优先载入 loading 包。
  • 对合图进行 tinypng 压缩,将 1.8mb 的合图压至 500kb。
  • 非必须资源在使用时异步加载(如音频、纹理、spine)。

“迭代”

篮球从立项到上线(微信)小范围测试,历经了三个多月,之后保持着一周一到二个版本的迭代,迭代的方向是针对游戏数据进行调优,主要就是留存/活跃/时长/付费四个指标,但这四个指标并不孤立,而是一个整体,比如我们试着提出以下问题:

  • 游戏界面信息是否足够清晰?
  • 新手引导的完成度如何?
  • 玩家玩到第几局后会流失?
  • 游戏是否太难(或简单)?
  • 玩家的成就感和挫败感在哪里?
  • 卡点是否合理?
  • 如何提升玩家的付费意愿?
  • ……

这些问题都需要通过详细的打点来分析,好游戏一定是调出来的,每一次调整都要有清晰的目标,充分验证假设。

每个版本做出一些调整,观察数据的变化,会发现,四个指标是同步上升的,我以上面的问题 2、4 作个简单的回答:

新手引导的完成度如何?

回答: 虽然我们认为新手引导已足够清晰,但最初的通过率只有 70%,通过对新手引导共 16 步提示进行打点分析,发现有三步引导(滞空跳投,前进上篮、前进扣篮)的通过率非常低,判断是由于是组合键操作,基础较低的玩家初次接触无法很好掌握而放弃。于是我们对这三个引导进行了简化调整,通过率上升至 90%,反映到新增次留上,可以提升 3%以上。

游戏是否太难(或简单)?

回答4: 最初的 AI 等级只有五级,与积分挂钩,后来发现五级不够,难度曲线太陡,玩家每升一个 level,胜率就会降低 10 多个 %,后来改为 10 个 level,并根据玩家最近 10 场的胜负状况动态调整AI配置,让玩家成长曲线更平滑,反映到游戏时长上,由最初的 500 秒升至最高的 800 秒。

“总结”

小游戏行业门槛低,每天都会有无数新游上线,但行业俨然已是红海,99%的游戏进来可能连个泡都没冒就沉底,这是每个 CP 都不愿看到的结果,希望我们分享的《单挑篮球》的创作过程能给大家带来一些启示。

———— / END / ————

再次向作者的分享致以谢意!欢迎大家点击文末【阅读原文】进入原贴与作者及广大开发者一同交流,为作者点赞!

如果您在使用 Cocos Creator 的过程中,获得了独到的开发心得、见解或是方法,并且乐于分享出来,帮助更多开发者解决技术问题,加速游戏开发效率,期待您与我们联系!

实现战旗类游戏《火焰纹章》移动范围效果

用摄像机实现残影幻影拖尾效果

Cocos Creator 通用框架设计:网络

自定义渲染组件及材质介绍

小姐姐的发丝高光怎么用 Creator 3D 实现?

小时候玩掌机,长大了开发掌机游戏

小游戏《快上车3D技术分享

3D 案例《弹弹乐》技术实现分享