Unity性能优化

server/2024/10/23 6:28:44/

前言

当游戏开发使用传统的OPP(面向对象编程)面对大量的Game object时FPS会显著降低,而使用Dots(面向数据编程)性能依旧很好

计算机内存基础

CPU自身有三级高速缓存,L1,L2,L3,其中CPU访问(L1)缓存最快,容量最小,第三级(L3)缓存最慢,容量最大。

内存是指CPU拿取数据的起源点,CPU访问内存所需的时钟周期远大于访问高速缓存所需的时钟周期

CPU处理数据的速度非常快,因此需要高速缓存区避免CacheMiss高速缓存缺失,以确保CPU一直在运行(未休息)

CPU操作数据会先从一,二,三级缓存中取得数据,如果数据不在三级缓存,就需要寻址内存中的数据

Dots(Data Oriented Tech Stack) 面向数据的技术堆栈

是一系列的集合

Entity Component System(ECS) :实体组件系统

Burst Compiler :Burst编译器 ,也是LLVM编译器

Job System :任务系统

masmatic:数学库

ECS

ECS即实体(Entity),组件(Component),系统(System),其中Entity,Component皆为纯数据向的类,System负责操控他们,这种模式会一定程度上优化我们的代码速度。

  • Entities:游戏中的事物,但在ECS中他只作为一个Id
  • Components:与Entity相关的数据,但是这些数据应该由Component本身而不是Entity来组织。(这种组织上的差异正是面向对象和面向数据的设计之间的关键差异之一)。
  • Systems:Systems是把Components的数据从当前状态转换为下一个状态的逻辑,但System本身应当是无状态的。例如,一个system可能会通过他们的速度乘以从前一帧到这一帧的时间间隔来更新所有的移动中的entities的位置。

为什么ECS架构会加快性能?

将传统游戏对象杂乱无序的组件归类,当读取数据时无法从L1中找到,先从这一堆杂乱无序的内中找到需要的内存,然后将它读取并且移动到L1缓存中,移除L1不需要的数据(因为内存容量小),这个过程就会造成CacheMiss

而当我们用ECS的架构组织数据,它会将相同组件Component 整齐排列在内存中,当我们开始遍历相同组件时并不需要一个个从内存中读取,可以指定一个长度一次性全读进来放入缓存中,因此避免了CacheMiss

因此ECS模式无疑更加适合现代CPU架构

Burst Complier

Burst是使用LLVM从IL/.NET字节码转换为高度优化的本机代码的编译器。它作为Unity package发布,并使用Unity Package Manager集成到Unity中。 它全盘接管了我们编写的新C#编译工作,可以让我们在特定模式下无痛写出高性能代码。

JobSystem

它可以让我们无痛写出多线程并行处理的代码,并且内部配合Burst Complier进行SIMD优化。 你可以把JobSystem和Unity的ECS一起用,两者配合可以让为所有平台生成高性能机器代码变得简单。

masmatic

一个C#数学库提供矢量类型和数学函数(类似Shader里的语法)。由Burst编译器用来将C#/IL编译为高效的本机代码。 

DOTS配置

官方文档

注意unity版本

要使用 Entities 包,必须安装 Unity 版本 2022.3.0f1 及更高版本

必须使用可编程渲染管线:

内置渲染管线(Built-in Render Pipeline)不是可编程渲染管线 

URP (Universal Render Pipeline)通用渲染管线,可编程

(High Definition Render Pipeline)(HDRP)高清渲染管线。可编程

安装包

  • com.unity.entities工具包:包括ECS,Burst,Job System,masmatic
  • com.unity.entities.graphics渲染entity
  • com.unity.physics:ECS物理
  • 通过window->packageManager->'+'加号->install package by name->install搜索
  • 注意所有包的版本要一致,如果无法安装指定版本通过:
  • 工程文件Packages/manifest.json中手动修正版本,重启引擎

IDE

版本要更新

unity设置

Edit > Project Settings > Editor菜单,然后启用 Enter Play Mode Options 设置,但禁用 Reload Domain 和 Reload Scene

c#Attribute特性

反射:

如果让你在不修改源代码的情况下,通过一个函数(参数为类类型),运行时获取所有参数为空的公有成员方法,这是不可能的

但是如果可以修改类呢?

  • 可以让每一个类继承Object基类,每个类都手写一个FuncData的表(所有MetaData元数据(FuncData,PropData)数据表)
  • 每个类都重写基类的public FuncData[] getFuncData();方法

这样就可以运行时获取

Attribute:

比如我们通过反射可以快速查找一个类是否仅包含数据

那如果我想要对特定类不同控制?

Attribute是反射的扩充,目的是在不破坏原有代码的  情况下,在代码的元数据上附加一些信息

[……]语法:

实际是调用一个继承Attribute类的特性(自定义/内置)的构造

Attribute:

在运行时动态地获取和操作类和对象的信息(添加元数据),提供了一种声明性方式[……]来指定代码的行为或其他相关信息,可以被应用于程序集、类、方法、属性、事件等

运行时:编译期间无法确定的,接收非特定对象,结果会根据具体对象而不同

创建一个custom自定义特性Attribute:

继承自 System.Attribute 类,

其中AttributeUsage 内置特性,用于指定一个自定义特性可以应用于哪些代码元素上可以控制特性的有效性目标(如类、方法、属性等)以及是否允许特性被多次应用于同一个目标。

  • AttributeTargets:指定特性可以应用于哪些目标(如类、方法、属性等)。
  • Inherited:指定特性是否可以被派生类继承。
  • AllowMultiple:指定是否允许在同一目标上多次应用该特性。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = false)]  
public class MyCustomAttribute : Attribute  
{  public string Description { get; }  public MyCustomAttribute(string description)  {  Description = description;  }  
}

使用:通常通过方括号 [] 语法使用特性:

[MyCustomAttribute("This is a class description")]  
public class MyClass  
{  [MyCustomAttribute("This is a method description")]  public void MyMethod()  {  // Method implementation  }  
}

在运行时,可以使用反射(attribute基类)来访问特性并读取其值:

通过Attribute.GetCustomAttribute获取到了,指定类关联的特性类,

var type = typeof(MyClass);  // 获取类上的MyCustomAttribute  
var classAttribute = (MyCustomAttribute)Attribute.GetCustomAttribute(type, typeof(MyCustomAttribute));  
Console.WriteLine(classAttribute.Description);  // 获取方法上的MyCustomAttribute  
var method = type.GetMethod("MyMethod");  
var methodAttribute = (MyCustomAttribute)Attribute.GetCustomAttribute(method, typeof(MyCustomAttribute));  
Console.WriteLine(methodAttribute.Description);

常见的 C# 内置特性:

  • Obsolete:标记一个程序元素为过时。
  • Serializable:指示一个类可以被序列化。
  • DllImport:用于声明非托管代码的外部方法。
  • Conditional:允许根据编译符号的存在与否来控制方法的调用。

C#常见内置特性功能:

  1. 查询类的信息:获取类的名称、基类、实现的接口、包含的字段、属性、方法等。
  2. 创建对象:即使不知道类的具体类型,也可以通过反射在运行时创建该类的对象实例。
  3. 调用方法:根据方法名和参数类型,在运行时调用对象的方法。
  4. 访问字段和属性:读取或修改对象的字段和属性值,即使这些字段和属性在编译时是未知的。

其他性能优化

Object Pooling对象池

用于减少对象Instantiate()创建和destroy()销毁的性能开销,减少创建销毁api的调用

维护一个list<>

创建-》如果pool中没有obj,那么就instantiate,如果有就从池内取出对象

销毁—》使用完成放回池内

组合设计模式

  • MVC(Model-View-Controller)
  • MVP(Model-View-Presenter)
  • MVVM(Model-View-ViewModel)

实现低耦合


http://www.ppmy.cn/server/134097.html

相关文章

Django学习-f对象和

F对象&#xff1a; Q对象&#xff1a;

HW支持-定时扫描局域网内所有设备MAC不在白名单则邮件提醒

需求背景 护网行动&#xff0c;是公安部组织的安全攻防演练活动。 曾经有被新安装的校园卡刷卡机黑到内网的经历&#xff0c;所以尽可能在护网期间能关就关&#xff0c;不新增设备。发现异常接入内网的设备即时进行提醒和处理。 实现步骤 MAC地址白名单放在一个txt文件中&…

搭建自己的Docker(容器)镜像加速器

容器镜像加速服务器 本Github项目可快速部署容器镜像加速服务器。 由于配置格式及docker客户端配置限制&#xff0c; 本项目仅适用于使用containerd runtime的容器镜像加速。 本项目两个分支&#xff1a; main: 使用nginx作为反向代理traefik: 使用traefik进行流量路由 前置…

索引选择的艺术:MySQL为何青睐B+树?

索引选择的艺术&#xff1a;MySQL为何青睐B树&#xff1f; 案例数据&#xff1a;表a有如下两个字段&#xff0c;且两个字段数据如下 字段a(主键)1234567字段b1293118366969 接下来将以常见的几种数据结构来看看进行比对&#xff0c;为什么MySQL最终使用了BTree&#xff08;B…

知识图谱推理(论文复现)

本文所涉及所有资源均在 传知代码平台 可获取。 目录 1. 论文概述 2. 论文方法 3. 实验部分

Elasticsearch 在linux部署 及 Docker 集群部署详解案例示范

1. 在 CentOS 上安装和配置 Elasticsearch 在 CentOS 系统下&#xff0c;安装 Elasticsearch 主要分为以下步骤&#xff1a; 1.1 准备工作 在开始安装之前&#xff0c;确保你的系统满足以下基本条件&#xff1a; CentOS 版本要求&#xff1a;推荐使用 CentOS 7 及以上版本。…

【Vulnhub靶场】Kioptrix Level 5

目标 本地IP&#xff1a;192.168.118.128 目标IP&#xff1a;192.168.118.0/24 信息收集 nmap探测存活主机&#xff0c;扫全端口&#xff0c;扫服务 首先探测到目标ip为&#xff1a;192.168.118.136 nmap -sP 192.168.118.0/24nmap -p- 192.168.118.136nmap -sV -A 192.168.…

达梦数据库使用 Flyway

参考&#xff1a;db-migration: Flyway、Liquibase 扩展支持达梦&#xff08;DM&#xff09;数据库、南大通用&#xff08;GBase 8s&#xff09;数据库&#xff0c;并支持 Flowable 工作流。 需要注意的是&#xff0c;下面两个依赖的顺序不能颠倒&#xff0c;因为有冲突的类 …