Unity DOTS Physics学习笔记-射线检测

前言

今年的GDC2019上,Unity推出了基于DOTS(Data Oriented Technology Stack,面向数据技术栈)的物理系统,好像还是跟Havok合作的。反正就是为了配合Unity主推的DOTS的一套全新的东西。

现在(2019年6月初),可以选择用Unity现有的物理系统或者使用Havok,但是选了Havok之后运行起来会提示暂不支持,还要等到2019年夏天。

fig01

这套物理系统的东西很多很复杂,而我的游戏也暂时不需要真实的物理模拟计算,而是只需要最常见的鼠标点击事件,所以我就先只看了场景查询(射线检测)这一块。

正片

创建碰撞体

在官方的Demo里,所有的碰撞体都是通过Unity的一套ConvertToEntity流程创建的,也就是在场景里创建传统的GameObject,然后在ECS启动时转为Entity并销毁原有的GameObject。而现在我们是需要动态创建碰撞体或者需要动态调整碰撞体的参数的,所以需要整理出一套通过脚本来操作的流程。

在Demo里,每一个碰撞体GameObject上面挂了两个关键组件:Physics ShapePhysics Body

fig02

Shape

通过阅读源码,我们发现转换程序读取到GameObject的相关信息,创建了一个Entity,包括一个叫做PhysicsCollider的组件,这个就是我们需要手动添加的关键内容了~组件里面只有一个Value字段,类型是BlobAssetReference<Collider>,这个东西可以通过Unity.Physics.BoxCollider.Create()之类的一组方法创建:

形状方法
长方体BoxCollider.Create(位置, 朝向, 尺寸, 凸半径, 过滤器, 材质)
胶囊体CapsuleCollider.Create(顶点1, 顶点2, 半径, 过滤器, 材质)
球体SphereCollider.Create(位置, 半径, 过滤器, 材质)
圆柱CylinderCollider.Create(位置, 高度, 半径, 朝向, 凸半径, 过滤器, 材质)
平面PolygonCollider.CreateQuad(顶点1-4), 过滤器, 材质
凸包ConvexCollider.Create(顶点数据, 凸半径, 缩放, 过滤器, 材质)
网格MeshCollider.Create(顶点数据, 顶点索引, 过滤器, 材质)

其中材质一项中,除了我们熟知的摩擦力,弹力以及合成方式之外,还有一个Flags字段,决定着这个碰撞体是否是触发器

过滤器就是我们所说的层级了,记录着属于哪些层级,将会和哪些层级发生作用。只不过在这套物理系统中,一个物体可以同时属于多个层级。

Body

对于静态碰撞体,那么到这里就结束了,而对于动态碰撞体,我们则需要添加更多的表示运动的组件。

首先是PhysicsMass,顾名思义这个组件记录了物体的质量信息,当然Unity提供的功能十分复杂(质量分布,惯性张量),这里运动学刚体和动态刚体就是通过惯性和质量来区分的,现在我们可以先不管它。

然后是表示运动速度的PhysicsVelocity,可以有线速度和角速度,这个没啥。以及表示阻力的PhysicsDamping,也有线速度阻尼和角速度阻尼。

最后是表示重力的PhysicsGravityFactor,对于运动学刚体,其重力系数固定为0,即不受重力;动态刚体则可以自由配置。

动手实践

到现在差不多可以开始动手写代码了,比如我们想在场景中的(0,0,0)点创建一个边长为1的正方体触发器,大概就是像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var entityManager = World.Active.EntityManager;
var entity = entityManager.CreateEntity();
entityManager.AddComponentData(entity, new LocalToWorld { Value = float4x4.identity });
entityManager.AddComponentData(entity, new Translation { Value = float3.zero });
entityManager.AddComponentData(entity, new Rotation { Value = quaternion.identity });
entityManager.AddComponentData(entity, new PhysicsCollider
{
Value = BoxCollider.Create(
float3.zero,
quaternion.identity,
new float3(1, 1, 1),
0,
material: new Material { Flags = Material.MaterialFlags.IsTrigger }
)
});

这样就创建了一个静态的trigger

射线检测

那么如何判断鼠标点击到了这个碰撞体呢?

很简单,第一步先获取到摄像机到鼠标位置的射线,这一步就没法用DOTS整合了,还是用传统的方式:

1
2
3
4
var cam = Camera.main;
var ray = cam.ScreenPointToRay(Input.mousePosition);
var origin = ray.origin;
var direction = ray.direction;

然后把射线数据传入DOTS Physics:

1
2
3
4
5
6
ref var world = ref World.Active.GetExistingSystem<BuildPhysicsWorld>().PhysicsWorld;
var input = new RaycastInput { Ray = new Ray { Origin = origin, Direction = direction }, Filter = CollisionFilter.Default };
if (world.CastRay(input, out var hit))
{
// do something with 'hit'
}

很直观了。

结尾

这套新的物理系统功能极其丰富,我这里只是针对我现在的需求(射线检测)使用到了一些皮毛,等后续如果有需求了还是得继续研究的~