3D系统要点


3D系统要点

  • rotation number 变 cc.Quat, angle 替换原来的 rotation

  • position cc.Vec2 变 cc.Vec3

  • 点击 属性检查器 右上方的 3D 按钮进行切换

1
node.is3DNode = true;
  • 3D 场景编辑(点击编辑器左上方的 3D 按钮)

鼠标右键旋转场景视角,使用滚轮缩放场景视图

  • 导入模型

3D 模型格式为使用非常广泛的 .fbx

导入模型的时候编辑器会自动解析模型的内容,并生成 Prefab、网格、骨骼动画 等资源。

自动生成的 Prefab 资源是不能修改的,如果需要修改,可以将这个自动生成的 prefab 拖拽到 场景编辑器 中进行编辑。编辑完成后,将其拖拽到资源管理器的任意文件夹中生成新的 Prefab,用这个新的 Prefab 作为自己的 Prefab 来使用。

  • Mesh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 首先应该指明网格中顶点数据存储的格式
let gfx = cc.renderer.renderEngine.gfx;

// 定义顶点数据格式,只需要指明所需的属性,避免造成存储空间的浪费
var vfmtPosColor = new gfx.VertexFormat([
// 用户需要创建一个三维的盒子,所以需要三个值来保存位置信息
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
{ name: gfx.ATTR_UV0, type: gfx.ATTR_TYPE_FLOAT32, num: 2 },
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
]);

let mesh = new cc.Mesh();
// 初始化网格信息
mesh.init(vfmtPosColor, 8, true);
// 修改 position 顶点数据
mesh.setVertices(gfx.ATTR_POSITION, [
cc.v3(-100, 100, 100), cc.v3(-100, -100, 100), cc.v3(100, 100, 100), cc.v3(100, -100, 100),
cc.v3(-100, 100, -100), cc.v3(-100, -100, -100), cc.v3(100, 100, -100), cc.v3(100, -100, -100)
]);

// 修改 color 顶点数据
let color1 = cc.Color.RED;
let color2 = cc.Color.BLUE;
mesh.setVertices(gfx.ATTR_COLOR, [
color1, color1, color1, color1,
color2, color2, color2, color2,
]);

// 修改 uv 顶点数据
mesh.setVertices(gfx.ATTR_UV0, [
cc.v2(0,0), cc.v2(0, 1), cc.v2(1, 0), cc.v2(1, 1),
cc.v2(1,1), cc.v2(1, 0), cc.v2(0, 1), cc.v2(0, 0),
]);

// 修改索引数据
mesh.setIndices([
0, 1, 2, 1, 3, 2, // front
0, 6, 4, 0, 2, 6, // top
2, 7, 6, 2, 3, 7, // right
0, 5, 4, 0, 1, 5, // left
1, 7, 5, 1, 3, 7, // bottom,
4, 5, 6, 5, 7, 6, // back
]);
  • Mesh Renderer 组件

网格的顶点数据一般都比较抽象,不太容易看出网格里面的三角形是如何分布的。这时候用户可以开启线框模式,用线段按照三角形的分布连接顶点与其他顶点,这样就比较容易看出网格顶点的多少和分布了。

1
cc.macro.SHOW_MESH_WIREFRAME = true;
  • 碰撞检测

射线检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 根据点击的点获取一条由屏幕射向屏幕内的射线
let ray = camera.getRay(touchPos);
// 根据传入的根节点向下检测,并返回检测结果
// 返回的结果包含了节点和距离
let results = cc.geomUtils.intersect.raycast(cc.director.getScene(), ray);
for (let i = 0; i < results.length; i++) {
results[i].node.opacity = 100;
}

如果希望检测的更精确,可以传入一个 handler 函数来进行检测。

let handler = function (modelRay, node, distance) {
// modelRay 为 ray 转换到 node 本地坐标系下的射线

let meshRenderer = node.getComponent(cc.MeshRenderer);
if (meshRenderer && meshRenderer.mesh) {
// 如果有 mesh renderer,则对 mesh 进行检测,虽然比较消耗性能,但是检测会更加精确
return cc.geomUtils.intersect.rayMesh(modelRay, meshRenderer.mesh);
}

// 返回
return distance;
};

let results = cc.geomUtils.intersect.raycast(cc.director.getScene(), ray, handler);
  • 创建基础 3D 物体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function createMesh (data, color) {
let gfx = cc.gfx;
let vfmt = new gfx.VertexFormat([
{ name: gfx.ATTR_POSITION, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
{ name: gfx.ATTR_NORMAL, type: gfx.ATTR_TYPE_FLOAT32, num: 3 },
{ name: gfx.ATTR_COLOR, type: gfx.ATTR_TYPE_UINT8, num: 4, normalize: true },
]);

let colors = [];
for (let i = 0; i < data.positions.length; i++) {
colors.push(color);
}

let mesh = new cc.Mesh();
mesh.init(vfmt, data.positions.length);
mesh.setVertices(gfx.ATTR_POSITION, data.positions);
mesh.setVertices(gfx.ATTR_NORMAL, data.normals);
mesh.setVertices(gfx.ATTR_COLOR, colors);
mesh.setIndices(data.indices);
mesh.setBoundingBox(data.minPos, data.maxPos);

return mesh;
}

// 创建长方体顶点数据
let data = cc.primitive.box(100, 100, 100);
// 根据顶点数据创建网格
let mesh = createMesh(data, cc.color(100, 100, 100));
// 将创建的网格设置到 Mesh Renderer 上
let renderer = this.getComponent(cc.MeshRenderer);
renderer.mesh = mesh;