游戏开发进阶Unity网格(Mesh\动态合批\骨骼动画\蒙皮)

目录
  • 一、前言
  • 二、Hello Mesh
  • 三、萌新初识Mesh
    • 1、引擎内置的Mesh
    • 2、Mesh是什么
  • 三、Mesh的创建方式
    • 1、第三方建模软件
    • 2、Unity建模插件:ProBuilder
    • 3、程序动态生成网格
  • 四、Unity中如何显示网格
    • 1、MeshFilter:网格过滤器
    • 2、MeshRenderer:网格渲染器
    • 3、SkinnedMeshRenderer:蒙皮网格渲染器
      • 3.1 骨骼动画
      • 3.2 SkinnedMeshRenderer组件
      • 3.2 使用BakeMesh进行优化
  • 五、纯代码动态创建网格
    • 1、创建Mesh对象
    • 2、顶点坐标
    • 3、UV坐标
    • 4、三角形序列
    • 5、重新计算法线和包围体
    • 6、完整版代码
    • 7、测试
    • 8、项目源码
  • 六、网格相关的开源项目
    • 1、2D网格涂鸦
    • 2、3D网格涂鸦
    • 3、网格体素化
    • 4、网格平滑算法
    • 5、网格切割
    • 6、网格合并
  • 七、未完的探险

一、前言

嗨,大家好,我是新发。
有同学私信我让我写一篇Unity网格相关的教程,

那我就带大家来一次Unity的网格探险之旅吧~

二、Hello Mesh

我背着旅行背包走在Unity的场景中,突然眼前出现了一棵树,

我走近一看,这棵树身上挂着MeshFilterMeshRenderer组件,根据Unity探险手册记载,这个MeshFilter是网格过滤器,它会引用一个网格资源,我顺腾摸瓜,找到了对应的网格,

实在太美了,我久久伫立,这就是网格啊!
正当我欣赏着网格三角形时,突然世界暗了下来,眼前出现了一团火,

我又拿出了Unity探险手册,啊,这一定就是粒子系统了!它可以动态生成网格。

天外传来一阵打字声,场景中出现了一行看起来像文字的网格,作为一个具有多年Hello World经验的程序员,我看出了第一个单词应该是Hello,第二个单词…我知道了,

Hello Mesh

(此处为震撼人心的入场音乐)

三、萌新初识Mesh

1、引擎内置的Mesh

网格的英文名是MeshUnity萌新最先接触的网格应该就是引擎内置的Cube(正方体)、Capsule(胶囊体)、Cylinder(圆柱体)、Plane(平面)、Sphere(球体)、Quad(四边形),如下

事实上,我们在Unity场景中,所有能被渲染出来的物体都会带有网格,比如3D模型、粒子特效、UI、文字等等。

2、Mesh是什么

从概念上讲,网格是图形硬件用来绘制复杂内容的构造。它至少包含一组定义3D空间中点的顶点,以及一组连接这些点的三角形,实际上还包含法线、顶点颜色纹理坐标等信息,这些三角形构成了网格所代表的任何表面。

我们可以看下UnityMesh类,Mesh的属性和方法很多,我这里列举几个比较常用的,如下

// 顶点坐标数组
public Vector3[] vertices { get; set; }
// 法线向量数组
public Vector3[] normals { get; set; }
// 顶点颜色数组
public Color[] colors { get; set; }
// 三角形序列数组,每三个数字为一组
public int[] triangles { get; set; }
// uv坐标数组
public Vector2[] uv { get; set; }
// 重新计算法线,在修改完顶点后,通常会更新法线来反映新的变化,注意,法线是根据共享的顶点计算出来的。
public void RecalculateNormals();
// 从法线和纹理坐标重新计算网格的切线。修改网格的顶点和法线之后,如果网格使用引用法线贴图的着色器进行渲染,则切线需要更新。
public void RecalculateTangents();
// 重新计算从网格包围体的顶点, 在修改顶点后需要这个函数以确保包围体是正确的,赋值三角形将自动重新计算这个包围体。
public void RecalculateBounds();

画个图,方便大家有个直观印象,

三、Mesh的创建方式

1、第三方建模软件

建模本质上就是建网格,我们可以事先通过第三方建模软件来创建模型网格,

常见的建模软件比如

3DS MAX官网:https://www.autodesk.com/products/3ds-max/overview

MAYA官网:https://www.autodesk.com/products/maya/overview

blender官网:https://www.blender.org/

2、Unity建模插件:ProBuilder

Unity官方提供了一个可以用来创建和自定义几何体的工具ProBuilder,我们可以在UnityPackage Manager中下载到这个插件,

使用ProBuilder我们可以直接在Unity中创建或编辑简单的几何体,不用通过第三方建模软件,提升了效率,方便快速搭建场景原型,

3、程序动态生成网格

网格也可以是程序动态生成的,比如粒子系统的网格就是动态生成的,

又比如文字,也是程序动态生成网格,

文章后面我还会手把手教你如何使用纯代码来构建网格,这里先不急着写代码,我们继续探寻网格的秘密先~

四、Unity中如何显示网格

Unity中,我们要显示一个网格,需要用到两个组件:MeshFilterMeshRenderer

注:你也可以直接使用SkinnedMeshRenderer组件,与MeshFilterMeshRenderer的区别我下文会讲。

1、MeshFilter:网格过滤器

MeshFilter是网格过滤器,我们需要通过它设置引用的网格资源,比如这里引用的是一个Cube(正方体)网格。

我们可以看下MeshFilter.cs的源码,

[RequireComponent(typeof(Transform))]
[NativeHeader("Runtime/Graphics/Mesh/MeshFilter.h")]
public sealed partial class MeshFilter : Component
{
    [RequiredByNativeCode]  // MeshFilter is used in the VR Splash screen.
    private void DontStripMeshFilter() {}

    extern public Mesh sharedMesh { get; set; }
    extern public Mesh mesh {[NativeName("GetInstantiatedMeshFromScript")] get; [NativeName("SetInstantiatedMesh")] set; }
}

MeshFilter只有两个属性:meshsharedMesh
我们查看Unity的官方手册,看看meshsharedMesh的区别:https://docs.unity3d.com/ScriptReference/MeshFilter.html

我来解读一下,mesh访问的是一个Mesh资源的实例(副本),这意味着我们修改这个mesh并不会修改到原始资源本身,改的只是Mesh的实例(副本)。
sharedMesh是原始资源的引用,如果修改了sharedMesh,比如修改顶点坐标,那么原始资源也会被修改。
画成图大概是这样子:

这里我顺手写个随机修改Mesh顶点坐标的脚本,如下,将下面这个RandoMeshmVertices脚本挂到MeshFilter组件所在的物体上即可,

// RandoMeshmVertices.cs
// 随机修改Mesh顶点坐标
using UnityEngine;
public class RandoMeshmVertices: MonoBehaviour
{
    // Mesh的实例
    MeshFilter meshFilter;
    // 顶点的原始坐标
    Vector3[] originalVertices;
    void Start()
    {
        meshFilter = GetComponent<MeshFilter>();
        originalVertices = meshFilter.mesh.vertices;
    }
    void Update()
    {
        // 随机修改顶点坐标
        Vector3[] vertices = meshFilter.mesh.vertices;
        for (int i = 0, len = originalVertices.Length; i < len; ++i)
        {
            var v = originalVertices[i];
            vertices[i] = v + Random.Range(-0.1F, 0.1F) * Vector3.one;
        }
        meshFilter.mesh.vertices = vertices;
        meshFilter.mesh.RecalculateNormals();
    }
}

运行效果如下,网格顶点坐标发生了随机偏移,

关于mesh属性的访问需要特别注意一下,我们先看看Unity官方手册的说明,https://docs.unity3d.com/ScriptReference/MeshFilter-mesh.html

翻译一下就是,如果一个Mesh资源已经被分配给MeshFiltermesh属性,那么当我们在代码中第一次访问mesh属性时才正真创建了Mesh的实例;再次访问mesh属性时则直接返回这个实例,并且一旦mesh属性被访问,则与原始共享网格的链接会丢失,此时sharedMesh变成mesh的别名,如果我们想避免这种自动生成Mesh实例,可以使用sharedMesh代替。
写成伪代码的话大致是这样子:

public class MeshFilter ...
{
	...
	private Mesh _mesh;
	public Mesh mesh
	{
		get
		{
			if (_mesh == null)
			{
				_mesh = new Mesh();
				Copy(sharedMeh, _mesh);
			}
			return _mesh;
		}
	}
	...
}

还有,如果我们访问了mesh属性而导致自动创建了Mesh实例,则需要在代码中主动调用Resources.UnloadUnusedAssets来销毁没有引用的Mesh实例,建议是在场景切换时调用Resources.UnloadUnusedAssets

2、MeshRenderer:网格渲染器

MeshRenderer,顾名思义,网格渲染器。我们依旧先来看看官方手册的介绍:

https://docs.unity3d.com/Manual/class-MeshRenderer.html

翻译过来就是MeshRenderer会从MeshFilter那里拿到网格数据并在所在物体的位置处将其渲染出来。
如果没有MeshRenderer,我们就看不见网格了,如下

另外,我们还需要在MeshRendererMaterials中指定一个材质球,这样才能正常显示,否则模型表面就是紫色的。

3、SkinnedMeshRenderer:蒙皮网格渲染器

SkinnedMeshRenderer是蒙皮网格渲染器,可能有小伙伴就会问了,上面使用MeshFilterMeshRenderer已经可以显示模型网格了,为什么又弄了一个SkinnedMeshRenderer呢?
看下Unity官方手册的介绍:https://docs.unity3d.com/Manual/class-SkinnedMeshRenderer.html

可以看到SkinnedMeshRenderer其实是针对带 骨骼动画 的模型的渲染的。

3.1 骨骼动画

为什么需要做骨骼动画呢?

就好比我们人一样,我们的骨骼会随着我们肌肉的伸缩而动,骨骼又可以带动它管辖的身体部位发生形变和移动,骨骼还会影响它所连接的其他骨骼一起发生联动。对应到模型动作上,想想一个简单的举手动作要牵涉到多少网格顶点的移动,如果没有骨骼,那动画师要每帧挨个网格顶点进行调整,即使动画做出来了,这个动画也不能复用到其他模型上,因为不同模型的顶点信息都不一样,这么低效的动画制作肯定是不行的,于是,就有了骨骼动画。

骨骼动画的原理

就是将模型分为骨骼(Bone)和蒙皮(Mesh)两个部分,骨骼可分为多层父子骨骼,每个骨骼都附加到周围网格的一些顶点上,在动画关键帧数据的驱动下,计算出各个父子骨骼的位置,基于骨骼的控制通过顶点混合动态计算出蒙皮网格的顶点。

动画师可以在MAYA软件上给模型绑定骨骼,绑定骨骼不是本文的重点,这里就不展讲开具体操作了,感兴趣的同学可以自行百科学习。

制作好导出为fbx格式,

fbx文件导入到Unity中,选中它,

Inspector视图中点击Rig按钮,

我们可以看到动画类型Animation TypeNoneLegacyGenericHumanoid四个,

具体选项可以参见Unity官方手册:https://docs.unity3d.com/Manual/FBXImporter-Rig.html

我这里演示一下人形骨骼动画,选择Humanoid类型,Avatar Definition选择Create From This Model,然后点击Configure

Inspector视图中我们就可以看到对应的骨骼绑定信息了,

如下,绿色的线段就是一根根骨骼,

我们调整一根骨骼,对应的网格也会跟着一起动,如下

这样做出来的人形动画是可以进行复用了,有请妹子上场,

骨骼动画资源的话,我在之前的文章中也介绍过一个宝藏网站Mixamohttps://www.mixamo.com/,上面有很多做好的人形骨骼动画,

看,是不是挺好玩的,

我们可以把它的动作直接复用到我们自己的人形模型上,效果如下:



3.2 SkinnedMeshRenderer组件

骨骼动画可以正常播放,要归功于SkinnedMeshRenderer组件,制作好骨骼动画的fbx文件导入Unity中,Unity会自动帮我们挂上SkinnedMeshRenderer组件,

其中几个重要的属性我讲一下,
Bounds:骨骼数据;
Mesh:要渲染的网格;
Root Bone:根骨骼,其他骨骼都是相对根骨骼移动的;
BlendShapes:一般用于制作表情融合,我之前写过一篇文章讲过BlendShapes

Unity通过BlendShape实现面部表情过渡切换Animation教程

我们再来看看SkinnedMeshRenderer脚本的属性和方法:

需要讲的应该就是这个BakeMesh方法了,下面我就单独拎出来讲下BakeMesh

3.2 使用BakeMesh进行优化

假设现在场景中有100只皮卡丘,每只皮卡丘的网格、贴图、动作相同,

如果每只皮卡丘身上都挂SkinnedMeshRenderer,那就是100SkinnedMeshRenderer在计算蒙皮,

由于SkinnedMeshRenderer是根据骨骼动画动态计算网格顶点坐标,这个运算开销还是不小的,有没有办法优化呢?

SkinnedMeshRenderer提供了一个BakeMesh方法,可以将一个蒙皮动画的某个时间点上的动作,Bake成一个不带蒙皮的Mesh,我们统一使用这个Mesh来显示其余的皮卡丘,这样就可以大大减少了SkinnedMeshRenderer的计算了,
画成图大概是这样子:

不过,上面这种方案的局限性是每只皮卡丘的动画是相同的,如果突然某一只皮卡丘要播放与其他皮卡丘不同的动画,那就不行了。

另一种Bake方案可以是这样:
对皮卡丘的每个动画进行遍历采样,把采样到的Mesh存到数组中,因为这里要Bake很多网格,比较耗时,建议在加载场景时时就完成采样过程;后面要播放某个动画时直接从这个Mesh数组中获取Mesh来显示,此时直接使用MeshFilterMeshRenderer的方式来显示网格就好了。
贴个BakeMesh的示例脚本:

using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// Bake Mesh 示例
/// </summary>
public class BakeMeshTest : MonoBehaviour
{
    [SerializeField]
    Animation m_animation;
    [SerializeField]
    SkinnedMeshRenderer m_skinnedMeshRenderer;
    [SerializeField]
    string m_clipToBake = "Idle";
    List<Mesh> m_bakedMeshList = new List<Mesh>();
    /// <summary>
    /// 采样帧数
    /// </summary>
    [SerializeField]
    int m_numFramesToBake = 30;
    void Start()
    {
        // 获取要Bake的动画片段
        AnimationState clipState = m_animation[m_clipToBake];
        if (clipState == null)
        {
            Debug.LogError(string.Format("Unable to get clip '{0}'", m_clipToBake), this);
            return;
        }
        // 开始播放动画
        m_animation.Play(m_clipToBake, PlayMode.StopAll);
        // 设置动画初始时间戳
        clipState.time = 0.0f;
        // 采样帧间隔
        float deltaTime = clipState.length / (float)(m_numFramesToBake - 1);
        for (int frameIndex = 0; frameIndex < m_numFramesToBake; ++frameIndex)
        {
            string frameName = string.Format("BakedFrame{0}", frameIndex);
            // 创建Mesh
            Mesh frameMesh = new Mesh();
            frameMesh.name = frameName;
            // 动画采样
            m_animation.Sample();
            // 执行BakeMesh
            m_skinnedMeshRenderer.BakeMesh(frameMesh);
            m_bakedMeshList.Add(frameMesh);
            // 设置动画时间戳
            clipState.time += deltaTime;
        }
        // 停止播放动画
        m_animation.Stop();
    }

}

需要提醒的是,这个方案是利用空间换时间,如果模型顶点数据特别多或动画时长特别长的时候,这时就会遇到内存瓶颈。

五、纯代码动态创建网格

一般情况下,网格是事先制作好的资源,但也有一些特殊的需求需要在代码中动态创建网格。
比如我之前写的一篇牙齿碎了的文章:

游戏开发Unity2D图片任意形状破碎裂片效果展示

现在我来教大家如何使用代码从零创建网格并将网格渲染出来,下文我以创建一个正方形网格为例进行讲解。

1、创建Mesh对象

第一步最简单,就是直接new一个Mesh

var mesh = new Mesh();

2、顶点坐标

首先分析一下,一个四边形有四个顶点,假设正方形边长为1,四个点的坐标如下,

写成代码就是这样:

// 构建顶点坐标
var vertices = new List<Vector3>();
vertices.Add(new Vector3(-0.5f, -0.5f, 0));
vertices.Add(new Vector3(-0.5f, 0.5f, 0));
vertices.Add(new Vector3(0.5f, 0.5f, 0));
vertices.Add(new Vector3(0.5f, -0.5f, 0));
// 将顶点坐标设置给Mesh
mesh.SetVertices(vertices);

3、UV坐标

UV坐标就是纹理贴图坐标,它将纹理上每一个点精确对应到模型物体的表面上,注意UV的取值范围是0~1
UV坐标系原点在左下角,U轴是水平轴,V轴是竖直轴,如下:

对应到我们的上面那个正方向网格的话,四个点的UV坐标如下:

写成代码就是这样:

// 构建UV坐标
var uvs = new List<Vector2>();
uvs.Add(new Vector2(0, 0));
uvs.Add(new Vector2(0, 1));
uvs.Add(new Vector2(1, 1));
uvs.Add(new Vector2(1, 0));
// 将UV坐标设置给Mesh
mesh.SetUVs(0, uvs);

4、三角形序列

网格需要切分成三角形,我们可以这样切分,

当然也可以这样切分,

两种切分方法对应不同的三角形序列,假设 法线方向 是垂直于屏幕从内指向屏幕外的话,第一种切分方式的三角形序列如下:

注:法线的方向就决定了表面正面,如果你的材质是单面渲染的话,那么只有从正面看才能看到网格被渲染。

即三角形序列为:{ 0, 1, 2, 0, 2, 3 },注意序号是从0开始的。
为什么是这样的顺序呢?我教大家一个技巧,伸出你的左手,竖起大拇指,像这样子,

大拇指指向法线的方向,那么此时你的其余四根手指头环绕的方向就是三角形的序号的顺序,三个序号为一组按顺序塞入数组中即可,即得到的数组就是:{ 0, 1, 2, 0, 2,3}当然,以下数组最终的效果都是等价的,只要顺序一致即可:

{ 0, 1, 2, 0, 2, 3 },
{ 1, 2, 0, 0, 2, 3 },
{ 0, 2, 3, 1, 2, 0 },

我们现在写成代码,

// 重新计算法线,注意,法线是根据共享的顶点计算出来的。
mesh.RecalculateNormals();

// 重新计算包围体,在修改顶点后需要这个函数以确保包围体是正确的
mesh.RecalculateBounds();

// 从法线和纹理坐标重新计算网格的切线(如果网格使用引用法线贴图的着色器进行渲染,则切线需要更新)
// 因为我们这里不使用法线贴图,所以就不调用它了
// mesh.RecalculateTangents();

5、重新计算法线和包围体

当我们设置或修改了顶点数据后,需要调用MeshRecalculate方法来重新计算一些必要的信息,比如重新计算法线、包围体,代码如下

// 重新计算法线,注意,法线是根据共享的顶点计算出来的。
mesh.RecalculateNormals();

// 重新计算包围体,在修改顶点后需要这个函数以确保包围体是正确的
mesh.RecalculateBounds();

// 从法线和纹理坐标重新计算网格的切线(如果网格使用引用法线贴图的着色器进行渲染,则切线需要更新)
// 因为我们这里不使用法线贴图,所以就不调用它了
// mesh.RecalculateTangents();

6、完整版代码

以上代码封装成GenQuadMesh.cs脚本,完整代码如下:

// 使用代码生成四边形网格
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class GenQuadMesh : MonoBehaviour
{
    public MeshFilter mf;
    private void Start()
    {
        mf.mesh = Build();
    }
   public static Mesh Build()
    {
        var mesh = new Mesh();
        // 构建顶点坐标
        var vertices = new List<Vector3>();
        vertices.Add(new Vector3(-0.5f, -0.5f, 0));
        vertices.Add(new Vector3(-0.5f, 0.5f, 0));
        vertices.Add(new Vector3(0.5f, 0.5f, 0));
        vertices.Add(new Vector3(0.5f, -0.5f, 0));
        // 将顶点坐标设置给Mesh
        mesh.SetVertices(vertices);
        // 构建UV坐标
        var uvs = new List<Vector2>();
        uvs.Add(new Vector2(0, 0));
        uvs.Add(new Vector2(0, 1));
        uvs.Add(new Vector2(1, 1));
        uvs.Add(new Vector2(1, 0));
        // 将UV坐标设置给Mesh
        mesh.SetUVs(0, uvs);
        // 设置三角形序列
        var triangles = new int[] { 0, 1, 2, 0, 2, 3 };
        mesh.SetTriangles(triangles, 0);
        mesh.RecalculateNormals();
        mesh.RecalculateBounds();
        return mesh;
    }
}

7、测试

创建一个空物体,挂上MeshFilterMeshRenderer组件。

再挂上我们上面写的GenQuadMesh脚本,赋值mf变量为MeshFilter对象,如下

运行Unity,看到一个紫色快,

Scene视图的模式设置为Wireframe,如下

现在我们可以看到我们动态创建的网格啦,

上面之所以显示紫色块,是因为我们没有给MeshFilter设置材质球,顺手做一个炮姐的材质球吧,

MeshRenderer设置材质球对象,

重新运行Unity,效果如下,

8、项目源码

要用代码动态创建一个Mesh,就是new一个Mesh,给它塞入顶点坐标、UV坐标和三角形序列即可。再复杂的网格也可以通过这些步骤创建出来~
下面这些就是使用纯代码创建出来的几何体网格,感兴趣的同学可以下载项目源码下来学习。
项目源码:https://codechina.csdn.net/linxinfa/unity-mesh-builder

六、网格相关的开源项目

我再推荐一些网格相关的开源项目给大家~

1、2D网格涂鸦

项目地址:https://github.com/mattatz/unity-triangulation2D

2、3D网格涂鸦

项目地址:https://github.com/mattatz/unity-teddy

3、网格体素化

项目地址:https://github.com/Scrawk/Mesh-Voxelization

4、网格平滑算法

项目地址:https://github.com/mattatz/unity-mesh-smoothing

5、网格切割

项目地址:https://github.com/hugoscurti/mesh-cutter

6、网格合并

项目地址:https://github.com/sanukin39/UniMeshCombiner

七、未完的探险

好了,这次探险之旅就暂时到这里吧,还有很多内容需要探索,先保持体力,我们下次再见,更多关于Unity网格(Mesh\动态合批\骨骼动画\蒙皮)的资料请关注我们其它相关文章!

(0)

相关推荐

  • Unity3D实现自动寻路

    Unity3D自动寻路,供大家参考,具体内容如下 1.首先在Unity3D创造中一个正方体,一个圆柱体和一个平面. 2.将正方体和平面设为静态(Static前的方格打勾) 3.在Window中打开AI进行导航网格烘培 4.在add component中添加Nav Mesh Agent(导航网格代理) 这里将Stopping Distance的值修改为0.5.如果使用默认值0,则最后圆柱体会与正方体重合在一起. 5.附加AI脚本给圆柱体 6.最终效果 Unity3D小白一枚,如有错误希望大佬们指正

  • 一篇文章教会你用Unity制作网格地图生成组件

    目录 前言 1,创建组建出网格的基本单元 2,编辑网格创建脚本 3,地图生成案例 总结 前言 如果你玩过三国志这种类型的战旗游戏或者模拟城市.部落冲突.海岛奇兵这种模拟经营类的游戏,那么你对网格地图一定不会陌生.在这些游戏中,所有地图场景中的物体都是基于整齐的网格来记录位置等信息.如下图: 如果你还是感知不到什么是网格地图.俄罗斯方块或者贪吃蛇你一定不会陌生,物体的存在是依托于规整的网格地图而存在的. 还是一如既往,本篇文章为零基础小白文,如果你是小萌新,并且对网格地图感兴趣的话,可以学习本片文

  • Unity3D网格功能生成球体网格模型

    本文实例为大家分享了Unity3D网格功能生成球体网格模型的具体代码,供大家参考,具体内容如下 前面已经讲过怎样使用mesh生成一个自己的网格,那么本文将会讲述怎样将这个网格变换成自己想要的形状,比如一个球体. 我们需要知道一个从平面坐标到球体坐标的映射公式.假设平面坐标是(x,y),球体坐标是(x0,y0,z0),则 球体坐标(x0,y0,z0)可以通过以下代码得到,(x,y) 对应vertices[i].x和vertices[i].y. v.x = r * Mathf.Cos(vertice

  • Unity3D动态生成平面网格

    在编写几何着色器的时候发现默认的Plane无法满足需求,并且顶点顺序未知,于是便写了一个网格生成代码,便于生成指定大小的Plane,且顶点顺序可控. 效果如下: 一个单元格由4个顶点,两个三角面组成. 四个顶点如下图 则生成面的顶点顺序为: 左上三角形:0 -> 1 -> 2 右下三角形:2 -> 3 -> 0 Unity中顺时针绘制为正面,逆时针绘制为反面. 实现脚本如下: //PlaneBuilder.cs using System.Collections; using Sys

  • 游戏开发进阶Unity网格(Mesh\动态合批\骨骼动画\蒙皮)

    目录 一.前言 二.Hello Mesh 三.萌新初识Mesh 1.引擎内置的Mesh 2.Mesh是什么 三.Mesh的创建方式 1.第三方建模软件 2.Unity建模插件:ProBuilder 3.程序动态生成网格 四.Unity中如何显示网格 1.MeshFilter:网格过滤器 2.MeshRenderer:网格渲染器 3.SkinnedMeshRenderer:蒙皮网格渲染器 3.1 骨骼动画 3.2 SkinnedMeshRenderer组件 3.2 使用BakeMesh进行优化 五

  • unity通过Mesh网格绘制图形球体

    本文实例为大家分享了unity通过Mesh网格绘制球体的具体代码,供大家参考,具体内容如下 接着上一篇文章说: 球体 public class 球体 : MonoBehaviour { MeshRenderer meshRenderer; MeshFilter meshFilter; List<Vector3> verts; List<int> indices; int N = 10; void Start() { verts = new List<Vector3>()

  • unity通过Mesh网格绘制图形(三角形、正方体、圆柱)

    一.介绍 Mesh类:通过脚本创建或是获取网格的类,网格包含多个顶点和三角形数组.顶点信息包含坐标和所在面的法线. unity中3D的世界的所有图形全部都是由三角形构成的. 比如unity已经装配好的几种图形我们可以看一下: 我们可以在unity中通过Mesh类来绘制图形. 所以在我们绘制其它图形之前,首先完成一个小目标,画一个三角形. 二.绘制三角形 首先做准备工作: 1.在场景中创建一个空物体,并挂载MeshRenderer和MeshFilter组件.//我们先不考虑碰撞器的问题,所以不添加

  • Unity游戏开发之炸弹人游戏的实现

    目录 前言 制作思路 开始制作 第一步:游戏场景制作 第二步:墙体代码 第三步:炸弹人制作 第四步:炸弹处理 第五步:敌人制作 第六步:游戏控制器 第七步:UI控制器 前言 大家小时候肯定玩过这款游戏,炸弹人也算是经典中的经典啦~ 希望看到这篇小游戏,可以让你重拾童年跟小伙伴一起对着大屁股电视机玩游戏的美好时光! 时间在慢慢的流逝,那些陪你一起度过童年的小伙伴有多久没联系了呢~ 看完这篇炸弹人,有时间的话就找自己童年的小伙伴们聊会天吧,一起找回童年的回忆和梦想! 回归主题,炸弹人小游戏制作开始!

  • Unity游戏开发之射击小游戏的实现

    目录 前言 游戏画面展示 游戏代码解析 游戏打包 总结 前言 人们一直都说学习和玩游戏不能兼顾,那我们就来边学习怎样制作游戏,边玩游戏 不就兼得了嘛~ 我可真是一个小天才呢~ 所以本篇文章为大家带来一个 横版2D射击小游戏,游戏制作超级简单,玩法一学就会, 一起来看看吧! 游戏画面展示 这款小游戏只用了两个UI界面,一个是菜单界面,另一个是战斗界面 菜单界面有三种模式,分别是一般.困难和地狱 战斗界面就是很简单的从两边刷野怪,然后主角开枪打死他们 UI搭建很简单,只有一张背景图使用Image,加

  • Unity通过脚本创建网格Mesh的方法

    目录 1.创建一个带Mesh的物体 2.通过脚本创建Mesh 1.创建一个带Mesh的物体 Unity中的网格作为组件不能脱离物体单独存在新建脚本CreateMesh public class CreateMesh: MonoBehaviour { void Start() { //该方法会新建一个名为Mesh的GameObject,该物体上包含一个 MeshFilter 和一个 MeshRenderer GameObject gameObject = new GameObject("Mesh&

  • Unity游戏开发实现背包系统的示例详解

    目录 引言 一.UI设计 二.UI 2.1 Slot中的物品显示 2.2 物品切换 2.3 SlotUI的实现 2.4 物品描述信息的展示 三.数据 四.逻辑 引言 背包是游戏中经常使用的一个组件,它负责管理玩家在游戏中所获得的道具.一个完整的背包系统应当具有将物品放置进背包.对背包内物品进行管理和使用背包内物品等功能.而往往一个背包系统的逻辑关系较为复杂,如果把所有功能都放在一个脚本中实现会使代码显得十分冗杂且缺乏逻辑.所以在背包系统的设计过程中,我们常将其分解为数据.逻辑和UI三部分分别来进

  • Unity游戏开发实现场景切换示例

    目录 引言 一.实现逻辑 二.代码实现 2.1 Transition Manager 2.2 Teleport 2.3 Cursor Manager 引言 在unity中可以将不同场景的背景和道具放置在不同的Scene当中,通过对Scene的加载和卸载来实现场景之间的切换.同时创建一个基础场景(Control Scene)来对整个游戏系统进行管理,在基础场景(Control Scene)中不放置背景图片或者游戏道具而只添加各种控制单元和Canvas. 一.实现逻辑 在场景切换的实现过程中需要定义

  • Python图形用户界面与游戏开发实例详解

    目录 前言 基于tkinter模块的GUI 基于wxPython模块的GUI Pygame游戏开发 1.在游戏窗口中绘图 2.图形的处理 3.动画效果 4.碰撞检测 5.事件处理 总结 前言 对于使用过计算机的人,应该对图形用户界面(GUI)应该都不会太陌生,这里就不在赘述.那么对于python这样的动态语言有没有GUI相关的库呢?答案是肯定有的,那么常见的有哪些呢?主要有tkinter.wxPython.PyQt.PyGTK等模块,而tkinter是python默认的模块,没有功能特别强大的G

  • javascript游戏开发之《三国志曹操传》零部件开发(一)让静态人物动起来

    首先来说,让一个游戏赋有可玩性必须要动静结合.(哈哈,大家以为我要讲作文了...但其实我今天要讲的是Javascript)静态的东西谁不会做呢?因为东西一生下来就是静态的(除非你是用的gif动画),所以不需要任何处理就能完成静态.那么我将要在下面告诉大家如何运用Javascript将静态图片变为动态图片. 一.图片准备     fight01.pngfight02.pngfight03.pngfight04.png03.png02.png01.png首先,我找了一些出自经典游戏<三国志曹操传>

随机推荐