大家好,我是阿赵。继续学习Unity官方的DOTS例子。
Tornado这个例子是Entites101的最后一个例子,是一个龙卷风特效的例子。
一、 Config

打开SubScene看看,里面的Config物体上挂着脚本,记录了这个例子需要用到的各种参数。
现在看这些参数会感觉莫名其妙,到了下面具体的功能再回头看会清晰一点。
二、 建筑生成部分
这里用到了2个System,分别是BuildingSpawnSystem和BuildingRenderSystem。
这里用到的组件是Bar:
public struct Bar : IComponentData
{
public int pointA;
public int pointB;
public float length;
}
这个组件可以理解成是组成建筑的一根积木,比如:

在生成这些建筑的时候,会分成2种,一种是有高度的建筑物,另外一种是散落在地上的建筑材料。它们在生成的时候记录的是点,比如里面有100根积木,每根积木有2个顶点,那么就有200个顶点(这里只是举例,建筑物其实是3个顶点前后连接生成3根积木),在生成的时候会把这些顶点全部存在Config的实体里面,然后Bar的pointA和pointB,记录的就是存在Config里面的顶点坐标列表的index。
在BuildingSpawnSystem里面,就是要生成这些Bar数据,首先是建筑数据:
for (int i = 0; i < 35; i++)
{
int height = random.NextInt(4, 12);
var pos = new float3(random.NextFloat(-45f, 45f), 0f, random.NextFloat(-45f, 45f));
float spacing = 2f;
var anchor = byte.MaxValue;
for (int j = 0; j < height; j++)
{
points.Add(new float3(pos.x + spacing, j * spacing, pos.z - spacing));
connectivity.Add(anchor);
points.Add(new float3(pos.x - spacing, j * spacing, pos.z - spacing));
connectivity.Add(anchor);
points.Add(new float3(pos.x, j * spacing, pos.z + spacing));
connectivity.Add(anchor);
anchor = 0;
}
}

这里随机生成了35栋层高随机4到12的建筑物。
然后是地上的细节:
for (int i = 0; i < 600; i++)
{
var posA = new float3(random.NextFloat(-55f, 55f), 0f, random.NextFloat(-55f, 55f));
var posB = posA;
posA.x += random.NextFloat(-.2f, -.1f);
posA.y += random.NextFloat(0f, 3f);
posA.z += random.NextFloat(.1f, .2f);
points.Add(posA);
connectivity.Add(0);
posB.x += random.NextFloat(.2f, .1f);
posB.y += random.NextFloat(0f, .2f);
posB.z += random.NextFloat(-.1f, -.2f);
points.Add(posB);
if (random.NextFloat() < .1f)
connectivity.Add(byte.MaxValue);
else
connectivity.Add(0);
}

两者加在一起,就形成了整个场景建筑的数据了:

把这些顶点组成Bar数据,存起来。
然后在BuildingRenderSystem里面,使用IJobEntity进行多线程批处理,根据Bar数据,把具体的模型显示出来
public partial struct PointRenderJob : IJobEntity
{
[ReadOnly] public NativeArray<float3> CurrentPoints;
public void Execute(ref LocalToWorld ltw, in Bar bar, in BarThickness thickness)
{
var a = CurrentPoints[bar.pointA];
var b = CurrentPoints[bar.pointB];
var d = math.distance(a, b);
var norm = (a - b) / d;
var t = (a + b) / 2;
var r = quaternion.LookRotationSafe(norm, norm.yzx);
var s = new float3(new float2(thickness.Value), d);
ltw.Value = float4x4.TRS(t, r, s);
}
}
三、 风暴生成部分
1、 TornadoSpawnSystem
这个System的作用是生成1000个粒子模型。并且随机位置:
public void OnUpdate(ref SystemState state)
{
var config = SystemAPI.GetSingleton<Config>();
var entities = state.EntityManager.Instantiate(config.ParticlePrefab, 1000, Allocator.Temp);
var random = Random.CreateFromIndex(1234);
foreach (var entity in entities)
{
var particle = SystemAPI.GetComponentRW<Particle>(entity);
var transform = SystemAPI.GetComponentRW<LocalTransform>(entity);
var color = SystemAPI.GetComponentRW<URPMaterialPropertyBaseColor>(entity);
transform.ValueRW.Position = new float3(random.NextFloat(-50f, 50f), random.NextFloat(0f, 50f),
random.NextFloat(-50f, 50f));
transform.ValueRW.Scale = random.NextFloat(.2f, .7f);
particle.ValueRW.radiusMult = random.NextFloat();
color.ValueRW.Value = new float4(new float3(random.NextFloat(.3f, .7f)), 1f);
}
state.Enabled = false;
}
如果单纯运行这个System,会看到这样的效果:

2、 TornadoSystem
这个System会通过IJobEntity多线程计算粒子被龙卷风吸引的效果。
先通过BuildingSystem.Position方法算出现在的时间点龙卷风应该所在的坐标,然后通过 BuildingSystem.TornadoSway算出龙卷风吸力摇摆的坐标,模拟物体绕着龙卷风旋转。
然后通过摇摆的坐标和粒子本身的坐标做一个力的方向,让粒子模拟被龙卷风吸引着旋转的效果:
public void Execute(ref LocalTransform transform, in Particle particle)
{
var tornadoPos = new float3(Tornado.x + BuildingSystem.TornadoSway(transform.Position.y, ElapsedTime),
transform.Position.y, Tornado.y);
var delta = tornadoPos - transform.Position;
float dist = math.length(delta);
delta /= dist;
float inForce = dist - math.saturate(tornadoPos.y / 50f) * 30f * particle.radiusMult + 2f;
transform.Position += new float3(-delta.z * ParticleSpinRate + delta.x * inForce, ParticleUpwardSpeed,
delta.x * ParticleSpinRate + delta.z * inForce) * DeltaTime;
if (transform.Position.y > 50f)
{
transform.Position.y = 0f;
}
}
结合起来,现在的龙卷风变成这样了,它会吸引粒子,并且在场景里面移动:

3、 CameraSystem
做完上面2步之后,龙卷风动起来了,但摄像机并没有跟随着龙卷风移动。所以需要通过CameraSystem对摄像机进行控制。
同样是通过BuildingSystem.Position得到当前时间点龙卷风所在的坐标,然后找到主摄像机,把坐标同步:
public void OnUpdate(ref SystemState state)
{
var tornadoPosition = BuildingSystem.Position((float)SystemAPI.Time.ElapsedTime);
var cam = Camera.main.transform;
cam.position = new Vector3(tornadoPosition.x, 10f, tornadoPosition.y) - cam.forward * 60f;
}
四、 交互部分
经过了上面的步骤,我们已经生成了建筑物,也生成了龙卷风效果,但现在龙卷风还不会破坏建筑物。
为了达到龙卷风会破坏建筑物的效果,我们看看最后的BuildingSystem。
这里的做法有点类似于龙卷风吸引粒子的做法,也是通过Position得到龙卷风坐标,通过TornadoSway得到摇摆坐标,然后把进入范围内的bar的point从原来坐标吸到空中。

3612

被折叠的 条评论
为什么被折叠?



