自制载具系统:CylinderCaster
自制载具系统:CylinderCaster
Everett Rain整体代码
1 | using System.Collections.Generic; |
变量解释
在开始主要程序的编写之前,我们需要先对 CylinderCaster
所需的变量有一个基本的了解
1 | private Rigidbody _rigidbody; |
-
_rigidbody
引用刚体组件,使物体能够参与物理模拟,如碰撞检测、重力影响等。同时,在 Awake 方法中初始化时,将其设置为运动学模式(isKinematic = true),这意味着它不会受到物理引擎的影响(例如重力或碰撞力),但仍然可以进行碰撞检测。 -
_meshCollider
引用附加到游戏对象的网格碰撞器组件,即根据轮胎模型的网格生成碰撞体。在 Awake 方法中初始化时,设置为凸多边形(convex = true)以提高性能,并设置为触发器(isTrigger = true),以便可以在不施加物理力的情况下检测碰撞。 -
_radius
、_width
、_resolution
的详细介绍可以在 自制载具系统:WheelCollider2 找到,已经了解的可跳过此处。
将 _radius
、_width
、_resolution
的读取权限暴露给外界,实现只读不写的部分公共化:
1 | public float Radius |
这些只读属性提供了访问私有成员变量的方式,允许外部脚本获取轮胎的尺寸和分辨率信息。
方法部分
初始化方法 Awake
1 | private void Awake() |
Awake
方法在脚本实例化时调用,用于初始化组件,在执行顺序上具有高于 Start
的优先级。
Rigidbody:
-
添加一个刚体组件,并将其设置为运动学模式(
isKinematic = true
),这意味着它不会受到物理引擎的影响,但仍然可以进行碰撞检测。
MeshCollider:
-
添加一个网格碰撞器,并将其设置为凸多边形(
convex = true
),以提高碰撞检测性能。 -
设置为触发器(
isTrigger = true
),以便可以在不施加物理力的情况下检测碰撞。
碰撞检测方法 Cast
1 | public bool Cast(Vector3 direction, out RaycastHit hitInfo, float distance) |
-
Cast
方法从外界获取Vector3
类型的direction
方向、获取并返回修改后的RaycastHit
碰撞信息、获取浮点类型的distance
距离。 -
在方法内部使用刚体的
SweepTest
方法执行射线检测。 -
SweepTest
方法检查轮子是否会在给定方向和距离内碰到其他物体,并返回碰撞信息(RaycastHit
),该碰撞信息也会原路返回到调用Cast
方法的位置。 -
返回值为布尔型,当刚体扫掠与任何碰撞器相交时为 true,否则为 false。
初始化几何形状 Init
1 | public void Init(float radius, float width, int resolution) |
-
Init
方法从外部获取浮点类型的radius
、width
,整型的resolution
变量 -
用从外界获取到的这三个变量初始化
CylinderCaster
类的_radius
、_width
、_resolution
三个变量。 -
将这三个变量传递给
GenerateMesh()
方法生成新的网格,并将其分配给网格碰撞器_meshCollider
。
生成网格方法 GenerateMesh
1 | private Mesh GenerateMesh(float radius, float width, int resolution) |
顶点生成
-
使用循环遍历每个角度(从 0 到 2π),计算圆柱表面的顶点位置。
-
每个角度对应两个顶点(左右两侧),形成圆柱侧面。
-
注意生成点的顺序:如果从车轮后方顺平行于地面的一条直径从后往前看,生成的点的顺序为:
第 0 细分段:顶点 0(左)、顶点 1(右) 弧度 0
第 1 细分段:顶点 2(左)、顶点 3(右) 弧度 0.5PI
第 2 细分段:顶点 4(左)、顶点 5(右) 弧度 1.0PI
第 3 细分段:顶点 6(左)、顶点 7(右) 弧度 1.5PI
注意:上面的“弧度”等价于从轮胎左侧观察建立坐标系的一般弧度值(从 X 轴正方向开始逆时针增加)。且该示例为 resolution = 4
的情况,只是为了方便理解,实际的 resolution
的最小值为 8,每个细分段对应着一个弧度角在该圆柱体侧面的一条高线。
三角形索引生成
-
初始化三角形索引列表:
1 | var tris = new List<int>(); |
tris
是一个整数列表,用来存储构成网格的三角形顶点索引。
每个三角形由三个顶点索引组成,而整个圆柱表面由多个这样的三角形拼接而成。
-
循环遍历分辨率以构建三角形:
1 | for (var i = 0; i < resolution; i++) |
resolution
是圆柱表面沿周长方向的细分数量,决定了圆柱的平滑度。
这个循环会遍历每个细分段,并为每一段添加两个三角形来形成一个四边形面片。
-
添加三角形索引
1 | // 第一组三角形 |
假设我们有 resolution = 4
,即圆柱侧面分为 4 个细分段(此时的圆柱实际上为长方体)。以下是详细的三角形索引生成过程:
细分段 0:
第一组三角形:0, 1, 2
第二组三角形:1, 3, 2
细分段 1:
第一组三角形:2, 3, 4
第二组三角形:3, 5, 4
细分段 2:
第一组三角形:4, 5, 6
第二组三角形:5, 7, 6
细分段 3:
第一组三角形:6, 7, 0
第二组三角形:7, 1, 0
将这个详细过程与生成顶点顺序搭配起来看,就可以明白这个函数是如何在圆柱体侧面通过顶点绘制三角形的了:
(为了防止翻页麻烦我把生成顶点的顺序摆在这里)
第 0 细分段:顶点 0(左)、顶点 1(右)
第 1 细分段:顶点 2(左)、顶点 3(右)
第 2 细分段:顶点 4(左)、顶点 5(右)
第 3 细分段:顶点 6(左)、顶点 7(右)
-
创建并配置 Mesh 对象
1 | var mesh = new Mesh(); // 新建 mesh 对象 |
-
verts
是之前生成的顶点列表,包含了所有圆柱表面的顶点位置。 -
tris
是刚刚构建的三角形索引列表,指定了哪些顶点应该连接成三角形。 -
将这两个数组赋值给新的 Mesh 对象。
-
mesh.triangles = tris.ToArray();
这一行代码将tris
列表中的整数数组赋值给Mesh
对象的triangles
属性。 -
Mesh.triangles
定义网格中所有三角形的顶点索引,它规定:每个三角形由三个顶点索引组成,这些索引指向Mesh.vertices
数组中的具体顶点位置。 -
当我们将
tris
转换为数组并赋值给mesh.triangles
时,Unity 的Mesh
渲染器会按照每三个连续的整数定义一个三角形。
总结
CylinderCaster 类通过接受圆柱体的半径、宽度、圆细分量来构建一个圆柱体形的网格,并且提供方法让外界访问生成圆柱体形网格的每个顶点,以及该圆柱体是否与任意方向任意距离内的物体发生交互。
该类的代码核心在于 GenerateMesh
生成网格方法。该方法通过循环遍历每个细分段,并为每个细分段添加两个三角形索引,最终生成一个完整的圆柱形网格。 这种方法高效而且灵活,可以根据不同的 resolution 值生成不同平滑度的圆柱表面。 这对于创建逼真的车辆轮胎非常有用,也为 WheelCollider2
类使用该类进行车轮的物理效果仿真提供了基础。