从小游戏到APP:探索单挑篮球进阶之路的完整历程

2026-02-07 0 511

游戏开发界,PVP对战和相关系统的构建往往面临诸多挑战,同时也孕育着创新。这其中蕴含着许多值得深入探讨的有趣话题。

同步方案的确立

玩传统IO类小游戏的PVP模式会遇到不少难题,比如人数过多导致游戏难以进行,这使得PVP模式不太划算。但如果我们用ECS结构来开发单挑篮球游戏,就能天然实现帧同步,因此我们选择了这个方案。在开发过程中,这算是一个比较明智的决定。某论坛上的一位大佬也专门写文章介绍了这一点。不过,这个方案也有不足之处,通用性强的框架通常需要牺牲性能,比如协议未压缩等问题,导致帧消息体积较大,这对需要高频操作的篮球游戏来说并不友好。

在游戏开发过程中,各种游戏玩法和操作频率对画面同步效果有很大影响。以篮球游戏为例,在频繁操作中,较大的画面信息会导致诸多问题。此外,在应用画面同步技术时,往往难以有效管理服务器的负载。一旦出现问题,仅靠提交工单处理,耗时较长,效率不高。

自建联机对战平台

开发APP时,我采用了Go语言,参照MGOBE的模式,独立开发了一套在线对战平台。该平台的API命名与MGOBE保持一致,因此客户端无需做太大调整即可实现对接。平台具备tcp和udp两种通信协议的支持,这是它的一个显著优势。不过,udp协议存在指令冗余,网络状况不佳时,消息包容易堆积,这对低端手机来说不太友好。因此,为了低端手机的连接体验,我们选择了tcp协议来连接对战服务器。这样的设计可以确保不同设备在游戏中的连接稳定性。

选择网络连接方式时,要全面考虑设备状况。对于配置较低的智能手机,其网络连接的稳定性可能不足。采用TCP协议,可以在与对战服务器连接时,有效减少许多潜在问题。

帧同步问题排查

客户端负责帧同步的计算,这就意味着两个客户端的计算结果必须完全相同,否则画面会出现不同步。在初期上线的单挑篮球版本中,出现了不少画面不同步的问题。因为服务器无法实时判断客户端是否同步,所以问题的排查只能通过分析客户端日志来完成,这无疑加大了排查的难度。

游戏画面不同步会影响玩家的游戏感受。同时,解决问题的方式不多,主要依靠客户端日志来寻找线索。这对开发者而言,是一项不小的挑战,需要投入大量时间去仔细分析日志内容。

游戏重启与追帧

游戏在重启追帧和途中追帧的步骤上大体一致,只是重启追帧需要重新进入战场,并且需要从第一帧开始追踪。对于像单挑篮球这样的游戏,单局只需1到2分钟,从头开始追帧并不会带来太大压力。采取合理的追帧策略,对确保游戏顺畅运行至关重要。

游戏持续时间不同,对追帧技巧的适用性各异。比如,篮球单挑一局时间不长,实时追帧尚可忍受,可若游戏持续时间较长,可能就需要更高效的追帧方法,以减少对玩家体验的负面影响。

AI行为树的设计

以小游戏中11分玩法为例,我针对10个不同难度,设计了10个行为树。这些行为树间的区别细微,主要在于AI对行为的响应速度和触发几率。此外,我还对AI每个行为的具体触发点进行了限制,并通过配置表来调节行为出现的几率和速度。这种精细的安排,有助于增强AI的智能程度。

游戏难度各异,对AI的策略也有所不同。精心构建AI的行为树,可以使游戏中的AI行为更贴合游戏难度,从而提升玩家的游戏感受。

Spine换装系统改进

changeCloth (skinName: string, slotName: string): any {
    let spine: sp.Skeleton = this.node.spine
    let skeletonData = spine.skeletonData.getRuntimeData()
    let skin = skeletonData.findSkin(skinName)
    const slot = spine.findSlot(slotName)
    const slotIndex = skeletonData.findSlotIndex(slotName)
    const attachment = skin.getAttachment(slotIndex, slotName)
    slot.setAttachment(attachment)
  }

将50个角色划分为50个脊骨,虽然这算是个方法,但每个脊骨都包含动画,这样一来就需要大量动画,每增加一个动画,就得在所有脊骨上同步更新,这对美术团队来说是个不小的压力。此外,这里的API与JavaScript不一致,有些方法和属性未导出时还会出现错误,因此我修改了脊骨在C++运行库中的代码。

游戏开发过程中,各系统模块之间相互牵连。Spine换装系统的升级需与动画等内部功能相协调,如何在不多增美术等人员负担的前提下优化系统,实为一大挑战。

在游戏制作过程中,大家是否也遭遇过类似的难题?期待大家能点个赞、转发这篇文章,并踊跃留言交流。

export default class SpineUtil {
  static async setSkin (spine: sp.Skeleton, heroId, skinId, collocation = {}) { // posType 1头饰 2上衣 3裤子 4鞋子 5手部 6腿部
    const skeletonData = spine.skeletonData.getRuntimeData()
    const baseClothesData = await AssetLoader.loadResAsync(`spine/clothes/c_${heroId}/c_${heroId}`, sp.SkeletonData)
    const baseClothesDataRuntimeData = baseClothesData.getRuntimeData()
    const baseClothesSkin = baseClothesDataRuntimeData.findSkin(\'c_\' + skinId)
    if (!baseClothesSkin) return
    let newSkinName = \'newSkin\' + heroId + skinId
    for (let pos in collocation) {
      newSkinName += \'_\' + collocation[pos]
    }
    const newSkin = new sp.spine.Skin(newSkinName)
    const { SkinColor } = app.db.actor.GetActorById(heroId)
    const findSkin = skeletonData.findSkin([\'white\', \'yellow\', \'black\'][SkinColor - 1])
    newSkin.copySkin(findSkin)
    // 使用默认外观
    for (const skinEntry of baseClothesSkin.getAttachments()) {
      const slot = !cc.sys.isNative ? skinEntry.slotIndex : baseClothesSkin.getEntrySlot(skinEntry)
      const name = !cc.sys.isNative ? skinEntry.name : baseClothesSkin.getEntryName(skinEntry)
      const attachment = !cc.sys.isNative ? skinEntry.attachment : skinEntry
      this.addAttachment(SKIN_PART, newSkin, slot, name, attachment)
      this.addAttachment(ARM_PART, newSkin, slot, name, attachment)
    }
    
    ... 省略部分代码
    
    if (skeletonData.skins[skeletonData.skins.length - 1].name === newSkin.name) {
      skeletonData.skins[skeletonData.skins.length - 1] = newSkin
    } else {
      !cc.sys.isNative ? skeletonData.skins.push(newSkin) : skeletonData.addSkin(newSkin)
    }
    spine.setSkin(newSkinName)
    }
}

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 开发教程 从小游戏到APP:探索单挑篮球进阶之路的完整历程 https://www.zuozi.net/70855.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务