当前位置: 首页 > news >正文

关于游戏性能优化的技巧

关于游戏性能优化的技巧

  • 游戏性能优化
  • 对象池
  • Jobs、Burst、多线程
  • 间隔处理
  • 定时更新
  • 全局广播
  • 缓存组件
  • 缓存常用数据
  • 2D残影优化
  • 2D骨骼转GPU动画
  • 定时器
  • 优化DrawCall合批处理
  • 优化碰撞层
  • 优化粒子特效

游戏性能优化

好久没有在CSDN上面写文章了,今天突然看到鬼谷工作室技术负责人分享的关于游戏性能优化的技巧想梳理一下游戏优化这块的理解,这篇文章不会从细节入手,针对的是大部分游戏性能瓶颈期和对优化没有完整概念的开发。优化的方法和手段很多如果我说的不详细的地方欢迎大家评论探讨,感谢。

对象池

对象池可以重复利用某一些对象,避免重复的创建实例化。比如特效使用对象池,当特效销毁时可以隐藏起来,并添加到对象池中没下次需要创建一样的特效时只需要从对象池中返回特效,并从对象池中移除。下次再使用这个对象池的时候就不需要重新实例化了。对象类别还包括游戏中常见的一些怪物模型、特效、音频、血条、飘字等等,还有一些辅助单刀的临时节点。我们使用技能的时候会创建很多弹道,这些弹道的移动轨迹是通过辅助节点来移动的。这些辅助节点创建的频率频繁,如果使用对象池,这些节点就不再需要重复创建。但是要注意,如果特效中使用了异步的功能,在加入到对象池之后记得要关闭,否则下次可能会有BUG。

在这里插入图片描述
在这里插入图片描述

Jobs、Burst、多线程

游戏中一些复杂的逻辑可能都是在主线程下面编写的如果游戏的复杂逻辑比较多,就会造成主线程的压力大,这个时候就可以使用多线程。但是,多线程使用不当可能会导致上下文切换比较频繁或线程锁的问题。使用Jobs就可以安全方便的编写多线程代码,最终可以提升上百倍的效率。我们弹道的移动轨迹和复杂公式,包括各种大地图上AI的评分都可以使用Jobs来编写,但是编写的东西要和主代码脱离,否则你可能会有很多模块化之间的交互。如果你的寻路之类使用Jobs写不太方便的话,可以直接采用多线程来进行优化。
在这里插入图片描述

间隔处理

比如1秒钟内创建了几十个怪物,即使有对象池,怪物的初始化也是需要时间的,这时就可以把怪物分摊到多帧间隔中创建,避免同一帧的卡顿。还有声音和特效,创建一个弹道可能会发出声音,一个技能使用的时候可能会创建几十个弹道,这个时候声音是不需要重复创建的,可以只创建一个声音。对于特效,比如打到怪物上面会有被击效果,弹道比较密集,此时你的特效可以考虑间隔处理,让它达到一定时间才会创建。
在这里插入图片描述

定时更新

血量UI、效果图标、技能CD等这些UI是不需要频繁更新的。每一次击打怪物的时候造成广播,可能造成1秒钟之内更新几百上千次UI。此时可以做间隔更新,比如设置一个最短时间或者定时更新,减少更新次数。对于技能上的逻辑,比如我们有一个技能,场上没存在一把剑影,移动速度加10。场上的弹道销毁和创建的频率比较高,不能鉴听创建和销毁的广播,此时就可以定时检测场上的弹道数量,降低检测频率。

在这里插入图片描述

全局广播

图中,最右边是大招,最中间是器灵、法宝、逆天改命和后天气运,加起来可能会有200个左右的被动技能。这些被动技能很多是监听,即打到人时做什么事情或者使用什么技能做出的事情,当然还有一些是一次性加属性的,比如近战之后某些技能攻击增加、弹道数量增加。我们监听性效果是比较多的,近战可能可以达到上百个监听性的效果。
在这里插入图片描述举个例子,最右边图中是刚刚那个技能,黄色框的是监听攻击到单位时就造成伤害的提升或释放技能,下面的是监听使用技能时场上所有的效果,达到一定层数就会额外提升发射弹道。比如黄色的这种,如果我们使用监听的方式,打到敌人时候判断攻击者是否是自己,攻击的技能是否是此技能。那么我们的所有伤害都会被监听到,是很浪费效率的,可以把广播拆细一点,比如指定角色使用了指定技能攻击到敌人,此时效果监听只需要把效果、身上挂载的单位和配置表上填写的技能ID组成一个唯一的串来监听这个技能打到的伤害,就可以把无效的广播去掉。下面的这个使用技能的广播也一样,如果用使用技能的单位和使用的技能来判断,会有很多无效判断。那么可以采用和上面一样的逻辑,使用指定角色和指定ID组成的一个唯一串来广播,只有有效的技能才会收到广播。如果我们技能频率比较高,比如1秒钟造成3000次伤害,监听很多无效广播来做判断会比较浪费效率。
在这里插入图片描述

缓存组件

频繁的获取组件也是不好的。比如子弹发射时,每次发射会获取到它的碰撞体来监听碰撞的敌人,然后造成伤害。当弹道过多的时,每次发射都进行获取是浪费性能的,这个时候可以把组件缓存起来。例如,对象池中组成的特效类似于右边的那个类,里面包含特效的对象,包括很多缓存的组件,动画、碰撞体等等。这个时候如果要获取组件,可以通过这个缓存的组件来获取,那么所有组件只有在第一次初始化的时候才会获取一次,不需要每次都获取。
在这里插入图片描述

缓存常用数据

我们在弹道表中可能会配置一些弹道速度和持续时间,如果在弹道类里面每次创建弹道的时候都来读取这个数据是浪费效率的,因此可以把这些数据储存到技能上面,通过技能传递数据到弹道类中,弹道类就不需要来堆配置表了。一般还有两种缓存,一种是不需要实时更新的数据,比如说角色战力、角色的各种向性评分,还有它的喜好评分,可以在一段时间内更新一次,也可以把这个数据缓存起来,通过系统或其他方式来更新。另外一种就是固定的数据,比如密集微粒。我们的密集微粒是通过它密集组装的词条来生成的。密集词条越高,它的威力就越高,这个值是固定的,只需要在首次创建的时候把它缓存,下次就不需要重新计算了。在这里插入图片描述

2D残影优化

Unity的2D残影方式一般都是实例化模型的当前动画这一帧,并把动画速度设置为0来实现。但是就算把动画速度设置为0,骨骼还是会继续模拟的,所以当场景过多时,对性能会有一定影响。优化场景的方法也很简单,每次创建残影时不再需要实例化整个模型,而是便利模型所有的精灵,读取精灵的顶点、UV等数据,创建出一个网格,通过设置网格的材质来实现残影效果。模型在创建残影时就不需要实例化来创建了,并且在创建残影时的网格也可以加到对象池当中复用,下一次同一个模型来创建残影时就可以通过对象池来获取数据,只需要把当前模型上精灵的顶点、UV等数据复制到那个网格中。但是这个操作比较耗时,可以开一个线程来做,这样在主线程几乎体会不到性能的消耗。在获取那些精灵的组件时,也可以把组件缓存起来,下次从缓存的地方来获取。
在这里插入图片描述

2D骨骼转GPU动画

先看右边这张图,密密麻麻的点就是一个2D角色,这个角色不是一个简单的面片,每个角色有100多个顶点,这里面有10000个角色。2D残影优化说到的骨骼性能问题,在这种同屏上有很多2D单位有动画模拟的时候性能是消耗比较高的。优化方法和2D残影差不多,就是把CPU的骨骼运动转到GPU上面。GPU的动画优点是可以解决CPU模拟动画时会有的性能消耗问题,但缺点是无法使用动画控制器功能,如动画过渡,动态骨骼也无法绑定。如果模型的顶点数量太多,单个动画的时长太长,就会导致这张贴图过大。
在这里插入图片描述
下面简单讲一下转换的思路。
转换动画的第一步是编写工具来读取骨骼动画上面的顶点数据,导出为GPU需要的资源。最左边的这张图包含了所有的动画文件和动画配置。动画配置指的是一些所需数据,比如动画时长,每个动画的名称、缩影等等。图集就是原来的贴图,图集材质就是自定义的一个Shader,可以自己控制这个骨骼动画的播放,网格就是读取模型上的所有精灵,通过这个精灵来生成一个网格。
中间这张图就是转换动画图的思路。动画的帧数一帧帧循环,然后读取网格上面的顶点数量,把每一个顶点的数据储存到图片上面,然后就可以通过Shader来驱动动画的模仿。右边的那张是生成的动画图,下面的是每个顶点在图片中的布局。比如第一帧,横向的X组成了所有顶点的位置。第二帧放上去,就可以组成那些动画的顶点数据了。右边那张图可以看到,顶点的数量决定了每张图的X宽度,动画的长度决定了图片的高度,这意味着模型顶点太多就会影响图片大小,包括动画时长。
在这里插入图片描述
Shader是如何驱动图片来驱动顶点的呢?先看Shader的顶点函数,顶点函数中有顶点的ID和它的动画时间,动画时间是自己定义的一个参数。有这两个数据后,可以对贴图进行采样,采样后就能得到每个顶点的位置了。X就是1除以图片宽度,可以理解为每个像素的宽度,然后乘上顶点ID,得到每一个顶点在动画中的位置。例如,采样之后可以设置动画的时间,动画时间是一个0-1的值,0是动画的第一帧,1是动画的最后一帧,对应的采样中的Y,X就是顶点ID乘上贴图,X图片宽度可以理解为采样第几个顶点。通过采样后的顶点数据可以改变顶点的位置,每一帧进行这个操作就能用Shader驱动动画来播放了。
第三步,编写自己的动画控制器脚本。脚本中可以提供自己需要的功能,比如播放动画、播放指定动画、设置动画的播放速度、从指定时间开始播放,上一张动画配置里面记录的数据可以放到这个动画控制器里使用,这样就可以操作整个动画的播放了。
在这里插入图片描述

定时器

如果游戏中使用了大量的定时器,比如延迟一帧或延迟N秒作为一个函数,多少秒之后会销毁这个弹道等等,那么每秒钟可能会创建上千个定时器。每个定时器都单独处理是比较消耗性能的,可以把它们统一管理起来,类似于ECS的思想。做法就是做自己的一个定时器,把需要的函数传递过去,包括延迟时间,在定时器里面统一循环,到达时间了,函数就来执行,统一管理后会比单独处理的性能高很多。游戏中如果有一些移动之类的操作都可以使用ECS的思想来统一管理。
在这里插入图片描述

优化DrawCall合批处理

DrawCall如果比较高,它的帧率是比较低的,就会对性能有影响。战斗UI如果比较复杂就必须优化战斗的DrawCall,因为战斗UI是常驻的,很影响战斗中的体验,不能让UI占用太多的性能消耗。如果不在战斗中,只打开几个普通UI,性能占用就不会太大,但是如果像背包这种大量数据的地方就需要注意。UI一般可以采用动态UI和静态UI来分离,可以合批处理。例如,字体和贴图一般来说不要在一个地方混着,可以把它分离出来做合批处理;战斗中的特效,如被击特效、身上挂的buff,以及其他渲染对象尽量保持在同一渲染层,就可以做合批处理。
在这里插入图片描述

优化碰撞层

如果战斗的物理对象比较多,也是比较消耗CPU性能的,此时就要优化碰撞层,去掉不必要的碰撞检测。比如,玩家的子弹不会和玩家碰撞,子弹不会和场景脚底的障碍物碰撞,此时就要根据自己的项目来设置物理层,越简单越好。场景的物理层可以分为地板的、脚底的障碍物、大型障碍物、可破坏的障碍物等。不同层的碰撞是不会相互影响的,调整好碰撞层后可以减少物理的消耗。

在这里插入图片描述

优化粒子特效

粒子模拟使用的是CPU进行计算,当粒子的发射数量比较多时,CPU压力就会比较大。我们应该在前期就把粒子的检测工具制作好。比如特效的贴图一般是根据自己项目来制定的,上不同的平台,贴图的大小就有不一样的规范。另外,每种粒子的粒子数量也要有规范,如果同屏只有一个仙法或比较大的特效,此时它的规范就要宽一点。如果是小特效但在同屏有很多个,此时应该有不一样的规范。
可以在工具里面自定义一些规则来检测每个粒子的数量。但是数量在同屏中会出现几个弹道,一般有两种情况:第一种是比较简单的弹道,可以通过配置表获取就能知道同屏会出现几个弹道;第二种是像鬼谷一样,很多地方都会修改弹道数量,这个时候可以制作一个检测器。在内部开发时,这个检测器会运行,检测同屏中每个弹道的粒子数量以及个数。将检测器获取到的数量上传到本地服务器记录异常特效,在优化完特效后就可以将异常特效移除。开发中制作新技能的时候,弹道肯定会进行测试,通过检测器就能获取到弹道的数量,以应对比较复杂的弹道情况。检测器获取粒子数量一样可以使用缓存,把特效脚本缓存起来,下次获取的时候就不太需要编译了,性能影响也是比较小的。
在这里插入图片描述
不只是特效,所有资源都应该有一个规范。比如音效的格式、场景的贴图大小、怪物角色的顶点数量等等,应该都有一个工具来检测它们。还有,要尽量在前期考虑好游戏中哪些逻辑是比较影响性能的,根据实际情况考虑使用多线程还是Jobs。如果游戏的复杂度比较高,也可以考虑使用ECS来制作。
Unity的ECS性能是比较高的。两个程序之间,它们可能本地有几十个或者几十万个实体对象,在做联机的时候是需要同步这两个实体对象的。我们制作了一个小demo测试了一下,几十万的实体对象来同步,ECS可以把所有的组件自动生成同步代码,同步系统会自动把这些数据同步到两台服务器,几十万的实体一帧内只需要几毫秒来运行,它的效率是非常夸张的。前期可以把这些项目的规范制定好,包括程序是否需要采用DOTS还是GPU的动画,这样后期优化的工作就会比较少。

原文连接

相关文章:

关于游戏性能优化的技巧

关于游戏性能优化的技巧 游戏性能优化对象池Jobs、Burst、多线程间隔处理定时更新全局广播缓存组件缓存常用数据2D残影优化2D骨骼转GPU动画定时器优化DrawCall合批处理优化碰撞层优化粒子特效 游戏性能优化 好久没有在CSDN上面写文章了,今天突然看到鬼谷工作室技术…...

antdesignpro实现滚动加载分页数据

原理解析:每滚动一次相当于翻页,请求后端时给的页码参数要想办法加1,后端才能根据页码给出相应数据 注意后端收到页码参数之后要准确计算出每页的首行数据,关键逻辑代码: # 根据前端传的页码,进行计算下一…...

步兵 cocos2dx 加密和混淆

文章目录 摘要引言正文代码加密具体步骤代码加密具体步骤测试和配置阶段IPA 重签名操作步骤 总结参考资料 摘要 本篇博客介绍了针对 iOS 应用中的 Lua 代码进行加密和混淆的相关技术。通过对 Lua 代码进行加密处理,可以确保应用代码的安全性,同时提高性…...

【算法设计与分析】——动态规划算法

🎃个人专栏: 🐬 算法设计与分析:算法设计与分析_IT闫的博客-CSDN博客 🐳Java基础:Java基础_IT闫的博客-CSDN博客 🐋c语言:c语言_IT闫的博客-CSDN博客 🐟MySQL&#xff1a…...

WPF组合控件TreeView+DataGrid之DataGrid封装

(关注博主后,在“粉丝专栏”,可免费阅读此文) wpf的功能非常强大,很多控件都是原生的,但是要使用TreeViewDataGrid的组合,就需要我们自己去封装实现。 我们需要的效果如图所示&#x…...

PIL/Pillow

Abstract PIL(Python Imaging Library)是一个用于图像处理的 Python 库。它提供了广泛的功能,包括图像加载、保存、调整大小、裁剪、旋转、滤镜应用等。 由于 PIL 的开发停止在 2009 年,因此推荐使用其后续的维护版本 Pillow。Pillow 是一个兼容 PIL 接…...

ARM 汇编入门

ARM 汇编入门 引言 ARM 汇编语言是 ARM 架构的汇编语言,用于直接控制 ARM 处理器。虽然现代软件开发更多地依赖于高级语言和编译器,但理解 ARM 汇编仍然对于深入了解系统、优化代码和进行低级调试非常重要。本文将为您提供一个简单的 ARM 汇编入门指南…...

SQL进阶:多表查询

在SQL基础部分,我们在讲解的过程中只用到了单表查询。但实际上,常见的业务场景单表查询不能满足,或者拆分查询性能过慢。这个时候我们就需要用到连接查询。即查询多表按一定规则合并后的数据。 注意,合并后的数据也是表&#xff…...

多层负载均衡实现

1、单节点负载均衡 1)站点层与浏览器层之间加入了一个反向代理层,利用高性能的nginx来做反向代理 2)nginx将http请求分发给后端多个web-server 优点: 1)DNS-server不需要动 2)负载均衡:通过ngi…...

Redis取最近10条记录

有时候我们有这样的需求,就是取最近10条数据展示,这些数据不需要存数据库,只用于暂时最近的10条,就没必要在用到Mysql类似的数据库,只需要用redis即可,这样既方便也快! 具体取最近10条的方法&a…...

Mybatis之增删改查

目录 一、引言 二、Mybatis——增 举例:添加用户 三、Mybatis——删 举例:删除用户 四、Mybatis——改 举例:修改用户 五、Mybatis——查 六、注意 END: 一、引言 书接上回,我们在了解完mybatis之后,肯…...

Go 代码检查工具 golangci-lint

一、介绍 golangci-lint 是一个代码检查工具的集合,聚集了多种 Go 代码检查工具,如 golint、go vet 等。 优点: 运行速度快可以集成到 vscode、goland 等开发工具中包含了非常多种代码检查器可以集成到 CI 中这是包含的代码检查器列表&…...

SwiftUI 趣谈之:绝不可能(Never)的 View!

概览 SwiftUI 的出现极大的解放了秃头码农们的生产力。SwiftUI 中众多原生和自定义视图对于我们创建精彩撩人的 App 功不可没! 不过,倘若小伙伴们略微留意过 SwiftUI 框架头文件里的源代码,就会发现里面嵌有一些奇怪 Never 类型&#xff0c…...

etcd是什么

目录 1.关于etcd2.应用场景 本文主要介绍etcd 概念和基本应用场景。 1.关于etcd etcd是一个开源的、分布式的键值存储系统,用于共享配置和服务发现。它是由CoreOS团队开发的,主要用于实现分布式系统的配置管理和服务发现。 etcd的主要特性包括&#x…...

应用全局的UI状态存储AppStorage

目录 1、概述 2、StorageProp 2.1、观察变化和行为表现 3、StorageLink 3.1、观察变化和行为表现 4、从应用逻辑使用AppStorage和LocalStorage 5、从UI内部使用AppStorage和LocalStorage 6、不建议借助StorageLink的双向同步机制实现事件通知 6.1、推荐的事件通知方式…...

MySQL数据库 触发器

目录 触发器概述 语法 案例 触发器概述 触发器是与表有关的数据库对象,指在insert/update/delete之前(BEFORE)或之后(AFTER),触发并执行触发器中定义的soL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的完整性,日志记录&am…...

C语言学习之给定任意的字符串,清除字符串中的空格

实例要求:给定任意的字符串,清除字符串中的空格,并将其输出;实例分析:1、指针函数实现,需要注意指针函数的返回值是一个指针类型;2、字符类型的数组实现,循环遍历并赋给新的数组&…...

由实验数据进行函数拟合的python实现

0.引言 已知公式求参的过程,对工程而言,一般是一个线性拟合或者非线性拟合的过程。我们现在来以代码片段为例,来描述如何求参。一般这个过程会涉及超定方程的计算。这个过程,原本需要使用matlab,现在python照样可以做…...

<JavaEE> 基于 UDP 的 Socket 通信模型

目录 一、认识相关API 1)DatagramSocket 2)DatagramPacket 3)InetSocketAddress 二、UDP数据报套接字通信模型概述 三、回显客户端-服务器通信 1)服务器代码 2)客户端代码 一、认识相关API 1)Data…...

Golang 链表的基础知识

文章目录 链表链表基础知识部分链表的存储方式链表的定义链表的操作性能分析 链表 更多有关于go链表的内容可以见这篇文章链表的创建和读取 链表基础知识部分 什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,…...

webpack 常见面试题

1、什么是webpack(必会) webpack是一个打包模块化javascript的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建模块化…...

three.js实战模拟VR全景视图

文章中使用到的案例图片都来源于&#xff1a;Humus - Textures 里面有很多免费的资源&#xff0c;可以直接下载&#xff0c;每个资源里面都提供6个不同方位的图片&#xff0c;我们通过threejs稍微处理一下&#xff0c;就能实现以下3D效果的场景了。 <template><div …...

聊聊Spring Boot配置文件:优先级顺序、bootstrap.yml与application.yml区别详解

Spring Boot 配置文件 优先级顺序 在Spring Boot中&#xff0c;配置文件的优先级顺序是&#xff1a;bootstrap.yml > application.yml > application-{profile}.yml&#xff0c;其中 {profile} 表示不同的环境配置&#xff0c;如 dev、test、prod 等。当存在相同名称的…...

Milvus向量数据库基础用法及注意细节

1、Milvus数据类型与python对应的数据类型 Milvus Python DataType.INT64 numpy.int64 DataType.INT32 numpy.int32 DataType.INT16 numpy.int16 DataType.BOOL Boolean DataType.FLOAT numpy.float32 DataType.DOUBLE numpy.double DataType.ARRAY list DataT…...

虚拟机多开怎么设置不同IP?虚拟机设置独立IP的技巧

随着虚拟化技术的不断发展&#xff0c;虚拟机已经成为了许多人的必备工具。在虚拟机中&#xff0c;我们可以轻松地创建多个虚拟机&#xff0c;并在每个虚拟机中设置不同的IP地址。下面&#xff0c;我们将介绍如何在虚拟机中设置独立IP地址的方法。 一、虚拟机多开设置不同IP的方…...

使用Docker-镜像命令

镜像名称一般分两部分组成:[repository]:[tag] 在没有指定tag时&#xff0c;默认是latest&#xff0c;代表最新版本的镜像 目录 案例一&#xff1a;从DockerHub中拉取一个nginx镜像并查看 1.1. 首先去镜像仓库搜索nginx镜像&#xff0c;比如DockerHub ​编辑 1.2.操作拉取n…...

4.3 C++对象模型和this指针

4.3 C对象模型和this指针 4.3.1 成员变量和成员函数分开存储 在C中&#xff0c;类内的成员变量和成员函数分开存储 只有非静态成员变量才属于类的对象上 #include <iostream>class Person { public:Person() {mA 0;} //非静态成员变量占对象空间int mA;//静态成员变量…...

计算机网络——计算机网络的概述(一)

前言&#xff1a; 面对马上的期末考试&#xff0c;也为了以后找工作&#xff0c;需要掌握更多的知识&#xff0c;而且我们现实生活中也已经离不开计算机&#xff0c;更离不开计算机网络&#xff0c;今天开始我们就对计算机网络的知识进行一个简单的学习与记录。 目录 一、什么…...

基于多反应堆的高并发服务器【C/C++/Reactor】(中)ChannelMap 模块的实现

&#xff08;三&#xff09;ChannelMap 模块的实现 这个模块其实就是为Channel来服务的&#xff0c;前面讲了Channel这个结构体里边它封装了文件描述符。假如说我们得到了某一个文件描述符&#xff0c;需要基于这个文件描述符进行它对应的事件处理&#xff0c;那怎么办呢&…...

微信小程序实现一个音乐播放器的功能

微信小程序实现一个音乐播放器的功能 要求代码实现wxml 文件wxss 文件js文件 解析 要求 1.页面包含一个音乐列表&#xff0c;点击列表中的音乐可以播放对应的音乐。 2.播放中的音乐在列表中有标识&#xff0c;并且可以暂停或继续播放。 3.显示当前音乐的播放进度和总时长&#…...