Faster-Godot4 dotnet 之ECS框架第一章:ECS框架安装,并体验ECS
打开Godot4引擎之后在任意的目录下新建一个C#脚本此时在项目根目录下会自动生成一个.sln文件可以打开文件资源管理器进入到项目根目录然后双击打开.sln会自动打开VisualStudio2026它能自动识别为解决方案项目。打开cmd到项目根目录并且确认已经安装donet-sdk8.0。安装ECS框架和扩展# 安装核心Arch ECS包 dotnet add package Arch # 可选配套包推荐一起装#Arch.System 系统基类dotnet add package Arch.System#Arch.System.SourceGenerator 源代码生成器减少反射大幅提速dotnet add package Arch.System.SourceGenerator#Arch-Events 事件扩展dotnet add package Arch-Events然后要引入Arch包到项目中ProjectSdkGodot.NET.Sdk/4.7.0PropertyGroupTargetFrameworknet8.0/TargetFrameworkEnableDynamicLoadingtrue/EnableDynamicLoadingPublishReadyToRuntrue/PublishReadyToRunSelfContainedtrue/SelfContainedAllowUnsafeBlocksTrue/AllowUnsafeBlocks/PropertyGroupItemGroupPackageReferenceIncludeArchVersion2.1.0/PackageReferenceIncludeArch-EventsVersion2.1.0/PackageReferenceIncludeArch.SystemVersion1.1.0/PackageReferenceIncludeArch.System.SourceGeneratorVersion2.1.0/TrimmerRootAssemblyIncludeGodotSharp/TrimmerRootAssemblyInclude$(TargetName)//ItemGroupItemGroupCondition$(Configuration)ReleaseTrimmerRootAssemblyInclude$(AssemblyName)RootModeAll/!-- 保护Arch程序集防止裁剪Arch内部泛型、组件元数据 --TrimmerRootAssemblyIncludeArchRootModeAll//ItemGroup/Project可以在场景中添加一个太阳、天空环境5.然后必须创建一个相机才可以看得见5. 调整相机角度到合适的位置6. 编写一个ECS脚本代码创建25万个物体给物体绕着任意旋转轴旋转新建一个CreateMultiEcsEntity.cs脚本usingArch;usingArch.Core;usingGodot;usingSystem;usingSystem.Diagnostics;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Threading.Tasks;// ECS data is rendered by one MultiMesh; no per-entity Node3D is created.publicpartialclassCreateMultiEcsEntity:Node3D{[Export]publicMeshTorusPrefab{get;set;}[Export(PropertyHint.Range,1,200000,1)]publicintGridSize{get;set;}500;[Export]publicfloatSpacing{get;set;}2.2f;privateWorld_ecsWorld;privateMultiMesh_multiMesh;privateEntityState[]_statesArray.EmptyEntityState();// Filled in parallel; submitted through Godots main-thread-only MultiMesh API.privateTransform3D[]_transformsArray.EmptyTransform3D();publicstructRotateComp{publicintIndex;publicVector3Axis;publicfloatSpeed;}ParallelOptionsparallelOpt;[StructLayout(LayoutKind.Sequential,Pack1)]privateunsafestructEntityState{publicVector3Position;publicVector3Axis;publicfloatSpeed;publicQuaternionRotation;//public fixed byte Padding[20];}publicoverridevoid_Ready(){varsizeUnsafeUnsafe.SizeOfEntityState();parallelOptnewParallelOptions{MaxDegreeOfParallelismSystem.Environment.ProcessorCount};if(TorusPrefabnull){GD.PrintErr(TorusPrefab must reference a Mesh.);return;}_ecsWorldWorld.Create();_multiMeshnewMultiMesh{TransformFormatMultiMesh.TransformFormatEnum.Transform3D,MeshTorusPrefab};AddChild(newMultiMeshInstance3D{Multimesh_multiMesh});SpawnTorusEcsEntities(Math.Max(1,GridSize));}publicvoidSpawnTorusEcsEntities(intgridSize){intcountchecked(gridSize*gridSize);_statesnewEntityState[count];_transformsnewTransform3D[count];_multiMesh.InstanceCountcount;for(intx0;xgridSize;x){for(intz0;zgridSize;z){intindexx*gridSizez;Vector3axisnewVector3((float)GD.RandRange(1.0,2.0),(float)GD.RandRange(2.0,3.0),(float)GD.RandRange(1.0,2.0)).Normalized();floatspeed(float)GD.RandRange(0.4,1.5);Vector3positionnewVector3((x-(gridSize-1)/2.0f)*Spacing,0,(z-(gridSize-1)/2.0f)*Spacing);_states[index]newEntityState{Positionposition,Axisaxis,Speedspeed,RotationQuaternion.Identity};_transforms[index]newTransform3D(Basis.Identity,position);_ecsWorld.Create(newRotateComp{Indexindex,Axisaxis,Speedspeed});}}SubmitTransforms();}publicoverridevoid_Process(doubledelta){if(_states.Length0)return;ulongt1Time.GetTicksMsec();floatdt(float)delta;// CPU-side ECS rotation and Transform3D construction run in parallel.Parallel.For(0,_states.Length,parallelOpt,i{refEntityStatestateref_states[i];floatangleDeltastate.Speed*dt;QuaternionrotStepnewQuaternion(state.Axis,angleDelta);state.RotationrotStep*state.Rotation;// 构造变换存入transforms数组_transforms[i]newTransform3D(newBasis(state.Rotation),state.Position);});// MultiMesh is a Godot resource and must only be modified from the main thread.SubmitTransforms();ulongt2Time.GetTicksMsec();GD.Print(t2-t1);}privatevoidSubmitTransforms(){for(inti0;i_transforms.Length;i){_multiMesh.SetInstanceTransform(i,_transforms[i]);}}publicoverridevoid_ExitTree(){_ecsWorld?.Dispose();}}然后把脚本拖拽到根节点8. 要选择一个网格最后构建项目运行项目25万个物体同时旋转耗时9 ~ 10毫秒左右如果打包成Release包可以进一步加快速度到6 ~ 7毫秒左右的耗时。使用传统的节点对象创建CreateMultiMonoObj.cs脚本usingGodot;usingSystem;publicpartialclassCreateMultiMonoObj:Node3D{privateconstintGridSize500;privateconstintInstanceCountGridSize*GridSize;privateconstintTransformStride12;[Export]publicMeshDonutMesh{get;set;}null;[Export]publicfloatSpacing2.5f;[Export]publicfloatRotateSpeedMin5.0f;[Export]publicfloatRotateSpeedMax8.0f;privateMultiMesh_multiMesh;privateVector3[]_rotationAxesArray.EmptyVector3();privatefloat[]_rotationSpeedsArray.Emptyfloat();privatefloat[]_rotationAnglesArray.Emptyfloat();privatefloat[]_transformBufferArray.Emptyfloat();publicoverridevoid_Ready(){if(DonutMeshnull){GD.PushWarning(请在检视面板给 DonutMesh 赋值 Mesh 资源);SetProcess(false);return;}_rotationAxesnewVector3[InstanceCount];_rotationSpeedsnewfloat[InstanceCount];_rotationAnglesnewfloat[InstanceCount];_transformBuffernewfloat[InstanceCount*TransformStride];// 格式必须在设置 InstanceCount分配实例缓冲区之前指定。_multiMeshnewMultiMesh{TransformFormatMultiMesh.TransformFormatEnum.Transform3D,MeshDonutMesh,InstanceCountInstanceCount};// 预设能包围所有旋转姿态的边界避免逐帧重新计算 25 万个实例的 AABB。AabbmeshBoundsDonutMesh.GetAabb();floatradiusmeshBounds.Position.Abs().Max(meshBounds.End.Abs()).Length();floatgridEnd(GridSize-1)*Spacing-250.0f;floatminMathf.Min(-250.0f,gridEnd)-radius;floatmaxMathf.Max(-250.0f,gridEnd)radius;_multiMesh.CustomAabbnewAabb(newVector3(min,-radius,min),newVector3(max-min,radius*2.0f,max-min));for(intx0;xGridSize;x){for(intz0;zGridSize;z){intindexx*GridSizez;intoffsetindex*TransformStride;// 每个 3D 变换占 12 个 float按 3 行旋转矩阵 平移存储。WriteBasis(offset,Basis.Identity);_transformBuffer[offset3]x*Spacing-250.0f;_transformBuffer[offset7]0.0f;_transformBuffer[offset11]z*Spacing-250.0f;// 随机三维单位旋转轴Vector3randomAxisnewVector3(GD.Randf()*2.0f-1.0f,GD.Randf()*2.0f-1.0f,GD.Randf()*2.0f-1.0f);_rotationAxes[index]randomAxis.IsZeroApprox()?Vector3.Up:randomAxis.Normalized();_rotationSpeeds[index]Mathf.Lerp(RotateSpeedMin,RotateSpeedMax,GD.Randf());}}_multiMesh.Buffer_transformBuffer;AddChild(newMultiMeshInstance3D{NameDonutMultiMesh,Multimesh_multiMesh});GD.Print($生成 MultiMesh 实例数量{InstanceCount}使用 1 个 MultiMeshInstance3D);}publicoverridevoid_Process(doubledelta){if(_multiMeshnull)return;ulongt1Time.GetTicksMsec();floatdt(float)delta;for(inti0;iInstanceCount;i){// 从累计角度重建旋转避免持续乘矩阵产生缩放/正交误差。_rotationAngles[i]Mathf.PosMod(_rotationAngles[i]_rotationSpeeds[i]*dt,Mathf.Tau);WriteBasis(i*TransformStride,newBasis(_rotationAxes[i],_rotationAngles[i]));}// 每帧一次批量提交不再逐个调用 Rotate 或 SetInstanceTransform。_multiMesh.Buffer_transformBuffer;ulongt2Time.GetTicksMsec();GD.Print(t2-t1);}privatevoidWriteBasis(intoffset,Basisbasis){// Basis.X/Y/Z 是列向量MultiMesh 缓冲区需要按行写入。_transformBuffer[offset]basis.X.X;_transformBuffer[offset1]basis.Y.X;_transformBuffer[offset2]basis.Z.X;_transformBuffer[offset4]basis.X.Y;_transformBuffer[offset5]basis.Y.Y;_transformBuffer[offset6]basis.Z.Y;_transformBuffer[offset8]basis.X.Z;_transformBuffer[offset9]basis.Y.Z;_transformBuffer[offset10]basis.Z.Z;}}经过运行发现每一帧率耗时高达100毫秒左右。从视觉上对比会发现差距非常大。帧率对比采用Mono实体同时旋转25万个Mesh网格FPS低到可怜的9帧/秒采用ECS框架同样的网格数量同时旋转高达79帧/秒ECS框架适合超多的实体的场景。

相关新闻

VLAN+链路聚合+静态路由:企业级网络综合组网实战指南

VLAN+链路聚合+静态路由:企业级网络综合组网实战指南

1. 从一个真实组网需求说起:为什么要把VLAN、链路聚合和静态路由放在一起用很多刚接触企业级网络的朋友会有个疑问:交换机上划VLAN我懂,链路聚合我也懂,路由器上写静态路由我也懂,但把这三样东西凑到一张拓扑里&#x…

2026/11/8 0:18:52 阅读更多 →
核心网络协议梳理与排障实战:HTTP、TCP/IP、DNS、SSH、FTP

核心网络协议梳理与排障实战:HTTP、TCP/IP、DNS、SSH、FTP

网络协议这东西,平时不接触的人觉得它高高在上,接触了又觉得知识点碎得跟散沙一样。但其实,不管你是做开发的、搞运维的,还是偶尔需要自己折腾一下服务器、翻翻路由器配置的“兴趣型工程师”,只要能把HTTP、IP、SSH、F…

2026/11/6 18:23:46 阅读更多 →
artcraft:轻量级创意工具集的设计与实现

artcraft:轻量级创意工具集的设计与实现

1. 项目缘起与整体设计思路1.1 为什么会有 artcraft 这个想法做创意工具这些年,我最大的感受就是:大部分所谓的“创作辅助工具”,其实都在帮倒忙。你要么被一堆参数面板淹没,要么被强制绑定某个平台、某种格式,最后做出…

2026/11/7 22:52:49 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/8 1:19:03 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/7 12:08:40 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/8 1:35:05 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/8 1:38:41 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/7 12:08:40 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/8 1:23:14 阅读更多 →

日新闻

周新闻

月新闻