Unity3D自定義創(chuàng)建圓錐體
前言
這幾天琢磨著開發(fā)個個人作品的時候,發(fā)現(xiàn)原來Unity3D官方?jīng)]有提供圓錐體的創(chuàng)建功能,就自己做了個編輯器擴展。鑒于之前搜索Mesh編程的時候很少有博客把自己的算法講清楚,這里我拋磚引玉,盡我所能為一些初學者提供參考,當然,算法未必優(yōu),如有更好的算法并樂意知會我則不勝感激,我是大齡轉(zhuǎn)行Unity3D開發(fā),一路行來都是自己琢磨,比較辛苦,先行謝過。
軟件環(huán)境
Win10 + Unity3D 2017.3.0f3
正文
基本思路是以原點為圓錐體底部圓的中心點,以其正上方1單元處的點為圓錐體錐尖頂點,其他點參照Cylinder為分布在半徑為0.5單元的圓上,每20度一個點,這樣總共加起來的頂點數(shù)量是38個,三角形索引數(shù)組數(shù)量是108個(錐體可以看作底部圓心上移,所以這兩部分的三角形數(shù)量是相等的,而底部每20度一個點,那么就有18個三角形,所以結(jié)果就是18∗3∗2=10818∗3∗2=108)。
下面開始逐步分解實現(xiàn)。
編輯器擴展
首先,擴展編輯器,在GameObject/3D Object下新建一個Cone菜單,為了假裝是親生的,就和Cube等原生菜單放在一起好了。
[MenuItem("GameObject/3D Object/Cone",false,priority = 7)]
public static void CreateCone()
{
SpawnConeInHierarchy();
}
這里主要就是利用MenuItem特性來實現(xiàn)的,其中false表示該菜單不需要有效性驗證,priority=7控制菜單顯示的位置,可以參考這里: Unity擴展Hierachry的右鍵菜單
方便起見,我把圖貼下面:
接下來實現(xiàn)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”會變?yōu)檫x中項的子物體。
PS:這里有個Bug,如果同時選中了多個物體,又是采用的Hierarchy面板右鍵菜單的方式,那么會在每個選中物體下都生成與選中物體數(shù)量相同的子物體,見下圖。這個Bug應該不僅限于版本2017.3,因為我在網(wǎng)上有搜到一個同情況的帖子,時間是2016年8月。
目前這個Bug我已經(jīng)提交給官方確認了,他們已轉(zhuǎn)交給QA,不過不影響使用,避免辦法就是不用右鍵菜單,而是點擊菜單欄”GameObject”下的菜單。
到此為止,我們已經(jīng)擴展了編輯器菜單,但是生成出來的是空物體,接下來我們實現(xiàn)SetMesh方法以創(chuàng)建Mesh,讓圓錐體顯示出來。
創(chuàng)建Mesh
分兩部,首先繪出底部的圓。
繪制圓形底部
圓心已經(jīng)確定為原點,半徑為0.5f,圓上分布共20個點,那么每個點的坐標就可以用三角函數(shù)算出。
private static void SetMesh(GameObject go)
{
if (null == go)
return;
//仿Cylinder參數(shù)
float myRadius = 0.5f;
int myAngleStep = 20;
Vector3 myTopCenter = new Vector3(0, 1, 0);
Vector3 myBottomCenter = Vector3.zero;
//構(gòu)建頂點數(shù)組和UV數(shù)組
//每20度一個頂點,再加上圓心,得出頂點數(shù)組長度
Vector3[] myVertices = new Vector3[360 / myAngleStep + 1];
//因為uv數(shù)組和頂點數(shù)組是一一對應的,所以這里同時計算uv數(shù)組
Vector2[] myUV = new Vector2[myVertices.Length];
//將圓心作為第一個頂點,對應的uv設置為貼圖正中
myVertices[0] = myBottomCenter;
myUV[0] = new Vector2(0.5f, 0.5f);
//循環(huán)計算其他頂點坐標
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],所以要進行轉(zhuǎn)換
myUV[i] = new Vector2(curX + 0.5f, curZ + 0.5f);
}
接下來,構(gòu)建三角形索引數(shù)組,19個頂點,共18個三角形,所以數(shù)組長度是18 * 3 = 54。
int[] myTriangle = new int[(myVertices.Length - 1) * 3];
//每三個索引(即頂點數(shù)組中的頂點索引值)為一個三角形索引組
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;
//最后一個三角形時終點索引應為1
myTriangle[i + 2] = i + 2 == myTriangle.Length / 2 - 1 ? 1 : i / 3 + 2;
}
}
最后,分配mesh,賦值材質(zhì)后就可以看到一個圓形物體了。
//構(gòu)建mesh
Mesh myMesh = new Mesh();
myMesh.name = "Cone";
myMesh.vertices = myVertices;
myMesh.triangles = myTriangle;
myMesh.uv = myUV;
myMesh.RecalculateBounds();
myMesh.RecalculateNormals();
myMesh.RecalculateTangents();
//分配mesh
MeshFilter mf = go.AddComponent<MeshFilter>();
mf.mesh = myMesh;
//分配材質(zhì)
MeshRenderer mr = go.AddComponent<MeshRenderer>();
Material myMat = new Material(Shader.Find("Standard"));
mr.sharedMaterial = myMat;
因為底部沒光照,所以看起來是黑的,另外,上面的代碼是我從最終代碼中手動修改得到的,可能有錯誤,只是用于理解思路,完整代碼會在最后給出。
完善錐體
底部圓既然已經(jīng)繪制成功,錐體可以理解為將圓心上移即可,在頂點數(shù)量上,三角形索引數(shù)組上都相當于double了一份即可。
這里有個情況說明一下,我本來是想共用圓上頂點的,這樣整個錐體的頂點數(shù)就是20,但經(jīng)過測試是不可以的,我參考了Cube,頂點數(shù)是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參數(shù)
float myRadius = 0.5f;
int myAngleStep = 20;
Vector3 myTopCenter = new Vector3(0, 1, 0);
Vector3 myBottomCenter = Vector3.zero;
//構(gòu)建頂點數(shù)組和UV數(shù)組
Vector3[] myVertices = new Vector3[360 / myAngleStep * 2 + 2];
//
Vector2[] myUV = new Vector2[myVertices.Length];
//這里我把錐尖頂點放在了頂點數(shù)組最后一個
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);
//因為圓上頂點坐標相同,只是索引不同,所以這里循環(huán)一般長度即可
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);
}
//構(gòu)建三角形數(shù)組
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;
//錐體最后一個三角形的中間頂點索引值為19
myTriangle[i + 1] = i == myTriangle.Length - 3 ? 19 : i / 3 + 2;
myTriangle[i + 2] = i / 3 + 1;
}
}
//構(gòu)建mesh
Mesh myMesh = new Mesh();
myMesh.name = "Cone";
myMesh.vertices = myVertices;
myMesh.triangles = myTriangle;
myMesh.uv = myUV;
myMesh.RecalculateBounds();
myMesh.RecalculateNormals();
myMesh.RecalculateTangents();
//分配mesh
MeshFilter mf = go.AddComponent<MeshFilter>();
mf.mesh = myMesh;
//分配材質(zhì)
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;
//設置創(chuàng)建操作可撤銷
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;
//設置創(chuàng)建操作可撤銷
Undo.RegisterCreatedObjectUndo(cone, "Undo Creating Cone");
SetMesh(cone);
}
}
}
PS:這里的uv設置比較簡單,所以對貼圖也特定要求,不然圖片會比較扭曲,需要的朋友可以自行修改。
結(jié)果
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持我們。
上一篇:Unity實現(xiàn)大轉(zhuǎn)盤的簡單筆記
欄 目:C#教程
下一篇:C# Ado.net實現(xiàn)讀取SQLServer數(shù)據(jù)庫存儲過程列表及參數(shù)信息示例
本文地址:http://m.jygsgssxh.com/a1/C_jiaocheng/4908.html
您可能感興趣的文章
- 01-10C#自定義簽名章實現(xiàn)方法
- 01-10winform實現(xiàn)創(chuàng)建最前端窗體的方法
- 01-10WinForm實現(xiàn)自定義右下角提示效果的方法
- 01-10C#實現(xiàn)自定義windows系統(tǒng)日志的方法
- 01-10C#自定義事件監(jiān)聽實現(xiàn)方法
- 01-10C#編程實現(xiàn)自定義熱鍵的方法
- 01-10C#動態(tài)創(chuàng)建button的方法
- 01-10深入淺出23種設計模式
- 01-10winform創(chuàng)建不規(guī)則窗體的方法
- 01-10C#動態(tài)創(chuàng)建Access數(shù)據(jù)庫及密碼的方法


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當前操作系統(tǒng)已
隨機閱讀
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 01-10delphi制作wav文件的方法
- 04-02jquery與jsp,用jquery
- 01-10C#中split用法實例總結(jié)
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05織夢dedecms什么時候用欄目交叉功能?


