【unity小技巧】unity性能优化以及如何进行性能测试

news2024/11/13 9:32:28

文章目录

  • 前言
  • GPU性能优化
    • 打包素材
  • CPU性能优化
    • 代码执行优化
  • 性能测试
    • Vector2.Distance 和 sqrMagnitude哪个好?
    • 动画切换优化
    • shader属性优化
  • URP渲染器资产优化
  • 对象池优化
  • 删除没必要的空函数
  • 图片、音乐音效、贴图等素材压缩
  • ScriptableObject优化参数
  • 参考
  • 完结

前言

功能的实现必不可缺,当然也要注意程序性能的优化。随着项目越来越大,优化变得至关重要,它能确保你的游戏任然可以快速流畅地运行。如果想知道如何对unity进行性能优化以及如何进行性能测试,那就继续往下看吧。

GPU性能优化

打包素材

点击运行游戏,打开分析帧调试器
在这里插入图片描述
然后点击启用工具,逐步查看Unity如何将图像渲染到屏幕上,我们可以看到有五个绘制调用,分别是背景、地板、敌人、玩家和玩家的武器
在这里插入图片描述
如果我们可以使用更少的绘制调用来实现相同的结果,它肯定会更快,我们可以使用Sprite Atlas来做到这一点在这里插入图片描述
把所有的资源都拖入,进行打包(注意这里属性和图片差不多,如果你是像素素材同样可以选择点类型,无压缩进行优化)
在这里插入图片描述
打包效果
在这里插入图片描述
我们再回去分析帧调试器查看,你会看到我们只有三个绘制调用
在这里插入图片描述
既然前面打包了,那么为什么不是一个呢,你可以看到它没有将敌人与玩家进行批处理,因为它们具有不同的材质
在这里插入图片描述
如果我们没必要使用不同的材质,我们可以选择把所有的材质换成一样的
在这里插入图片描述
这样的话,可以看到就仅仅绘制了一次,这明显加快了GPU绘制
在这里插入图片描述

CPU性能优化

代码执行优化

我们经常会有书写这样的代码,比如新增EnemyBehavior.cs脚本挂载在敌人身上,这里在Update中每一帧都调用敌人面向玩家等操作,但是这完全是资源的浪费,在播放模式下没有必要在每帧上都这样做
在这里插入图片描述
运行状态下,打开Profiler 分析器
在这里插入图片描述
只需单击图表上的任意位置,我们的游戏就会暂停,以便我们可以对其进行分析,可以看到目前EnemyBehavior的更新占用了1.2%CPU使用率
在这里插入图片描述
这时候我们可以选择使用脚本委托和定时器控制它的更新频率,比如新增Ticker脚本,没过0.2秒调用TickEvent方法
在这里插入图片描述
逻辑不在Update中调用了,注册委托每0.2s调用一次
在这里插入图片描述
在这里插入图片描述
可以看到敌人是仍然总是面向玩家,但不是每帧都运行该逻辑,运行频率被降低了,但它并没有对游戏中敌人的行为产生明显的影响
在这里插入图片描述

性能测试

但是当涉及到某些代码片段时,仅用这些王具很难判断出什么实际上性能更高,有时您只需要自己测试一些东西,所以让我向您展示如何设置基准测试,来让您运行自己的测试

新增性能测试的类

using UnityEngine;
using System.Diagnostics;

// 用于性能测试的类
public class BenchMarker : MonoBehaviour
{
    [Range(0, 1000000)]
    [SerializeField] private float _iterations; // 迭代次数
    private BenchMarkTest _benchMarkTest; // 性能测试对象
    private Stopwatch sw; // 计时器

    // 在启动时获取BenchmarkTest组件
    private void Awake()
    {
        _benchMarkTest = GetComponent<BenchMarkTest>(); // 获取BenchmarkTest组件
    }

    // 执行测试的方法
    public void RunTest()
    {
        sw = Stopwatch.StartNew(); // 创建并启动计时器

        for (int i = 0; i < _iterations; i++)
        {
            _benchMarkTest.PerformBenchmarkTest(); // 执行性能测试
        }

        sw.Stop(); // 停止计时器
        UnityEngine.Debug.Log(sw.ElapsedMilliseconds + "ms"); // 输出测试时间
    }
}

Vector2.Distance 和 sqrMagnitude哪个好?

在Unity中,Vector2.Distance 和 sqrMagnitude 都是用来计算向量之间距离的方法,但它们的性能特性略有不同。

新增脚本,这里距离判断我们使用了Vector2.Distance,实际它的性能很差,因为它在内部计算中使用了开方运算 Mathf.Sqrt,开方运算通常比较昂贵,尤其是在大量计算时会消耗较多的计算资源。
在这里插入图片描述
点击测试
在这里插入图片描述
可以看到它平均需要执行390毫秒左右
在这里插入图片描述
我们现在换成性能更高的办法.sqrMagnitude进行距离判断,sqrMagnitude 属性返回向量的平方长度,与Vector2.Distance 方法不同,sqrMagnitude 不进行开方操作,因此它的计算代价更低。
在这里插入图片描述
重新测试发现,平均快了几毫秒左右
在这里插入图片描述
事实证明,我们可以继续使用Vector2.Distance 进行距离计算,因为它的可读性更高且对性能的影响似乎可以忽略不计

动画切换优化

通常我们都是这样进行动画切换
在这里插入图片描述
测试效果
在这里插入图片描述
但是其实有更好的办法,其实Unity获取这个动画字符串后会在幕后对其进行哈希处理,所以之后我们可以通过只做一次来节省时间,所以在这里我们可以提前对动画字符串进行哈希处理
在这里插入图片描述
可以看到,节约了近30毫秒的性能
在这里插入图片描述

shader属性优化

我们也想给改变shader属性来执行相同的操作,比如这里实现更改敌人的材质的颜色
在这里插入图片描述
改变颜色
在这里插入图片描述
测试结果,可以看到大概需要610ms左右
在这里插入图片描述
让我们尝试同样对字符串进行哈希处理
在这里插入图片描述
结果,发现速度加快了将近一半
在这里插入图片描述

URP渲染器资产优化

在URP渲染器资产中,如果你不需要深度纹理(Depth Texture)或不透明纹理(Opaque Texture )可以将其关闭,因为这些纹理最终会根据您的相机所看到的内容绘制纹理的深度或不透明度。如果你关闭它们,最终会优化大量不必要的内存
在这里插入图片描述

对象池优化

但如果您进行了大量实例化和销毁操作例如,如果您有一个带有大量子弹的游戏,那么您将需要实现个对象池系统,参考:【Unity小技巧】Unity探究自制对象池和官方内置对象池(ObjectPool)的使用

删除没必要的空函数

如果你的脚本中有空函数,请删除它们,每个函数也会有一点开销成本,即使它实际上没有执行任何内容
在这里插入图片描述

图片、音乐音效、贴图等素材压缩

合理压缩素材会很大的减低我们的内存

比如先看看我的背景没有压缩,它是6.8MB
在这里插入图片描述
低质量压缩它是1.1MB
在这里插入图片描述
ps:合理的使用压缩,确保它不会明显降低你的画面质量

ScriptableObject优化参数

不同的敌人实例之间更改这些变量,现在当我点击播放时,这些脚本中的每一个都将有自己的实例,并且在这些类实例中的每一个上这些变量将在内存中拥有自己的副本,并且对于这种情况完全是浪费
内存中拥有自己的副本,并且对于这种
这里我们可以使用ScriptableObject
在这里插入图片描述
配置
在这里插入图片描述
这样我们就不会为这些变量的创建数十几个副本存放在内存中,它只是位于ScriptableObject上的一个副本,每个敌人都只指向该ScriptableObject的对象

即使你想要实现不同的敌人配置,你也只需创建另一个ScriptableObject的对象资产,绑定它并修改参数即可
在这里插入图片描述

参考

https://www.youtube.com/watch?v=kJ5I9md9NG4

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1973021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构与算法-15高级数据结构_树论(堆树)

堆树 1 简介 1.1 什么是堆树 定义&#xff1a;堆树是一种特殊的完全二叉树&#xff0c;其中每个节点的值都遵循一定的堆属性。具体来说&#xff0c;堆分为最大堆和最小堆。 最大堆&#xff1a;在最大堆中&#xff0c;每个父节点的值都大于或等于其任何子节点的值。这意味着…

SpringMVC02

1.拦截器 1.1基本概念 SpringMVC 中的Interceptor拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆等操作。对于SpringMVC拦截器的定义方式 有两种: 实现接口:org.springframework.web.ser…

CAD-文字、图块、多行文字,沿多段线对齐到多段线的顶点,沿直线进行均分,都可以操作

图块和文字对齐直线-均布直线-对齐多段线顶点-旋转平行 (defun c:duiqi () ;将图块与直线对齐&#xff0c;并均分。;先创建的图块排最右;先等分的坐标排最右;刚好对应了(defun MoveToPosition (Blockname p_list / ent refPoint dx dy) ;移动对象到指定坐标(prompt "\nSel…

【Git-驯化】一文学会git中对代码进行存储操作:git stash技巧

【Git-驯化】一文学会git中对代码进行存储操作&#xff1a;git stash技巧 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内…

@change事件传参

change事件传参 change"(value)>handleChange(value, item,index)" 这样可以接收index参数区分是哪一个组件事件&#xff0c;又可以接收子组件传的value值 <div class"boxItem" v-for"(item, index) in checkPeopleList" :key"inde…

VUE实现TAB切换不同页面

VUE实现TAB切换不同页面 实现效果 资源准备 ReceiveOrderList, TodoListMulti, SignList 这三个页面就是需要切换的页面 首页代码 <template><div><el-tabs v-model"activeTab" type"card" tab-click"handleTabClick"><…

用于相位解包的卷积和空间四向 LSTM 联合网络

原文&#xff1a;A Joint Convolutional and Spatial Quad-Directional LSTM Network for Phase Unwrapping 作者&#xff1a;Malsha V. Perera 和 Ashwin De Silva 摘要&#xff1a; 相位展开是一个经典的病态问题&#xff0c;其目标是从包裹相位中恢复真实的相位。本文&…

鸿蒙(API 12 Beta2版)NDK开发【使用Node-API扩展能力接口】

简介 [扩展能力接口]进一步扩展了Node-API的功能&#xff0c;提供了一些额外的接口&#xff0c;用于在Node-API模块中与ArkTS进行更灵活的交互和定制&#xff0c;这些接口可以用于创建自定义ArkTS对象等场景。 Node-API接口开发流程参考[使用Node-API实现跨语言交互开发流程]…

非负数、0和正整数 限制最大值且保留两位小数在elementpuls表单中正则验证

一、结构 <el-form-item label"单价&#xff1a;" prop"price"><el-inputv-model.trim"formData.price"placeholder"请输入"blur"formMethod.fixTwo"><template #append>(元)</template></el-i…

基础算法:离散化(C++实现)

文章目录 1. 离散化的定义2. 离散化例题2.1 离散化二分2.2 离散化哈希表 1. 离散化的定义 离散化是一种在程序设计和算法优化中常用的技术&#xff0c;其核心思想是将无限空间中有限的个体映射到有限的空间中去&#xff0c;以此提高算法的时空效率。具体来说&#xff0c;离散化…

Docker 安装 GitLab教程

本章教程,主要介绍如何在Docker 中安装GitLab。 GitLab 是一个开源的 DevOps 平台,提供了一整套工具,用于软件开发生命周期的各个阶段,从代码管理到 CI/CD(持续集成和持续交付/部署),再到监控和安全分析。 一、拉取镜像 docker pull gitlab/gitlab-ce:latest二、创建 G…

【React】探讨className的正确使用方式

文章目录 一、className的正确用法二、常见错误解析三、实例解析四、错误分析与解决五、注意事项六、总结 在React开发中&#xff0c;正确使用className属性对组件进行样式设置至关重要。然而&#xff0c;由于JavaScript和JSX的特殊性&#xff0c;开发者常常会犯一些小错误&…

ShardingSphere实战(2)- 水平分表

上篇博客&#xff0c;我们讲了 ShardingSphere实战&#xff08;1&#xff09;- 分库分表基础知识&#xff0c;这篇博客&#xff0c;正式开始实操。 项目环境&#xff1a; JDK11 MySQL 8.0.30 Springboot 2.7.4 Mybatis ShardingSphere HikariCP 连接池 一、Maven 依赖 <…

filebeat发送日志

filebeat: 1.可以在本机收集日志 2.也可以远程收集日志 3.轻量级的日志收集系统&#xff0c;可以在非Java环境运行 logstash是在jvm环境中运行&#xff0c;资源消耗很高&#xff0c;启动一个logstash需要消耗500M左右的内存 filebeat只消耗10M左右的内存 test3是装有logstash的…

C语言的内存布局

根据 C 语言的内存布局规律&#xff0c;通常局部变量和全局变量哪一个的地址更小&#xff1f; 答&#xff1a;如图所示。 下面代码中&#xff0c;为何两个不同的变量可以存放在同一个地址上&#xff1f; #include <stdio.h> void func1(void); void func2(void); voi…

安装 qcloud-python-sts 失败 提示 gbk codecs decode byte 应该如何解决

安装 qcloud-python-sts 失败 提示 gbk codecs decode byte 应该如何解决 解决方案&#xff1a; 将windows 修改为utf-8编码格式 解决步骤如下&#xff1a; 1. 进入控制台 2. 点击区域 4. 点击管理 4.勾选UTF-8 5.重启系统即可

Java零基础之多线程篇:线程间如何通信?

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

计算机技术基础 (bat 批处理)Note6

计算机技术基础 &#xff08;bat 批处理&#xff09;Note6 本节主要讲解FOR命令语句&#xff08;循环&#xff09;在 bat 批处理中的使用 (part 2) 变量延迟 命令语句 在没有开启变量延迟的情况下&#xff0c;批处理命令行中的变量改变&#xff0c;必须到下一条命令才能体现…

C++ STL在算法题中的常用语法

Vector 1.将vector<int>中的元素全部置换为0 fill(vec.begin(), vec.end(), 0); 2.vector容器是可以直接用比较是否值等的&#xff01; Unordered_set 1. unordered_set的删除&#xff08;count的值也会减少&#xff09; 2.unordered_map中的int默认值是0&#xff0c;…

Prometheus-v2.45.0+Grafana+邮件告警

目录 普罗米修斯监控架构介绍 Prometheus 监控架构 1. 数据抓取&#xff08;Scraping&#xff09; 2. 时序数据库&#xff08;TSDB&#xff09; 3. 数据模型 4. PromQL 查询语言 5. 告警&#xff08;Alerting&#xff09; 6. Alertmanager 7. 可视化&#xff08;Visu…