unity代码创建mesh

2023-10-29

简介 Introduction  



这个教程将让你学会如何创建一个星型控件以及如何制作这个控件的自定义编辑器。你将学会:
  • 动态的建立Mesh。
  • 使用一个嵌套类。
  • 建立一个自定义编辑器。
  • 使用SerializedObject。
  • 支持所见即所得。
  • 对Undo、Redo、Reset和prefab提供支持。
  • 支持多对象编辑。
  • 支持场景视图内编辑。

我们假设你已经学会了Unity C#的基础编程知识,以及Unity 编辑器的基础知识。如果你已经完成了相关的学习,Let's Go! 

建立Star类 Creating the star  



我们建立一个全新的Unity工程,然后建立一个新的C#脚本,将它命名为Star。我们将用这个脚本,建立一个由三角面拼接成的星,这里需要一个Mesh。 

什么是Mesh?  


3D模型是由多边形拼接而成,一个复杂的多边形,实际上是由多个三角面拼接而成。所以一个3D模型的表面是由多个彼此相连的三角面构成。三维空间中,构成这些三角面的点以及三角形的边的集合就是Mesh。 
using UnityEngine;   public class Star : MonoBehaviour {   private Mesh mesh;   }  


任何对于Mesh的使用,都必须搭配一个MeshFilter组件,而MeshFilter又被用于MeshRenderer组件。只有这样,才能被Unity绘制。所以,这些组件都必须被加载到GameObject对象上,我们的Star对象也必须这么做。当然,我们可以手动添加这些组件,但默认的自动添加是一个更好的办法。所以我们需要添加一个RequireComponent类作为Star对象的一个特性。 


什么是类的特性?  


特性像对类附加一个标签,用来告诉编译器这个类需要如何处理。是除了类声明的代码之外,对类做的附加说明。另外,特性不止针对类,对方法和属性同样适用。 

typeof有什么用?  


typeof是一种运算符,能够获得任何类的类型描述数据,数据里最常用的就是类的名字。那为什么不直接在代码里写类的名字就好呢?因为代码中所有的赋值和运算都需要变量,直接使用类的名字会导致编译错误。 
using UnityEngine;  [RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]  public class Star : MonoBehaviour { 

private Mesh mesh; 




现在,我们建立一个新的空GameObject,将它命名为My First Star,然后拖拽我们的脚本Star到My First Star上。你可以看到,My First Star拥有了两个组件,MeshRenderer和Star。
拖动一个,得到三个 

下一个步骤是建立一个Mesh。我们需要在Unity的Start事件里来做这些事,Start事件将在程序启动的时候发生。我们还需要在MeshFilter中给这个新的Mesh起一个名字。 
using UnityEngine; 

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 
public class Star : MonoBehaviour { 

private Mesh mesh;  void Start () {   GetComponent<MeshFilter>().mesh = mesh = new Mesh();   mesh.name = "Star Mesh";   }  } 


无编辑器模式与实时预览模式 
当然,现在我们在预览模式下还看不到任何东西,因为Mesh还是空的。所以让我们开始编辑顶点数组吧,我们的Star类需要一个用来设置顶点数量的属性,以及这些定点与中心的相对距离。 
第一个顶点是Star的中心点,其余的顶点将顺时针排列。我们将使用四元数来计算这些点的排列。因为我们假设俯视Z轴,所以,轮转的角度是负数,否则,将使这些点做逆时针排列。我们不需要设置第一个点,因为vector默认会被设置成0, Mesh中使用本地坐标系。 
using UnityEngine; 

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 
public class Star : MonoBehaviour {  public Vector3 point = Vector3.up;   public int numberOfPoints = 10;  private Mesh mesh;  private Vector3[] vertices;  void Start () { 
GetComponent<MeshFilter>().mesh = mesh = new Mesh(); 
mesh.name = "Star Mesh";  vertices = new Vector3[numberOfPoints + 1];   float angle = -360f / numberOfPoints;   for(int v = 1; v < vertices.Length; v++){   vertices[v] = Quaternion.Euler(0f, 0f, angle * (v - 1)) * point;   }   mesh.vertices = vertices;  } 



新的编辑器属性 
三角面会被保存成顶点数组,每个面三个顶点。因为我们使用三角形来描述多边形,每个三角形都起始于相同的中点,并且与其他的三角形相连。最后一个三角形与第一个三角形相连。例如,如果有四个三角形,那么顶点数组如下{0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1}。 
using UnityEngine; 

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 
public class Star : MonoBehaviour { 

public Vector3 point = Vector3.up; 
public int numberOfPoints = 10; 

private Mesh mesh; 
private Vector3[] vertices;  private int[] triangles;  void Start () { 
GetComponent<MeshFilter>().mesh = mesh = new Mesh(); 
mesh.name = "Star Mesh"; 

vertices = new Vector3[numberOfPoints + 1];  triangles = new int[numberOfPoints * 3];  float angle = -360f / numberOfPoints; 
for(int v = 1 , t = 1 ; v < vertices.Length; v++ , t += 3 ){ 
vertices[v] = Quaternion.Euler(0f, 0f, angle * (v - 1)) * point;  triangles[t] = v;   triangles[t + 1] = v + 1;  }  triangles[triangles.Length - 1] = 1;  mesh.vertices = vertices;  mesh.triangles = triangles;  } 



一个简单的星星 
现在,我们的星星看起来还只是一个简单的多边形。Unity也提示说丢失材质坐标,因为默认的Shader需要这些坐标。我们不会使用一个纹理来描绘所有的星星,让我们通过建立我们自己的Shader来消除这个警告,这个Shader将只使用顶点着色。 
我们建立一个新的Shader将它命名为Star,然后写入以下代码。 


什么是CGPROGRAM?  


Basically, data flows from the Unity engine into the graphics card, where it's processed per vertex. Then interpolated data flows from the vertices down to the individual pixels. In this case, we pass position and color data all the way down. The only additional thing we do is convert vertex positions from world space to screen space. 
The statements above the CGPROGRAM switch off default lighting and depth buffer writing. Culling is switched off so we can see the triangles from both sides, not just the front. "Blend SrcAlpha OneMinusSrcAlpha" is default alpha blending, allowing for transparency. 

为什么不使用fixed-function shader?  


fixed-function shader已经属于过时的技术了。 CGPROGRAM 在将数据转化成屏幕像素方面拥有更强大的功能。 
Shader "Star"{ 
SubShader{ 
Tags{ "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"} 
Blend SrcAlpha OneMinusSrcAlpha 
Cull Off 
Lighting Off 
ZWrite Off 
Pass{ 
CGPROGRAM 
#pragma vertex vert 
#pragma fragment frag 

struct data { 
float4 vertex : POSITION; 
fixed4 color: COLOR; 
}; 

data vert (data v) { 
v.vertex = mul(UNITY_MATRIX_MVP, v.vertex); 
return v; 


fixed4 frag(data f) : COLOR { 
return f.color; 

ENDCG 





现在我们建立一个新的材质球,命名为Star,将Shader设置为我们刚刚编写的Star,并且将这个材质球赋予My First Star。
添加材质球之后 

顶点着色默认是白色,所以我们的多边形现在变成了白色。我们想要一个更漂亮的星星。所以我们来为每个点定义一种颜色。我们再添加一个frequency属性,这样我们就能让程序自动重复点的序列,而不用我们逐个定义全部的点。这个选项取代了numberOfPoints。 
我们在最后需要确认frequency属性是否正确,并且星星至少拥有一个点。如果没有,我们的代码就可能出错。 

Why check both for null and the length?  


When freshly created, our star component won't have an array yet. It's also technically possible for scripts to explicitly set our array to null later on. We need to watch out for that, to prevent errors. Only if the array does exists do we go ahead and check its length as well. 
using UnityEngine; 

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 
public class Star : MonoBehaviour {  public Vector3[] points;   public int frequency = 1;  private Mesh mesh; 
private Vector3[] vertices; 
private int[] triangles; 

void Start () { 
GetComponent<MeshFilter>().mesh = mesh = new Mesh(); 
mesh.name = "Star Mesh";  if(frequency < 1){   frequency = 1;   }   if(points == null || points.Length == 0){   points = new Vector3[]{ Vector3.up};   }   int numberOfPoints = frequency * points.Length;  vertices = new Vector3[numberOfPoints + 1]; 
triangles = new int[numberOfPoints * 3]; 
float angle = -360f / numberOfPoints; 
for(int  iF = 0,  v = 1, t = 1 ; iF < frequency; iF++){   for(int iP = 0; iP < points.Length; iP += 1,  v += 1, t += 3){ 
vertices[v] = Quaternion.Euler(0f, 0f, angle * (v - 1)) *  points[iP]
triangles[t] = v; 
triangles[t + 1] = v + 1;  }  } 
triangles[triangles.Length - 1] = 1; 

mesh.vertices = vertices; 
mesh.triangles = triangles; 




配置好的点 
我们需要些颜色!如果把全部的顶点都指定相同的颜色就很简单,但这样太无聊了。我们来试试给每个顶点分配一个颜色。我们需要一个数组来保存这些颜色数据,而且必须保持颜色和顶点的数量一致。这有点小麻烦,我们干脆换成另外一种方式,在Star类中建立一个新的类,这个类可以保存一个顶点的颜色和位置。然后我们可以用这个类的数组来代替vector数组。 
这类叫Point,如果在Star类之外使用,就是Star.Point。在Star里面Point就可以了。为了让Unity能够将Point序列化,我们为Point添加System.Serializable特性。 


为什么不用结构体?  


Because Star.Point is so lightweight and its data is always needed all at once, it would make sense to use a struct type and avoid the overhead that objects add. However, Unity does not support serialization of custom struct types. So you're stuck using classes to bundle data you want to store. 
If you're really concerned about the object overhead and possible null errors, you can always store the offset and color data in two separate arrays. However, then you would need to make sure that these arrays always stay synchronized. While that is definitely doable, the class approach is simpler. That's why I use it in this tutorial. 
using System;  using UnityEngine; 

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 
public class Star : MonoBehaviour {  [Serializable]   public class Point {   public Color color;   public Vector3 offset;   }  public  Point [] points; 
public int frequency = 1; 

private Mesh mesh; 
private Vector3[] vertices;  private Color[] colors;  private int[] triangles; 

void Start () { 
GetComponent<MeshFilter>().mesh = mesh = new Mesh(); 
mesh.name = "Star Mesh"; 

if(frequency < 1){ 
frequency = 1; 

if(points == null || points.Length == 0){ 
points = new  Point []{  new Point() }; 


int numberOfPoints = frequency * points.Length; 
vertices = new Vector3[numberOfPoints + 1];  colors = new Color[numberOfPoints + 1];  triangles = new int[numberOfPoints * 3]; 
float angle = -360f / numberOfPoints; 
for(int iF = 0, v = 1, t = 1; iF < frequency; iF++){ 
for(int iP = 0; iP < points.Length; iP += 1, v += 1, t += 3){ 
vertices[v] = Quaternion.Euler(0f, 0f, angle * (v - 1)) * points[iP] .offset colors[v] = points[iP].color;  triangles[t] = v; 
triangles[t + 1] = v + 1; 


triangles[triangles.Length - 1] = 1; 

mesh.vertices = vertices;  mesh.colors = colors;  mesh.triangles = triangles; 




有了颜色之后 
最后是关于中心点的。现在,我们还没有给它设置颜色,所以它一直保持着透明。让我们来为它添加一个颜色属性,最终,这个星星看上去变漂亮了。 
using System; 
using UnityEngine; 

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] 
public class Star : MonoBehaviour { 

[Serializable] 
public class Point { 
public Color color; 
public Vector3 offset; 


public Point[] points; 
public int frequency = 1;  public Color centerColor;  private Mesh mesh; 
private Vector3[] vertices; 
private Color[] colors; 
private int[] triangles; 

void Start () { 
GetComponent<MeshFilter>().mesh = mesh = new Mesh(); 
mesh.name = "Star Mesh"; 

if(frequency < 1){ 
frequency = 1; 

if(points == null || points.Length == 0){ 
points = new Point[]{ new Point()}; 


int numberOfPoints = frequency * points.Length; 
vertices = new Vector3[numberOfPoints + 1]; 
colors = new Color[numberOfPoints + 1]; 
triangles = new int[numberOfPoints * 3]; 
float angle = -360f / numberOfPoints;  colors[0] = centerColor;  for(int iF = 0, v = 1, t = 1; iF < frequency; iF++){ 
for(int iP = 0; iP < points.Length; iP += 1, v += 1, t += 3){ 
vertices[v] = Quaternion.Euler(0f, 0f, angle * (v - 1)) * points[iP].offset; 
colors[v] = points[iP].color; 
triangles[t] = v; 
triangles[t + 1] = v + 1; 


triangles[triangles.Length - 1] = 1; 

mesh.vertices = vertices; 
mesh.colors = colors; 
mesh.triangles = triangles; 




漂亮的星星,麻烦的编辑器

Mesh mesh = new Mesh();
mesh.vertices = objInstace.VertexArray;
mesh.triangles = objInstace.TriangleArray;
if (objInstace.UVArray.Length > 0)
     mesh.uv = objInstace.UVArray;
if (objInstace.NormalArray.Length>0)
     mesh.normals = objInstace.NormalArray;
mesh.RecalculateBounds();
 
GameObject go = new GameObject();
MeshFilter meshFilter = go.AddComponent<MeshFilter>();
meshFilter.mesh = mesh;
 
MeshRenderer meshRenderer = go.AddComponent<MeshRenderer>();

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

unity代码创建mesh 的相关文章

随机推荐

  • JDBC连接mysql数据库

    jdbc连接mysql数据库代码 package com study jdbctest import java sql Connection import java sql DriverManager import java sql Res
  • 你尚未连接--代理服务器可能有问题,或地址不正确。

    这种情况可能是你装了梯子之后 自动开启了代理服务器 解决方法 1 控制面板 gt 网络和Internet 2 网络和共享中心 gt internet选项 3 连接 gt 局域网设置 4 把代理服务器的沟沟取消 这样就解决了
  • Django model select获取数据详细讲解

    基本操作 获取所有数据 对应SQL select from User User objects all 匹配 对应SQL select from User where name Pala User objects filter name P
  • 越伟大的事物越不带个人因素

    很多事物都带上个人因素 例如带有某些人的印记 但是越伟大的事物越不带个人因素 因为这时个人因素对于事物的发展已经不重要了 例如一位伟大的君主建立的王朝 会带上他的个人印记 例如他定的国号等 并且他的个人喜好 观点会影响民众 他的个人魅力和魄
  • JavaScript数据结构之队列

    JavaScript 数据结构之队列思维导图 JavaScript 数据结构之队列源码
  • Mock&Mockito使用手册

    基础概念 mock 测试就是在测试过程中 对于某些不容易构造或者不容易获取的对象 用一个虚拟的对象来创建以便测试的测试方法 这个虚拟的对象就是mock对象 mock对象就是真实对象在调试期间的代替品 Mock 对象 模拟对象的概念就是我们想
  • 关于从平台借用数据集的想法

    大家都知道 深度学习算法需要大量的数据 但是获取有标记的数据太难了 往往要花费大量的精力 一些比赛平台往往会发布任务相关的数据集 但是这些数据集是储存在云端不可获取的 如果我们能够借用这些与我们任务相关的数据的话 可以帮我们节约很长的时间
  • template elasticsearch6备注

    install sh脚本 ES TEMPLATE DIR home xlj elasticsearch 6 2 3 config template event json if test f ES TEMPLATE DIR then echo
  • ESP32 之 ESP-IDF 教学(十)—— 电机控制器(MCPWM)

    本文章 来自原创专栏 ESP32教学专栏 基于ESP IDF 讲解如何使用 ESP IDF 构建 ESP32 程序 发布文章并会持续为已发布文章添加新内容 每篇文章都经过了精打细磨 通过下方对话框进入专栏目录页 CSDN 请求进入目录 O
  • 【满分】【华为OD机试真题2023 JS】找出通过车辆最多颜色

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 找出通过车辆最多颜色 知识点滑窗 时间限制 1s 空间限制 256MB 限定语言 不限 题目描述 在一个狭小的路口 每秒只能通过一辆车 假如车辆的颜色只有3种 找出N秒内经过的最
  • OSG第三方库编译之三十八:hdf5编译(Windows、Linux、Macos环境下编译)

    目录 1 hdf5介绍 2 hdf5下载 3 Windows下编译 4 Linux下编译 5 MacOS下编译 1 hdf5介绍 HDF5 Hierarchical Data Format 是一种跨平台传输的文件格式 存储图像和数据 HDF
  • android wifi信号显示,如何获得可用的wifi网络并将其显示在android中的列表中

    朋友们 我想找到所有可用的WiFi网络并将其显示在我尝试过的列表中 如下所示 但它不起作用 我已经编辑了我的代码 现在我得到了结果但是得到了我不需要的所有结果 我只需要列表中的wifi网络名称 public class MainActivi
  • leetcode402——Remove K Digits

    题目大意 字符串num代表一个非负整数 删除其中k位使得数字变得最小 输出不能出现前导零和空串 分析 单调栈 贪心 贪心策略 从左往右删 如果左邻居 gt 当前位 就应该删掉左邻居 这样才能使数字变最小 栈 遍历字符串 当前位小于栈顶元素时
  • java线程异常_java线程异常处理方法

    工作中常发现有些程序发生异常但却没有错误日志 原因就是一些开发线程异常处理错误 导致程序报错但异常信息打印到堆栈上 不好在生产环境中定位问题 在java多线程程序中 所有线程都不允许抛出未捕获的checked exception 比如sle
  • bios无法识别usb键盘问题解决备忘

    戴尔的T3600工作站bios中无法使用usb键盘 参考 USB 3 0 和 USB 2 0 在 Precision T3600 T5600 或 T7600 系统上出现故障 Dell 中国https www dell com support
  • 禁止ubuntu系统弹出报错界面

    永远禁用掉Apport这一特性 可以远离在每次重启时出现错误提示的纷扰 编辑apport文件 etc default apport sudonano etc default apport 找到 enabled 1 这一行 并改变到0 zer
  • 什么是MES生产制造执行系统?实施系统有哪些好处?

    制造企业关心三个问题 生产什么 生产多少 如何生产 企业的生产计划回答了前两个问题 如何生产 由生产现场的过程控制系统SFC 掌握 ERP CRM等系统只为生产计划的编制提供数据信息 APS系统是提供详细的生产计划 为了使 计划 到达 生产
  • Java序列化对象的一个使用案例-使用Http发送对象

    Effective Java 中序列化一节关于java的序列化存在如下说法 对象序列化 object serialization API 它提供了一个框架 用来将对象编码成字节流 serializing 并从字节流编码中重新构建对象 des
  • 2023年Flutter淡出视野,是正在被悄悄放弃吗?

    前言 Flutter 完全没有被放弃的意思 相反Google还不断的更新 年年在进行优化迭代 就在十天前还更新到了3 7 什么是Flutter Flutter 是谷歌于 2017 年创建的用户界面工具包 它是一个开源框架 提供完整的小部件
  • unity代码创建mesh

    简介 Introduction 这个教程将让你学会如何创建一个星型控件以及如何制作这个控件的自定义编辑器 你将学会 动态的建立Mesh 使用一个嵌套类 建立一个自定义编辑器 使用SerializedObject 支持所见即所得 对Undo