【推荐100个unity插件之36】Unity6使用DOTS基础篇——Entities(非常适合做一些弹幕射击游戏)

文章目录

  • 前言
  • DOTS 核心组成
  • DOTS 解决传统问题的痛点
    • 1、优化内存布局:
    • 2、减少垃圾回收和内存管理开销:
    • 3、提高并行计算能力:
    • 4、高效的系统和组件设计:
    • 5、易于扩展和优化:
  • 安装
  • 文档
  • 在编辑器下构建 ECS World
  • 查看Entity的属性
  • 最简单的ecs程序
    • 传统方式
    • ecs方式
      • 1. 创建一个组件(Component)
      • 2. 创建一个系统(System)
      • 3. 触发打印
      • 4. 新建空物体挂载脚本
  • 旋转组件
  • 优化
  • 多线程并行执行
  • 使用 Burst 编译器优化性能
  • 屏蔽系统间的干扰
  • 调试窗口
  • 后续
  • 完结

前言

Unity DOTS(Data-Oriented Technology Stack)是 Unity 推出的数据驱动型技术栈,旨在帮助开发者构建高性能的游戏和应用,特别是在需要处理大量实体(Entities)时。DOTS 通过提供不同的工具和框架(如 ECS、Jobs 和 Burst),能够极大提升性能,尤其是在大规模的并行计算和高帧率下的优化。

在传统的面向对象(OOP)编程方式中,游戏开发者通常依赖继承和类之间的关系来管理数据和行为,这会导致性能瓶颈,尤其是在面对大规模实体时。随着现代游戏中世界规模和交互数量的增加,传统方法的缺点变得尤为突出。

DOTS 的设计核心是“数据优先”,它通过优化内存布局和计算过程,能够显著提高性能,解决传统开发中的一些常见瓶颈,特别是在性能密集型的场景下(如大量物体的物理计算、AI 行为等)。
在这里插入图片描述

DOTS 核心组成

  • ECS(Entity Component System):以数据为中心,使用实体(Entities)、组件(Components)和系统(Systems)来组织和管理数据,旨在提升内存访问效率。
  • Jobs System:通过多线程并行化任务,能将大量的计算工作分配给多个处理器核心,从而提升性能。
  • Burst Compiler:通过静态分析代码并优化生成的机器码,能大幅度提升 CPU 计算性能。

DOTS 解决传统问题的痛点

1、优化内存布局:

DOTS 中的 ECS 通过将数据按组件组织,并优化内存布局,能够让数据按照连续块存储在内存中,从而大幅提高 CPU 缓存命中率,减少缓存未命中的问题。
这种数据布局优化使得大量实体的处理能更高效,尤其在涉及到大规模游戏世界或复杂场景时。

2、减少垃圾回收和内存管理开销:

DOTS 避免了传统对象实例化和销毁的开销,通过使用对象池(或“内存分配池”)来复用实体,减少 GC(垃圾回收)的频繁调用,从而降低性能损耗。

3、提高并行计算能力:

DOTS 通过 Jobs System 将任务分割并并行化,能够充分利用多核 CPU 的计算能力。开发者可以轻松编写多线程代码,而无需担心线程安全问题,系统会自动管理并行化。
Burst Compiler 进一步优化了代码的执行效率,确保多线程任务能够最大化地提高性能。

4、高效的系统和组件设计:

DOTS 的 ECS 模式简化了逻辑和数据的分离,让每个系统(System)仅处理数据(Components)中的某一部分。通过这种设计,开发者可以更方便地对数据进行分区、筛选和批处理,提高程序的执行效率。
这种“数据驱动”模型使得开发者可以更直观地分析和优化性能瓶颈。

5、易于扩展和优化:

DOTS 提供了一个高效的调试和分析工具链(如 Profiler),可以帮助开发者实时监控和调整性能瓶颈。与此同时,ECS 和 Jobs 使得开发者能够在代码级别进行细粒度的优化。

安装

我这里使用的是最新的unity6,unity6 Entities已经从预览版放出来了(貌似2022版本就不是预览版了),直接搜索安装即可.
如果你用的是其他,可能方法各有不同,需要自行去查找如何进行安装
(注意:推荐在URP或者hdrp中使用DOTS
在这里插入图片描述
安装Entities Graphics,实体的dots显示包
在这里插入图片描述

文档

https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/index.html

在编辑器下构建 ECS World

之前的流程是在 GameObject 挂上一个 Convert To Entity 的组件,就能转换成 Entity。不过新的流程修改了,这个组件被移除了,新的流程如下:

在 Hierarchy 窗口下右键,选择 New Subscene > Empty Scene,创建一个新的 SubScene。
在这里插入图片描述
现在只要在这个SubScene里面的东西,都会自动转换成实体(Entity)
在这里插入图片描述

查看Entity的属性

之前这一块是在 EntityDebugger 里面的,相信大家已经发现了,在Entities 1.0 中,已经没有这个 EntityDebugger 了。

查看 Entity 的方法如下:

直接 Play,然后在 Hierarchy 中选择对应的 Cube(已经转成实体)

可以通过右上角查看实体情况
在这里插入图片描述
里面你就能看到Entity的属性了
在这里插入图片描述
可以看到这里加了很多Data,除了LTW相关的就是渲染相关的,东西还是非常多的。不过这里我们都完全不需要管它,只要知道这里能看各个Data的数据就OK了。

最简单的ecs程序

这里我我带大家实现一个简单的打印效果

传统方式

传统方式应该就不用我过多介绍了,相信大家用的已经非常多了

public class test : MonoBehaviour
{void Update(){Debug.Log("传统方式打印");}
}

ecs方式

要使用 Unity DOTS 实现类似于传统方式的 Update() 打印功能,首先需要了解如何在 DOTS 中使用 ECS(Entity Component System)。DOTS的核心思想是将数据(组件)与行为(系统)分离,使用不同的方式来处理每帧更新的逻辑。

1. 创建一个组件(Component)

定义一个结构体,表示一个组件,用于存储打印信息

using Unity.Collections;
using Unity.Entities;public struct PrintMessageComponent : IComponentData {// 固定长度字符串,用于存储打印的消息public FixedString128Bytes printData;
}

2. 创建一个系统(System)

定义一个系统,处理所有包含 PrintMessageComponent 的实体

using Unity.Entities;
using Unity.Transforms;
using UnityEngine;partial class PrintMessageSystem : SystemBase {// 系统每帧更新时会调用此方法protected override void OnUpdate(){// 查询所有包含 LocalTransform 和 PrintMessageComponent 的实体foreach ((RefRW<LocalTransform> localTransform, RefRO<PrintMessageComponent> printMessageComponent)in SystemAPI.Query<RefRW<LocalTransform>, RefRO<PrintMessageComponent>>()){// 打印每个实体中的 printMessageComponent 内容Debug.Log(printMessageComponent.ValueRO.printData);}}
}

3. 触发打印

定义一个 MonoBehaviour 脚本,用于在场景中生成实体

using Unity.Entities;
using UnityEngine;public class EntitySpawner : MonoBehaviour
{//用于在 Inspector 面板中输入的字符串值public string value;// 内部类 Baker,用于将 MonoBehaviour 转换为实体private class Baker : Baker<EntitySpawner>{// Bake 方法会在编辑器模式下调用,将 MonoBehaviour 组件数据转化为实体组件数据public override void Bake(EntitySpawner authoring){// 创建一个动态使用的实体Entity entity = GetEntity(TransformUsageFlags.Dynamic);// 给实体添加 PrintMessageComponent 组件,并设置其 printData 为 authoring 中的值AddComponent(entity, new PrintMessageComponent{printData = authoring.value,});}}
}

4. 新建空物体挂载脚本

在这里插入图片描述

效果
在这里插入图片描述

旋转组件

定义一个旋转速度组件,用于存储每个实体的旋转速度

using Unity.Entities;// 定义一个实现 IComponentData 接口的结构体,用于表示旋转速度
public struct RotateSpeed : IComponentData {// 旋转速度的数值public float value;
}

定义一个旋转速度的 Authoring 类,用于在 Unity 编辑器中设置旋转速度的值

public class RotateSpeedAuthoring : MonoBehaviour {// 用于设置旋转速度的公共字段public float value;// Baker 类用于将 MonoBehaviour 组件转换为 ECS 的组件数据private class Baker : Baker<RotateSpeedAuthoring>{// 该方法在实体生成时被调用,负责将 MonoBehaviour 上的属性数据转化为 ECS 组件public override void Bake(RotateSpeedAuthoring authoring){// 获取当前的实体,并设置为动态使用(TransformUsageFlags.Dynamic)Entity entity = GetEntity(TransformUsageFlags.Dynamic);// 为实体添加 RotateSpeed 组件,并将作者的旋转速度值传递给它AddComponent(entity, new RotateSpeed{value = authoring.value, // 设置旋转速度});}}
}

定义一个旋转立方体系统,负责处理与旋转速度相关的逻辑

public partial struct RotatingCubeSystem : ISystem
{// 系统创建时调用的方法public void OnCreate(ref SystemState state){// 在系统创建时要求必须有 RotateSpeed 组件才能进行更新state.RequireForUpdate<PrintMessageComponent>();}// 系统更新时调用的方法void OnUpdate(ref SystemState state){// 查询所有包含 LocalTransform 和 RotateSpeed 组件的实体foreach ((RefRW<LocalTransform> localTransform, RefRO<RotateSpeed> rotateSpeed)in SystemAPI.Query<RefRW<LocalTransform>, RefRO<RotateSpeed>>()){// 通过当前的旋转速度(rotateSpeed)来更新实体的旋转// 使用 RotateY 方法按照 Y 轴旋转,旋转角度由旋转速度与 DeltaTime 计算得出localTransform.ValueRW = localTransform.ValueRO.RotateY(rotateSpeed.ValueRO.value * SystemAPI.Time.DeltaTime);}}
}

挂载脚本
在这里插入图片描述

效果
在这里插入图片描述

优化

新增OnCreate,当系统第一次被创建时,OnCreate 方法会被调用。在此方法中,调用 state.RequireForUpdate<RotateSpeed>() 来确保系统只会在实体中包含 RotateSpeed 组件时进行更新。这保证了只有需要旋转的实体会触发系统的更新。

using Unity.Transforms;
using Unity.Entities;// 定义一个旋转立方体系统,负责处理与旋转速度相关的逻辑
public partial struct RotatingCubeSystem : ISystem
{// 系统创建时调用的方法public void OnCreate(ref SystemState state){// 在系统创建时要求必须有 RotateSpeed 组件才能进行更新state.RequireForUpdate<RotateSpeed>();}// 系统更新时调用的方法void OnUpdate(ref SystemState state){// 查询所有包含 LocalTransform 和 RotateSpeed 组件的实体foreach ((RefRW<LocalTransform> localTransform, RefRO<RotateSpeed> rotateSpeed)in SystemAPI.Query<RefRW<LocalTransform>, RefRO<RotateSpeed>>()){// 通过当前的旋转速度(rotateSpeed)来更新实体的旋转// 使用 RotateY 方法按照 Y 轴旋转,旋转角度由旋转速度与 DeltaTime 计算得出localTransform.ValueRW = localTransform.ValueRO.RotateY(rotateSpeed.ValueRO.value * SystemAPI.Time.DeltaTime);}}
}

多线程并行执行

系统仍然在主线程运行
在这里插入图片描述
修改代码,使用rotatingCubeJob.ScheduleParallel();系统在多线程并行执行

public partial struct RotatingCubeSystem : ISystem
{// 系统创建时调用的方法public void OnCreate(ref SystemState state){// 在系统创建时要求必须有 RotateSpeed 组件才能进行更新state.RequireForUpdate<RotateSpeed>();}// 系统更新时调用的方法void OnUpdate(ref SystemState state){// 创建旋转立方体的工作作业,并传递时间增量信息RotatingCubeJob rotatingCubeJob = new RotatingCubeJob{deltaTime = SystemAPI.Time.DeltaTime // 获取每帧的时间增量};// 调度工作作业// rotatingCubeJob.Schedule();// state.Dependency = rotatingCubeJob.Schedule(state.Dependency);//在多个线程上并行rotatingCubeJob.ScheduleParallel();}// 定义旋转立方体的作业,IJobEntity 用于操作实体public partial struct RotatingCubeJob : IJobEntity{public float deltaTime; // 存储时间增量,用于旋转计算// 执行作业时的具体操作:旋转实体public void Execute(ref LocalTransform localTransform, in RotateSpeed rotateSpeed){// 旋转的倍率系数float power = 1f;// 根据旋转速度和时间增量来旋转实体localTransform = localTransform.RotateY(rotateSpeed.value * deltaTime * power);}}
}

在这里插入图片描述

使用 Burst 编译器优化性能

加入关键字[BurstCompile]即可,使用 Burst 编译器优化性能
在这里插入图片描述
记得查看Burst编译器是否勾选使用
在这里插入图片描述

屏蔽系统间的干扰

前面我们实现了一个打印和旋转系统,虽然我们的打印功能已经被隐藏了
在这里插入图片描述
但是如果通过在打印系统OnUpdate里打印日志
在这里插入图片描述
你会发现系统其实仍然在执行
在这里插入图片描述
我们可以加上[DisableAutoCreation]特性,屏蔽该系统的干扰,这样这个系统就不会再运行了
在这里插入图片描述

调试窗口

在这里插入图片描述

  • Hierarchy : 显示当前场景中的实体信息

  • Components :显示所有 ComponentData 的结构体信息。

  • Systems:显示当前运行的所有 System 信息,能看到其使用了哪些实体。

  • Archetypes:显示原型信息。

  • Journaling:日志记录,可以显示用了哪些方法,有哪些实体、ComponentData之类,应该是可以用来分析性能,但我还没有仔细研究。

后续

DOTS基础篇就先写到这里了,如果你感兴趣也和传统方式进行性能比较,这里我就不写了。

后续我看有时间再考虑写写一些进阶知识或者dots项目实战内容。

完结

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

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

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

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

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

相关文章

AI一键生成原创圣诞印花图案

一、引言 随着科技的飞速发展&#xff0c;AI 已经深入到我们生活和工作的各个角落&#xff0c;为创意设计领域带来了前所未有的变革。在圣诞即将来临之际&#xff0c;想要设计独特的圣诞印花图案却又担心缺乏灵感或专业技能&#xff1f;别担心&#xff0c;千鹿 AI 为我们提供了…

第一部分 —— 密文类型

文章目录 1. Abstract1.1 Some Conceptions 2. TFHE Ciphertexts3. GLWE3.1 Trivial GLWE ciphertexts3.2 LWE 和 RLWE3.3 Public key encryption 4. GLev4.1 Lev and RLev 5. GGSW5.1 GSW and RGSW 1. Abstract TFHE 指的是 全同态加密策略。意思是&#xff0c;允许对密文进行…

进制转换详解

进制转换的基本概念 进制转换是将一个数从一种基数&#xff08;进制&#xff09;转换为另一种基数的过程。例如&#xff0c;将十进制数转换为二进制、八进制或十六进制。 转换过程 以十进制数转换为其他进制为例&#xff0c;转换的基本步骤如下&#xff1a; • 除以目标进制…

构建数字影像生态群,致力推动数字经济发展

在当今数字化浪潮汹涌澎湃的时代&#xff0c;数字经济逐渐成为全球经济增长新的核心驱动力。国际数字影像产业园作为数字影像领域的创新高地&#xff0c;正以其独特的优势和不懈的努力&#xff0c;为推动数字经济的蓬勃发展贡献着卓越力量。 国际数字影像产业园凭借其优越的地理…

性能测试工具1:perf

1.介绍 perf是linxu下的一款性能分析工具。Linux的性能计数器是一个新的基于内核的子系统&#xff0c;它为所有性能分析提供了一个框架。它包括硬件级别&#xff08;CPU/PMU、性能监控单元&#xff09;功能和软件(软件计数器、跟踪点)功能。 通过perf,应用程序可以利用PMU…

学籍照片电子版手机拍照采集且批量自动命名的方法

学籍照片作为学生档案的重要组成部分&#xff0c;其电子版的采集和管理显得尤为重要&#xff0c;目前主要通过“全国学籍信息管理系统”进行管理。传统的拍照和命名方式不仅耗时耗力&#xff0c;而且容易出现错误。为了提高效率和准确性&#xff0c;下面介绍如何由教师自己使用…

在wsl2中安装archlinux

在之前的博客中&#xff0c;我介绍了如何在虚拟机或者真实机上安装archlinux并且进行一定的配置&#xff0c;但是实际上Linux不管怎么配置在日常使用中都没有Windows简单便利&#xff0c;在开发有关Linux的程序时过去用虚拟机或者直接在Windows上使用ssh在远程服务器上进行开发…

蓝桥杯真题1259奇怪的捐赠(python版)

解题思路:将100万转换为7进制数,数位之和就是分成的份数 num 100_0000 sum 0 while num > 0:remainder num % 7num num // 7sum remainder print(sum)代码来自题目题解 num对7进行取余&#xff0c;取值范围理应是[0,1,2,3,4,5,6] 但是对于题目给定的捐赠金额实际上并不…

学习日志020---qt信号与槽

作业 import sysfrom PySide6.QtWidgets import QApplication, QWidget,QPushButton,QLineEditfrom Form import Ui_Form from second import Ui_second from PySide6.QtCore import Qtclass MyWidget(QWidget,Ui_Form):def __init__(self):super().__init__()self.setupUi(se…

python学习笔记15 python中的类

上一篇我们介绍了python中的库 &#xff0c;学习了一些常见的内置库。详细内容可点击–>python学习笔记14 python中的库&#xff0c;常见的内置库&#xff08;random、hashlib、json、时间、os&#xff09; 这一篇我们来看一下python中的类 创建一个类 class 类的名称():de…

Redis面试专题-持久化

前言 开始Redis面试知识的复习和资料的收集&#xff08;收集和参考了网上的优质文章&#xff09;&#xff0c;本篇文章会不断更新&#xff0c;本系列文章主要分为两部分&#xff0c;一部分是该专题所涉及的相关基础知识&#xff0c;另一部分是面试题与思考题&#xff0c;大部分…

Altium Designer基础知识2:交互式差分布线

Altium Designer基础知识2&#xff1a;交互式差分布线 一、本文内容与前置知识点1. 本文内容2. 所用软件 二、差分式布线介绍1. 介绍2. 使用场景 三、布线流程1. 创建差分式布线对2. 布线 一、本文内容与前置知识点 1. 本文内容 Altium Designer的基础知识&#xff0c;差分布…

注意力机制的输入

注意力机制的输入 flyfish 注意力机制用于确定序列中每个组成部分相对于其他部分的相对重要性。 绘图源码 import matplotlib.pyplot as plt from matplotlib.patches import FancyArrowPatchplt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] Fa…

Anaconda创建虚拟环境+CUDA、cuDNN一同安装

文章目录 前言一、CUDA的作用二、下载CUDA的步骤2.1 查看电脑NVIDIA适合的CUDA版本&#xff08; 两种方法&#xff09;1&#xff09;打开NVIDIA控制面板&#xff0c;目前我的CUDA版本是12.12&#xff09;使用命令行查看&#xff0c;使用命令&#xff1a;nvidia-smi。 2.2 根据p…

数学建模之熵权法

熵权法 概述 **熵权法(Entropy Weight Method,EWM)**是一种客观赋权的方法&#xff0c;原理&#xff1a;指标的变异程度越小&#xff0c;所包含的信息量也越小&#xff0c;其对应的权值应该越低&#xff08;例如&#xff0c;如果对于所有样本而言&#xff0c;某项指标的值都相…

Python学习第十六天--迭代器和生成器

一、可迭代对象 六大标准数据类型&#xff1a;字符串&#xff0c;列表&#xff0c;元组&#xff0c;字典&#xff0c;集合&#xff0c;数值类型 可迭代对象&#xff1a;字符串&#xff0c;列表&#xff0c;元组&#xff0c;字典&#xff0c;集合。即&#xff1a;通过for...in…

【JavaScript】选项卡切换

选项卡切换 选项卡切换是一种常见的网页设计模式&#xff0c;用于在一个页面内显示和切换不同内容区域&#xff0c;而无需加载页面。用户可以通过点击选项卡切换显示不同的内容&#xff0c;而隐藏其他内容。 多选项显示&#xff1a;页面顶部、侧边或其他地方通常有多个选项卡…

【Spring】Spring 整合 MyBatis

在实际项目开发中&#xff0c;将 Spring 和 MyBatis 进行整合可以提高开发效率、简化配置、增强事务管理和可维护性&#xff0c;同时利用 Spring 的强大功能能提升系统的稳定性。这里从独立使用 MyBatis 开始&#xff0c;逐步实现与 Spring 框架的整合。 MyBatis 独立开发 现…

JavaWeb学习(1)(同步或异步请求、依赖jQuery简单实现Ajax技术)

目录 一、Web的基本流程与页面局部刷新。 &#xff08;1&#xff09;web开发时基本流程。 &#xff08;2&#xff09;页面的"全局刷新"与"局部刷新"。 二、Ajax技术。 &#xff08;1&#xff09;基本介绍。 &#xff08;2&#xff09;基本特点。 1、与服务…

win11 vs2022 python3.9环境下运行jupyterlab

jupyter官网及简介&#xff1a;https://jupyter.org/ Jupyter 集合“浏览器 编程 文档 绘图 多媒体 发布”众多功能与一身&#xff0c;适合探究式学习。 JupyterLab是最新的基于网络的笔记本、代码和数据的互动开发环境。 Jupyter Notebook是JupyterLab的上一代版本。 由…