一、新建Unity项目
1、新建工程
1)、打开Unity Hub。新建一个项目工程,注意我用的是Unity2020.3.48版本的。
注意这几步,点击新建项目,等待数秒,一个Unity的工程就新建好了。
2、导入资源
1)、我们需要的模型资源已经打包好了,用的时候直接导入到Unity就行。模型资源我会放在文章末尾的资源中,有需要的可以自己下载!
3、工程项目的开发
1)、我们来看一下上一步中导入的资源,在项目的位置和资源文件内容。
2)、在Assets文件夹下新建一个Scenes文件夹。在Scenes文件夹下新建一个场景。打开场景,在场景中新建一个Scene文件夹。将Resource文件夹下的Car模型放在Scene文件夹下。我们会看到这样的场景。
3)、我们需要新建一个地面Ground。
我们可以看到下面这个Ground背景,如下图所示:
4)、接下来我们需要创建很多的网格线。如下图
4、网格数据的创建
1)、新建RectMesh.cs文件,用于创建Rect网格数据。
2)、新建一个PrimitiveBase.cs作为基类。PrimitiveBase.cs的具体代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
[ExecuteInEditMode]
public class PrimitiveBase : MonoBehaviour
{
public Vector3 offset;
protected MeshFilter meshFilter;
public Transform cacheTransform;
protected Vector3[] vertices;
protected int[] triangles;
protected Vector2[] uvs;
private void Awake()
{
Init();
InitMesh();
}
public void Init()
{
cacheTransform = this.transform;
meshFilter = GetComponent<MeshFilter>();
meshFilter.sharedMesh = new Mesh();
}
protected virtual void InitMesh()
{
}
protected virtual void UpdateShape()
{
}
protected void UpdateMesh()
{
if(meshFilter.sharedMesh == null)
{
meshFilter.sharedMesh = new Mesh();
}
meshFilter.sharedMesh.vertices = vertices;
meshFilter.sharedMesh.triangles = triangles;
meshFilter.sharedMesh.uv = uvs;
}
private void OnValidate()
{
InitMesh();
}
}
代码的具体含义,需要自己去研究了,这里涉及到OpenGL的相关知识,有需要的朋友,可以去百度搜索相关的资料。
3)、RectMesh.cs文件的内容:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class RectMesh : PrimitiveBase
{
public float m_Width;
public float m_Length;
public enum PivotAlign
{
Left,
Center,
Right
}
public PivotAlign widthAlign = PivotAlign.Center;
public PivotAlign lengthAlign = PivotAlign.Center;
protected override void InitMesh()
{
if (cacheTransform == null || meshFilter == null)
{
Init();
}
triangles = new int[] { 0, 2, 3, 0, 3, 1 };
uvs = new Vector2[4];
uvs[0].x = 0;
uvs[0].y = 0;
uvs[1].x = 1;
uvs[1].y = 0;
uvs[2].x = 0;
uvs[2].y = 1;
uvs[3].x = 1;
uvs[3].y = 1;
UpdateShape();
}
protected override void UpdateShape()
{
Vector3 localPos = offset;
float w2 = m_Width * 0.5f;
float l2 = m_Length * 0.5f;
vertices = new Vector3[4];
float x0, z0, x1, z1, x2, z2, x3, z3;
x0 = z0 = x1 = z1 = x2 = z2 = x3 = z3 = 0;
switch(widthAlign)
{
case PivotAlign.Left:
x0 = 0f;
x1 = m_Width;
x2 = 0;
x3 = m_Width;
break;
case PivotAlign.Center:
x0 = -w2;
x1 = w2;
x2 = -w2;
x3 = w2;
break;
case PivotAlign.Right:
x0 = -m_Width;
x1 = 0f;
x2 = -m_Width;
x3 = 0f;
break;
}
switch(lengthAlign)
{
case PivotAlign.Left:
z0 = 0;
z1 = 0;
z2 = m_Length;
z3 = m_Length;
break;
case PivotAlign.Center:
z0 = -l2;
z1 = -l2;
z2 = l2;
z3 = l2;
break;
case PivotAlign.Right:
z0 = -m_Length;
z1 = -m_Length;
z2 = 0;
z3 = 0f;
break;
}
vertices[0].x = localPos.x + x0;
vertices[0].y = localPos.y;
vertices[0].z = localPos.z + z0;
vertices[1].x = localPos.x + x1;
vertices[1].y = localPos.y;
vertices[1].z = localPos.z + z1;
vertices[2].x = localPos.x + x2;
vertices[2].y = localPos.y;
vertices[2].z = localPos.z + z2;
vertices[3].x = localPos.x + x3;
vertices[3].y = localPos.y;
vertices[3].z = localPos.z + z3;
UpdateMesh();
}
}
4)、新建RingMesh.cs文件,用于创建圆形网格数据。具体代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class RingMesh : PrimitiveBase
{
public int segment = 1;
public float ringWidth;
public float _ringRadius;
public float _angleBegin;
public float _angleEnd;
protected override void InitMesh()
{
if(cacheTransform == null || meshFilter == null)
{
Init();
}
segment = segment > 0 ? segment : 1;
triangles = new int[segment*2*3];
for(int i = 0;i < segment;i++)
{
int k = i * 6;
int j = i * 2;
triangles[k] = j;
triangles[k + 1] = j + 2;
triangles[k + 2] = j + 3;
triangles[k + 3] = j;
triangles[k + 4] = j + 3;
triangles[k + 5] = j + 1;
}
int vertexCount = segment * 2 + 2;
uvs = new Vector2[vertexCount];
float singleUV = 1f / segment;
float uvY = 0f;
for(int i =0;i < vertexCount;i+=2)
{
uvs[i].x = 0f;
uvs[i + 1].x= 1f;
uvs[i].y = uvY;
uvY += singleUV;
}
UpdateShape();
}
protected override void UpdateShape()
{
int vertexCount = segment * 2 + 2;
vertices = new Vector3[vertexCount];
float angle = _angleBegin * Mathf.Deg2Rad;
float wHalf = ringWidth * 0.5f;
float sin = Mathf.Sin(angle);
float cos = Mathf.Cos(angle);
float minRingRadius = _ringRadius - wHalf;
float maxRingRadius = _ringRadius + wHalf;
Vector3 localPos = offset;
float x = cos * minRingRadius + localPos.x;
float y = localPos.y;
float z = sin * minRingRadius + localPos.z;
vertices[0].x = x;
vertices[0].y = y;
vertices[0].z = z;
x = cos * maxRingRadius + localPos.x;
y = sin * maxRingRadius + localPos.z;
vertices[1].x = x;
vertices[1].y = y;
vertices[1].z = z;
float singleAngle = (_angleEnd - _angleBegin)/segment*Mathf.Deg2Rad;
for(int i =0;i < segment;i++)
{
angle += singleAngle;
sin = Mathf.Sin(angle);
cos = Mathf.Cos(angle);
x = cos * minRingRadius + localPos.x;
y = localPos.y;
z = sin * minRingRadius + localPos.z;
vertices[i * 2 + 2] = new Vector3(x,y,z);
x = cos * maxRingRadius + localPos.x;
y = localPos.y;
z = sin * maxRingRadius + localPos.z;
vertices[i * 2 + 3] = new Vector3(x,y,z);
}
UpdateMesh();
}
}
至此,我们自己创建的Mesh网格数据已经写好了,接下来我们去Unity工程中,创建相应的网格。
二、Rect和Ring网格数据的创建
1、Rect和Ring网格数据的创建
1)、新建空的GameObject。具体的看下图:
找到一一对应的网格数据脚本,设置一下Materials。就可以了。具体的参数和网格数据我已经写好了。资源放在了最后面。
2)、下图是具体的效果,可以看一下。
2、直线的动画和曲线的动画
1)、这里我们需要一个插件,用于制作动画的插件,比较方便。
就是这个插件,我已经放工程文件里面了,自己取。
2)、先在RectMesh.cs文件中添加一段动画。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class RectMesh : PrimitiveBase
{
public float m_Width;
public float m_Length;
public float length
{
get { return m_Length; }
set { m_Length = value; }
}
public enum PivotAlign
{
Left,
Center,
Right
}
public PivotAlign widthAlign = PivotAlign.Center;
public PivotAlign lengthAlign = PivotAlign.Center;
protected override void InitMesh()
{
if (cacheTransform == null || meshFilter == null)
{
Init();
}
triangles = new int[] { 0, 2, 3, 0, 3, 1 };
uvs = new Vector2[4];
uvs[0].x = 0;
uvs[0].y = 0;
uvs[1].x = 1;
uvs[1].y = 0;
uvs[2].x = 0;
uvs[2].y = 1;
uvs[3].x = 1;
uvs[3].y = 1;
UpdateShape();
}
protected override void UpdateShape()
{
Vector3 localPos = offset;
float w2 = m_Width * 0.5f;
float l2 = m_Length * 0.5f;
vertices = new Vector3[4];
float x0, z0, x1, z1, x2, z2, x3, z3;
x0 = z0 = x1 = z1 = x2 = z2 = x3 = z3 = 0;
switch(widthAlign)
{
case PivotAlign.Left:
x0 = 0f;
x1 = m_Width;
x2 = 0;
x3 = m_Width;
break;
case PivotAlign.Center:
x0 = -w2;
x1 = w2;
x2 = -w2;
x3 = w2;
break;
case PivotAlign.Right:
x0 = -m_Width;
x1 = 0f;
x2 = -m_Width;
x3 = 0f;
break;
}
switch(lengthAlign)
{
case PivotAlign.Left:
z0 = 0;
z1 = 0;
z2 = m_Length;
z3 = m_Length;
break;
case PivotAlign.Center:
z0 = -l2;
z1 = -l2;
z2 = l2;
z3 = l2;
break;
case PivotAlign.Right:
z0 = -m_Length;
z1 = -m_Length;
z2 = 0;
z3 = 0f;
break;
}
vertices[0].x = localPos.x + x0;
vertices[0].y = localPos.y;
vertices[0].z = localPos.z + z0;
vertices[1].x = localPos.x + x1;
vertices[1].y = localPos.y;
vertices[1].z = localPos.z + z1;
vertices[2].x = localPos.x + x2;
vertices[2].y = localPos.y;
vertices[2].z = localPos.z + z2;
vertices[3].x = localPos.x + x3;
vertices[3].y = localPos.y;
vertices[3].z = localPos.z + z3;
UpdateMesh();
}
public Tweener DoLength(float endValue,float duration,float delay)
{
return DOTween.To(() => m_Length, x => length = x, endValue, duration).SetDelay(delay);
}
}
2)、创建一个MeshAnimBase.cs文件,动画的基类。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MeshAnimBase : MonoBehaviour
{
public float minDuration = 0.3f;//动画时间
public float maxDuration = 0.7f;
public float minDelay = 0.3f;//动画延时
public float maxDelay = 0.8f;
public bool animAtStart = false; //动画是否播放标志
public virtual void StartAnimation(bool forward)
{
}
}
3)、创建一个RectMeshAnim.cs文件,矩形动画组件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RectMeshAnim : MeshAnimBase
{
private RectMesh[] rectMeshes;
private float[] lengths;
private int count;
private void Start()
{
rectMeshes = gameObject.GetComponentsInChildren<RectMesh>();
count = rectMeshes.Length;
lengths = new float[count];
for(int i =0;i < count;i++)
{
lengths[i] = rectMeshes[i].length;
}
}
public override void StartAnimation(bool forward)
{
if(forward)
{
PlayForwardAnimation();
}
else
{
PlayBackwardAnimation();
}
}
private void SetBeginState()
{
for(int i =0;i < count;i++)
{
rectMeshes[i].length = 0;
}
}
private void PlayForwardAnimation()
{
float duration;
float delay;
for(int i =0;i < count;i++)
{
duration = Random.Range(minDuration,maxDuration);
delay = Random.Range(minDelay,maxDelay);
rectMeshes[i].DoLength(lengths[i],duration,delay);
}
}
private void PlayBackwardAnimation()
{
float duration;
float delay;
for (int i = 0; i < count; i++)
{
duration = Random.Range(minDuration, maxDuration);
delay = Random.Range(minDelay, maxDelay);
rectMeshes[i].DoLength(0, duration, delay);
}
}
}
4)、先在RingMesh.cs文件中添加一段动画。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class RingMesh : PrimitiveBase
{
public int segment = 1;
public float ringWidth;
public float _ringRadius;
public float _angleBegin;
public float _angleEnd;
public float AngleBegin
{
get { return _angleBegin; }
set { _angleBegin = value;UpdateShape(); }
}
public float AngleEnd
{
get { return _angleEnd; }
set { _angleEnd = value;UpdateShape(); }
}
protected override void InitMesh()
{
if(cacheTransform == null || meshFilter == null)
{
Init();
}
segment = segment > 0 ? segment : 1;
triangles = new int[segment*2*3];
for(int i = 0;i < segment;i++)
{
int k = i * 6;
int j = i * 2;
triangles[k] = j;
triangles[k + 1] = j + 2;
triangles[k + 2] = j + 3;
triangles[k + 3] = j;
triangles[k + 4] = j + 3;
triangles[k + 5] = j + 1;
}
int vertexCount = segment * 2 + 2;
uvs = new Vector2[vertexCount];
float singleUV = 1f / segment;
float uvY = 0f;
for(int i =0;i < vertexCount;i+=2)
{
uvs[i].x = 0f;
uvs[i + 1].x= 1f;
uvs[i].y = uvY;
uvY += singleUV;
}
UpdateShape();
}
protected override void UpdateShape()
{
int vertexCount = segment * 2 + 2;
vertices = new Vector3[vertexCount];
float angle = _angleBegin * Mathf.Deg2Rad;
float wHalf = ringWidth * 0.5f;
float sin = Mathf.Sin(angle);
float cos = Mathf.Cos(angle);
float minRingRadius = _ringRadius - wHalf;
float maxRingRadius = _ringRadius + wHalf;
Vector3 localPos = offset;
float x = cos * minRingRadius + localPos.x;
float y = localPos.y;
float z = sin * minRingRadius + localPos.z;
vertices[0].x = x;
vertices[0].y = y;
vertices[0].z = z;
x = cos * maxRingRadius + localPos.x;
y = sin * maxRingRadius + localPos.z;
vertices[1].x = x;
vertices[1].y = y;
vertices[1].z = z;
float singleAngle = (_angleEnd - _angleBegin)/segment*Mathf.Deg2Rad;
for(int i =0;i < segment;i++)
{
angle += singleAngle;
sin = Mathf.Sin(angle);
cos = Mathf.Cos(angle);
x = cos * minRingRadius + localPos.x;
y = localPos.y;
z = sin * minRingRadius + localPos.z;
vertices[i * 2 + 2] = new Vector3(x,y,z);
x = cos * maxRingRadius + localPos.x;
y = localPos.y;
z = sin * maxRingRadius + localPos.z;
vertices[i * 2 + 3] = new Vector3(x,y,z);
}
UpdateMesh();
}
public Tweener DoEndAngle(float endValue,float duration,float delay)
{
return DOTween.To(() => AngleEnd, x => AngleEnd = x, endValue, duration).SetDelay(delay);
}
}
5)、创建一个RingMeshAnim.cs动画文件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RingMeshAnim : MeshAnimBase
{
private RingMesh[] ringMeshes;
private float[] endAngles;
private int count;
private void Start()
{
ringMeshes = gameObject.GetComponentsInChildren<RingMesh>();
count = ringMeshes.Length;
endAngles = new float[count];
for(int i =0;i < count;i++)
{
endAngles[i] = ringMeshes[i].AngleEnd;
}
SetBeginState();
}
public override void StartAnimation(bool forward)
{
if(forward)
{
PlayForwardAnimation();
}
else
{
PlayBackwardAnimation();
}
}
private void SetBeginState()
{
for(int i =0;i < count;i++)
{
ringMeshes[i].AngleBegin = 0f;
ringMeshes[i].AngleEnd = 0f;
}
}
private void PlayForwardAnimation()
{
float duration;
float delay;
for(int i =0;i < count;i++)
{
duration = Random.Range(minDuration,maxDuration);
delay = Random.Range(minDelay,maxDelay);
ringMeshes[i].DoEndAngle(endAngles[i],duration,delay);
}
}
private void PlayBackwardAnimation()
{
float duration;
float delay;
for (int i = 0; i < count; i++)
{
duration = Random.Range(minDuration, maxDuration);
delay = Random.Range(minDelay, maxDelay);
ringMeshes[i].DoEndAngle(0, duration, delay);
}
}
}