ProjectTiny包是一个纯DOTS环境,可以在Unity里体验下完全用ECS来写代码的感觉。
学习了解了下如何在DOTS环境下动态创建一个Quad网格。在MonoBehavior里创建一个Quad只需要两三行代码,在ECS里创建一个最简单的Quad Mesh,代码都要多好几倍。开发环境用的是Ubuntu20.04,Unity2020.1.17,ProjectTiny0.32。
官方有个列子RuntimeGeometry3D可以根据鼠标路径来创建网格路线,不过里边代码比较多,看起来有点麻烦。通过创建一个最简单的Quad,更方便了解其中的原理。直接上代码:
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using Unity.Tiny.Rendering;
namespace Tiny3D {
public class CreateQuadMeshSystem : SystemBase
{
protected override void OnUpdate()
{
var quadEntity = EntityManager.CreateEntity();
// 添加储存顶点数据的Buffer
EntityManager.AddBuffer<DynamicLitVertex>(quadEntity);
// 添加储存顶点下标的Buffer
EntityManager.AddBuffer<DynamicIndex>(quadEntity);
var vertex = EntityManager.GetBuffer<DynamicLitVertex>(quadEntity); // 不能直接用AddBuffer接口返回的,要再GetBuffer一下,不然运行会报错
var index = EntityManager.GetBuffer<DynamicIndex>(quadEntity);
vertex.Capacity = 4;
vertex.ResizeUninitialized(4);
index.Capacity = 6;
index.ResizeUninitialized(6);
DynamicMeshData dmd = new DynamicMeshData{
Dirty = true,
IndexCapacity = index.Capacity,
VertexCapacity = vertex.Capacity,
NumIndices = index.Length,
NumVertices = vertex.Length,
UseDynamicGPUBuffer = true
};
MeshBounds mb;
MeshHelper.FillPlaneMeshLit(vertex.AsNativeArray().Reinterpret<DynamicLitVertex, LitVertex>(),
index.AsNativeArray().Reinterpret<DynamicIndex, ushort>(), new float3(-0.5f, -0.5f, 0), new float3(1, 0, 0), new float3(0, 1, 0),
out mb.Bounds);
// color-定为红色
MeshHelper.SetAlbedoColor(vertex.AsNativeArray().Reinterpret<DynamicLitVertex, LitVertex>(), new float4(1, 0, 0, 1));
// normal-由于创建的是LitMesh,不计算好法线的话,light-color就计算不正确,Quad就渲染成黑漆漆的
MeshHelper.ComputeNormals(vertex.AsNativeArray().Reinterpret<DynamicLitVertex, LitVertex>(), index.AsNativeArray().Reinterpret<DynamicIndex, ushort>());
// tangent-计算光照还要用到切线
MeshHelper.ComputeTangentAndBinormal(vertex.AsNativeArray().Reinterpret<DynamicLitVertex, LitVertex>(), index.AsNativeArray().Reinterpret<DynamicIndex, ushort>());
// 添加各种渲染LitMesh必须用到的组件
EntityManager.AddComponentData<DynamicMeshData>(quadEntity, dmd); // 动态网格数据,这种可以在运行时改顶点数据的。还有一种是固定了顶点数据的,无法在运行时改数据!
EntityManager.AddComponentData<MeshRenderer>(quadEntity, new MeshRenderer{ // 网格渲染组件
mesh = quadEntity,
material = CreateLitMaterial(),
startIndex = 0,
indexCount = dmd.NumIndices
});
EntityManager.AddComponentData(quadEntity, new LitMeshRenderer()); // 这个组件用来表明该Mesh是支持光照的网格。还有一种是Mesh是Unlit的,没有光照
EntityManager.AddComponentData(quadEntity, mb); // Mesh的包围盒
EntityManager.AddComponentData<Translation>(quadEntity, new Translation { Value = float3.zero });
EntityManager.AddComponentData<Rotation>(quadEntity, new Rotation { Value = quaternion.identity });
EntityManager.AddComponentData<LocalToWorld>(quadEntity, new LocalToWorld { Value = float4x4.Translate(float3.zero) });
// 添加下面这个组件可以使Quad绕x轴旋转,按空格键可以停止/恢复旋转
EntityManager.AddComponentData(quadEntity, new DemoSpinner { spin = math.normalize(new quaternion(new float4(1, 0, 0, 1))) });
// 销毁该System,避免重复创建。可以用EntityCommadBuffer在OnCreate中创建Entity,但是那样有其他限制
World.GetExistingSystem<SimulationSystemGroup>().RemoveSystemFromUpdateList(this);
World.DestroySystem(this);
}
Entity CreateLitMaterial() {
var materialEntity = EntityManager.CreateEntity();
EntityManager.AddComponentData(materialEntity, new LitMaterial
{
texAlbedoOpacity = Entity.Null,
texMetal = Entity.Null,
texNormal = Entity.Null,
texEmissive = Entity.Null,
constEmissive = new float3(0),
constOpacity = 1.0f,
constAlbedo = new float3(1),
constMetal = 0.0f,
constSmoothness = .18f,
normalMapZScale = 1.0f,
twoSided = false,
transparent = false,
scale = new float2(1, 1),
offset = new float2(0, 0)
});
return materialEntity;
}
}
}
拷贝上面的代码,丢到官方那个Tiny3D工程目录里,删掉多余的代码和场景里的模型,设置好相机的位置,打包Web并运行就可以看到一个红色方块在旋转了。当然也可以打包Android。运行Web时注意要设置浏览器的跨域支持,不然无法加载本地文件。