前言

这几天琢磨着开发个个人作品的时候,发现原来Unity3D官方没有提供圆锥体的创建功能,就自己做了个编辑器扩展。鉴于之前搜索Mesh编程的时候很少有博客把自己的算法讲清楚,这里我抛砖引玉,尽我所能为一些初学者提供参考,当然,算法未必优,如有更好的算法并乐意知会我则不胜感激,我是大龄转行Unity3D开发,一路行来都是自己琢磨,比较辛苦,先行谢过。

软件环境

Win10 + Unity3D 2017.3.0f3

正文

基本思路是以原点为圆锥体底部圆的中心点,以其正上方1单元处的点为圆锥体锥尖顶点,其他点参照Cylinder为分布在半径为0.5单元的圆上,每20度一个点,这样总共加起来的顶点数量是38个,三角形索引数组数量是108个(锥体可以看作底部圆心上移,所以这两部分的三角形数量是相等的,而底部每20度一个点,那么就有18个三角形,所以结果就是18∗3∗2=10818 * 3 *2 = 108)。
下面开始逐步分解实现。

编辑器扩展

首先,扩展编辑器,在GameObject/3D Object下新建一个Cone菜单,为了假装是亲生的,就和Cube等原生菜单放在一起好了。

[MenuItem("GameObject/3D Object/Cone",false,priority = 7)]
public static void CreateCone()
{SpawnConeInHierarchy();
}

这里主要就是利用MenuItem特性来实现的,其中false表示该菜单不需要有效性验证,priority=7控制菜单显示的位置,可以参考这里:
链接:http://www.cnblogs.com/yangrouchuan/p/6690689.html
方便起见,我把图贴下面:

接下来实现SpawnConeInHierarchy方法:

private static void SpawnConeInHierarchy(){Transform[] selections = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.ExcludePrefab);if (selections.Length <= 0){GameObject cone = new GameObject("Cone");cone.transform.position = Vector3.zero;cone.transform.rotation = Quaternion.identity;cone.transform.localScale = Vector3.one;//SetMesh(cone);return;}foreach (Transform selection in selections){GameObject cone = new GameObject("Cone");cone.transform.SetParent(selection);cone.transform.localPosition = Vector3.zero;cone.transform.localRotation = Quaternion.identity;cone.transform.localScale = Vector3.one;//SetMesh(cone);}}

这里分两种情况,如果没有在Hierarchy面板选中任何物体,那么就在根目录下生成一个名字为”Cone”的GameObject,如果有选中物体,则生成的”Cone”会变为选中项的子物体。
PS:这里有个Bug,如果同时选中了多个物体,又是采用的Hierarchy面板右键菜单的方式,那么会在每个选中物体下都生成与选中物体数量相同的子物体,见下图。这个Bug应该不仅限于版本2017.3,因为我在网上有搜到一个同情况的帖子,时间是2016年8月。

目前这个Bug我已经提交给官方确认了,他们已转交给QA,不过不影响使用,避免办法就是不用右键菜单,而是点击菜单栏”GameObject”下的菜单。

到此为止,我们已经扩展了编辑器菜单,但是生成出来的是空物体,接下来我们实现SetMesh方法以创建Mesh,让圆锥体显示出来。

创建Mesh

分两部,首先绘出底部的圆。

绘制圆形底部

圆心已经确定为原点,半径为0.5f,圆上分布共20个点,那么每个点的坐标就可以用三角函数算出。

private static void SetMesh(GameObject go){if (null == go)return;//仿Cylinder参数float myRadius = 0.5f;int myAngleStep = 20;Vector3 myTopCenter = new Vector3(0, 1, 0);Vector3 myBottomCenter = Vector3.zero;//构建顶点数组和UV数组//每20度一个顶点,再加上圆心,得出顶点数组长度Vector3[] myVertices = new Vector3[360 / myAngleStep + 1];//因为uv数组和顶点数组是一一对应的,所以这里同时计算uv数组Vector2[] myUV = new Vector2[myVertices.Length];//将圆心作为第一个顶点,对应的uv设置为贴图正中myVertices[0] = myBottomCenter;myUV[0] =  new Vector2(0.5f, 0.5f);//循环计算其他顶点坐标for (int i = 1; i <= myVertices.Length / 2; i++){float curAngle = i * myAngleStep * Mathf.Deg2Rad;float curX = myRadius * Mathf.Cos(curAngle);float curZ = myRadius * Mathf.Sin(curAngle);myVertices[i] = new Vector3(curX, 0, curZ);//顶点坐标范围是[-0.5,0.5],而uv坐标范围是[0,1],所以要进行转换myUV[i] = new Vector2(curX + 0.5f, curZ + 0.5f);}

接下来,构建三角形索引数组,19个顶点,共18个三角形,所以数组长度是18 * 3 = 54。

int[] myTriangle = new int[(myVertices.Length - 1) * 3];       //每三个索引(即顶点数组中的顶点索引值)为一个三角形索引组for (int i = 0; i <= myTriangle.Length - 3; i = i+3){//每组都以圆心起始myTriangle[i] = 0;//为能从圆锥底部看见物体,这里按逆时针顺序排列,也就是(0 1 2 0 2 3...)myTriangle[i + 1] = i / 3 + 1;//最后一个三角形时终点索引应为1myTriangle[i + 2] = i + 2 == myTriangle.Length / 2 - 1 ? 1 : i / 3 + 2;}}

最后,分配mesh,赋值材质后就可以看到一个圆形物体了。

//构建meshMesh myMesh = new Mesh();myMesh.name = "Cone";myMesh.vertices = myVertices;myMesh.triangles = myTriangle;myMesh.uv = myUV;myMesh.RecalculateBounds();myMesh.RecalculateNormals();myMesh.RecalculateTangents();//分配meshMeshFilter mf = go.AddComponent<MeshFilter>();mf.mesh = myMesh;//分配材质MeshRenderer mr = go.AddComponent<MeshRenderer>();Material myMat = new Material(Shader.Find("Standard"));mr.sharedMaterial = myMat;

因为底部没光照,所以看起来是黑的,另外,上面的代码是我从最终代码中手动修改得到的,可能有错误,只是用于理解思路,完整代码会在最后给出。

完善锥体

底部圆既然已经绘制成功,锥体可以理解为将圆心上移即可,在顶点数量上,三角形索引数组上都相当于double了一份即可。
这里有个情况说明一下,我本来是想共用圆上顶点的,这样整个锥体的顶点数就是20,但经过测试是不可以的,我参考了Cube,顶点数是24,说明不同面的顶点是不能共用的,可能是因为法线方向等因素吧。
修改后的完整代码如下,

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;public class ConeCreatorEditor
{[MenuItem("GameObject/3D Object/Cone",false,priority = 7)]public static void CreateCone(){SpawnConeInHierarchy();}private static void SetMesh(GameObject go){if (null == go)return;//仿Cylinder参数float myRadius = 0.5f;int myAngleStep = 20;Vector3 myTopCenter = new Vector3(0, 1, 0);Vector3 myBottomCenter = Vector3.zero;//构建顶点数组和UV数组Vector3[] myVertices = new Vector3[360 / myAngleStep * 2 + 2];//Vector2[] myUV = new Vector2[myVertices.Length];//这里我把锥尖顶点放在了顶点数组最后一个myVertices[0] = myBottomCenter;myVertices[myVertices.Length - 1] = myTopCenter;myUV[0] =  new Vector2(0.5f, 0.5f);myUV[myVertices.Length - 1] = new Vector2(0.5f,0.5f);//因为圆上顶点坐标相同,只是索引不同,所以这里循环一般长度即可for (int i = 1; i <= (myVertices.Length -2) / 2; i++){float curAngle = i * myAngleStep * Mathf.Deg2Rad;float curX = myRadius * Mathf.Cos(curAngle);float curZ = myRadius * Mathf.Sin(curAngle);myVertices[i] = myVertices[i + (myVertices.Length - 2) / 2] = new Vector3(curX, 0, curZ);myUV[i] = myUV[i + (myVertices.Length - 2) / 2] = new Vector2(curX + 0.5f, curZ + 0.5f);}//构建三角形数组int[] myTriangle = new int[(myVertices.Length - 2) * 3];       for (int i = 0; i <= myTriangle.Length - 3; i = i+3){if (i + 2 < myTriangle.Length / 2){myTriangle[i] = 0;myTriangle[i + 1] = i / 3 + 1;myTriangle[i + 2] = i + 2 == myTriangle.Length / 2 - 1 ? 1 : i / 3 + 2;}else{//绘制锥体部分,索引组起始点都为锥尖myTriangle[i] = myVertices.Length - 1;//锥体最后一个三角形的中间顶点索引值为19myTriangle[i + 1] = i == myTriangle.Length - 3 ? 19 : i / 3 + 2;myTriangle[i + 2] = i / 3 + 1;}}//构建meshMesh myMesh = new Mesh();myMesh.name = "Cone";myMesh.vertices = myVertices;myMesh.triangles = myTriangle;myMesh.uv = myUV;myMesh.RecalculateBounds();myMesh.RecalculateNormals();myMesh.RecalculateTangents();//分配meshMeshFilter mf = go.AddComponent<MeshFilter>();mf.mesh = myMesh;//分配材质MeshRenderer mr = go.AddComponent<MeshRenderer>();Material myMat = new Material(Shader.Find("Standard"));mr.sharedMaterial = myMat;}private static void SpawnConeInHierarchy(){Transform[] selections = Selection.GetTransforms(SelectionMode.TopLevel | SelectionMode.ExcludePrefab);if (selections.Length <= 0){GameObject cone = new GameObject("Cone");cone.transform.position = Vector3.zero;cone.transform.rotation = Quaternion.identity;cone.transform.localScale = Vector3.one;//设置创建操作可撤销Undo.RegisterCreatedObjectUndo(cone, "Undo Creating Cone");SetMesh(cone);return;}foreach (Transform selection in selections){GameObject cone = new GameObject("Cone");cone.transform.SetParent(selection);cone.transform.localPosition = Vector3.zero;cone.transform.localRotation = Quaternion.identity;cone.transform.localScale = Vector3.one;//设置创建操作可撤销Undo.RegisterCreatedObjectUndo(cone, "Undo Creating Cone");SetMesh(cone);}}
}

PS:这里的uv设置比较简单,所以对贴图也特定要求,不然图片会比较扭曲,需要的朋友可以自行修改。

结果

Unity3D编辑器扩展--自定义创建圆锥体相关推荐

  1. 【Unity3D编辑器扩展】Unity3D中实现Text的字体的替换

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦. 一.前言 ...

  2. Unity3D 编辑器扩展 跳转显示指定目录

    做编辑器扩展开发时,假如我们生成一个文件到Assets之外的目录,要查看它的时候,需要从系统路径一步步打开到那个目录. Unity给我们提供了一个API可以直接打开指定目录,类似Project窗口右键 ...

  3. unity3D编辑器扩展

    编辑器扩展只是在编辑项目中运行,发布出来是不会运行的. 固定创建一个文件夹Editor:所有的资源或者代码都不会被打包进去. 01.使用MenuItem添加菜单栏按钮 脚本不需要作为组件存在,可以不用 ...

  4. Unity3D编辑器扩展1——批量处理资源

    前言 最近一段时间正在学习Unity的编辑器扩展方面的内容,因此想把所学到的知识以一种方式记录下来.有可能有很多不足的地方,甚至有错误的地方,请大佬们多多提供帮助. 什么是编辑器扩展 先说说什么是编辑 ...

  5. 【Unity3D编辑器扩展】Unity3D中实现UI界面控制,UI界面的显示和隐藏实现

    推荐阅读 CSDN主页 GitHub开源地址 Unity3D插件分享 简书地址 我的个人博客 QQ群:1040082875 大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有 ...

  6. Unity3d编辑器模式下创建和替换Prefab

    最近在项目中需要开发一套地图数据生成编辑器,记录自己在这个过程中使用的一些好用的创建和替换Prefab的方法. PrefabUtility.CreatePrefab(localPath,obj) 这个 ...

  7. unity3D 编辑器扩展,设置应用图标

    Unity 2018的Android平台Icon怎样设置,自己打开Unity去看下: File->Build Settings->Player Settings->android 平 ...

  8. Unity编辑器扩展 自定义脚本属性面板--基础篇

    开发中,如果使用插件会发现插件的组件,在属性面板上的设计非常方便,看着很高大上,他们是怎么做到的呢 基础 在Editor文件夹下,创建我们的属性面板编辑脚本 using UnityEditor的命名空 ...

  9. Unity3D Editor 编辑器扩展3 Editor脚本

    Unity3D Editor 编辑器扩展3 Editor脚本 环境:Unity2017.2 语言:C# 总起: 在编辑Unity项目的时候,总不可能避免的接触到Unity自身自带的Inspector参 ...

最新文章

  1. k8s nginx ingress配置TLS
  2. JQUERY获取各种HTML控件的值
  3. WebAPI(part5)--排他操作
  4. 前端学习(1897)vue之电商管理系统电商系统之实现搜索功能
  5. Java 远程mapduce_java – 如何远程运行mapreduce作业
  6. oracle磁盘提取工具,实战:巧用磁盘管理工具给oracle提速
  7. c++11线程必须要懂得同步技术
  8. 为什么Linux登录后显示“-bash-3.2#”-转
  9. Swagger启动报错Failed to start bean ‘documentationPluginsBootstrapper‘
  10. anaconda的python文件打包失败的问题解决方案
  11. java中io流浅析
  12. git 暂存文件操作 stash
  13. U盘安装Debian 6 amd64版本
  14. k3导入账套_K3金蝶维护绝密(内部技术教程)
  15. 天津天狮学院电子与计算机学院,天津天狮学院欢迎您!
  16. Android apps 拍立知-功能实现2(相机/选择相册及图像识别调用)
  17. 给Ubuntu文件夹解锁
  18. 奇点大学人工智能专家:人造智能大脑已接近现实
  19. 诺基亚n78微信显示服务器忙,诺基亚N78系统详细解析
  20. 有占空比的c语言中断程序,如何用51程序改变占空比

热门文章

  1. 【干货】新显卡太贵,便宜老卡怎么选?二手亮机卡过渡指南!
  2. 专升本英语——语法知识体系(入门部分)
  3. 办公室电脑如何修改IP地址、设置共享、连接打印机
  4. 【MindSpore】DCGAN生成漫画头像-----利用华为云modelarts云终端实现
  5. C# 数字转汉字(一二三)
  6. 树莓派:GPIO/引脚/Pin 介绍
  7. 7-10 黑洞数(20 分)
  8. 安徽赛区-云巡未来-第十一届全国大学生电子商务“创新、创意及创业”挑战赛 赛后总结
  9. RC串、并联选频网络特性的硬件分析与详解
  10. 安装mathpix注册不了账户:unexcepted error