本文作者为 SAGITEAM 团队成员 蟹老板 。首发于 Cocos论坛


文章的开始,请允许我挖个坟 —— 微信小游戏从立项到上线!谈谈《猎头专家》的开发历程,点击下面的链接阅读:

这个系列由于时间、精力和生存的问题,最终还是太监了,现在我们新开一个时间线继续。

去年这个时候,我们还只有一款小游戏,今年我们已经可以召唤两次神龙了…… 以下是为大家精选的小游戏

(请用微信扫码开始游戏)

所有二维码

这次跟大家分享的是小游戏《单挑篮球》的开发历程,我会从 立项,创意,技术、迭代 四个方面来介绍。

一、立项

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

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

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

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

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

请分别用四个APP来扫:

单挑篮球二维码

二、创意

场景

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

场景

角色

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

角色

角色

主界面

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

角色

新手引导

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

操作

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

新手操作

数值及AI

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

AI1

AI2

道具系统

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

道具系统

三、技术

物理表现

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

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

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

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

物理运动

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

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 框架,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行为树

本游戏使用了一个开源可视化的行为树编辑器 behavior3editor,要聊起行为树,篇幅就不够了,所以,就此打住……,这里仅奉上行为树节点的名称供参考

行为树 1

行为树 2

UI设计

本游戏UI使用的是 http://www.fairygui.com/ 这主要是为了方便美术同学,因为美术同学觉得fgui比creator好用……你要问我fgui好不好,我只能说有好有坏吧,可以省掉开发同学一些拼界面的工作量,但坑也是有一些的。

性能调优

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

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

四、迭代

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

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

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

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

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

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

五、总结

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

全文完