Unity制作简单3D图表
轉(zhuǎn)載請注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),謝謝支持!
開篇廢話:
在大學(xué)時稍微自學(xué)過一段時間Unity3D,雖然現(xiàn)在在做安卓,但一直對游戲開發(fā)很感興趣,所以平時偶爾有空也會稍微看看,不過水平還是未入門菜鳥級的。
下面這個demo是看了雨松MOMO大神所寫的幾篇基礎(chǔ)文章后,寫的一個練習(xí)demo,用來展示簡易的3D圖表。
這個Demo非常初級,純粹是為了練習(xí)知識點,但是所應(yīng)用到的知識點非常基礎(chǔ),非常重要,適合初學(xué)者學(xué)習(xí)。截圖如下:
簡介:
這個Demo可以分為4個部分
第一部分——建立3D坐標(biāo)系:
外框首先由四個平面所圍成的一個“3D坐標(biāo)系”,和X、Y、Z三個“坐標(biāo)”組成。
1.四個平面
這四個平面所用的就是系統(tǒng)自帶的cube,創(chuàng)建并調(diào)整cube的大小和方向組成上面的形狀。
2.創(chuàng)建坐標(biāo)系prefab
創(chuàng)建一個預(yù)設(shè),并將左平面,右平面和后平面添加到下平面中最后添加到預(yù)設(shè)里,這樣他們就變成了一個整體。如下圖所示:
3.調(diào)整prefab在世界中的位置和他的scale
由于要設(shè)立坐標(biāo)點,所以需要使剛才建立的坐標(biāo)系的零點對應(yīng)世界坐標(biāo)的零點。
在本例中:假設(shè)每個單位長度為1,X坐標(biāo)需要分成12份(12個月)坐標(biāo)系scale的x值為24(每兩份代表一個刻度)。Y軸代表百分比,分為5份(每份一個刻度)。Z軸同理。
通過上面的3個步驟,3D坐標(biāo)系的樣子已經(jīng)出來了,下面開始為這個坐標(biāo)系添加“刻度名稱”。
第二部分——為坐標(biāo)添加刻度名稱:
刻度名稱使用兩個知識點:
1.世界坐標(biāo)轉(zhuǎn)換為屏幕坐標(biāo):
添加刻度名稱其實就是將世界坐標(biāo)的坐標(biāo)值(因為我們已經(jīng)在上一步中將坐標(biāo)系和世界坐標(biāo)同步了)轉(zhuǎn)換為屏幕坐標(biāo)值。
2.使用GUI.Label寫出坐標(biāo)名稱:
通過剛才算出的屏幕坐標(biāo)處使用GUI.Label函數(shù)寫出當(dāng)前坐標(biāo)點的名稱。
下面是本例中這個方法的代碼:
[csharp]view
plaincopyprint?
//isY代表是不是Y軸
voiddrawCoordinate(Vector3point,stringname,boolisY){
//將世界坐標(biāo)轉(zhuǎn)換為屏幕坐標(biāo)
Vector2position=camera.WorldToScreenPoint(point);
position=newVector2(position.x,position.y);
//設(shè)置刻度的大小和顏色
Vector2nameSize=GUI.skin.label.CalcSize(newGUIContent(name));
GUI.color=Color.yellow;
//根據(jù)X,Y軸的不同加上適當(dāng)偏移量畫出刻度
if(isY){
GUI.Label(newRect(position.x-nameSize.x-coordOffset,Screen.height-position.y,nameSize.x,nameSize.y),name);
}
else{
GUI.Label(newRect(position.x-nameSize.x,Screen.height-position.y,nameSize.x,nameSize.y),name);
}
}
調(diào)用方法如下:
[csharp]view
plaincopyprint?
voidOnGUI()
{
drawCoordinate(newVector3(0,0,0),"0.00%",true);
drawCoordinate(newVector3(2,0,0),"Jan",false);
}
最后我貼上從網(wǎng)上找的關(guān)于Unity中坐標(biāo)系的知識點(原文實在找不到了我就不貼連接了):
【Unity中四種坐標(biāo)系】
1、World Space(世界坐標(biāo)):
我們在場景中添加物體(如:Cube),他們都是以世界坐標(biāo)顯示在場景中的。
transform.position可以獲得該位置坐標(biāo)。
2、Screen Space(屏幕坐標(biāo)):
以像素來定義的,以屏幕的左下角為(0,0)點,右上角為(Screen.width,Screen.height),Z的位置是以相機(jī)的世界單位來衡量的。
Screen.width = Camera.pixelWidth
Screen.height = Camera.pixelHeigth
鼠標(biāo)位置坐標(biāo)屬于屏幕坐標(biāo),Input.mousePosition可以獲得該位置坐標(biāo),
手指觸摸屏幕也為屏幕坐標(biāo),Input.GetTouch(0).position可以獲得單個手指觸摸屏幕坐標(biāo)。
3、ViewPort Space(視口坐標(biāo)):
視口坐標(biāo)是標(biāo)準(zhǔn)的和相對于相機(jī)的。相機(jī)的左下角為(0,0)點,右上角為(1,1)點,Z的位置是以相機(jī)的世界單位來衡量的。
4、繪制GUI界面的坐標(biāo)系:
這個坐標(biāo)系與屏幕坐標(biāo)系相似,不同的是該坐標(biāo)系以屏幕的左上角為(0,0)點,右下角為(Screen.width,Screen.height)。
【四種坐標(biāo)系的轉(zhuǎn)換】
1、世界坐標(biāo)→屏幕坐標(biāo):
camera.WorldToScreenPoint(transform.position);
這樣可以將世界坐標(biāo)轉(zhuǎn)換為屏幕坐標(biāo)。其中camera為場景中的camera對象。
2、屏幕坐標(biāo)→視口坐標(biāo):
camera.ScreenToViewportPoint(Input.GetTouch(0).position);
這樣可以將屏幕坐標(biāo)轉(zhuǎn)換為視口坐標(biāo)。其中camera為場景中的camera對象。
3、視口坐標(biāo)→屏幕坐標(biāo):
camera.ViewportToScreenPoint();
4、視口坐標(biāo)→世界坐標(biāo):
camera.ViewportToWorldPoint();
第三部分——在3D坐標(biāo)系中畫折線
坐標(biāo)系建立好以后我們就可以在這個坐標(biāo)系中畫折線來啦。
坐標(biāo)系已經(jīng)和世界坐標(biāo)系同步,所以每個坐標(biāo)的坐標(biāo)點我們已經(jīng)知道,下面的問題就是如何將點連成線。
Unity提供了LineRenderer來畫線,可以通過如下兩種方法創(chuàng)建它:
一.Unity編輯器的方式:
1.Unity -> GameObject -> Create Empty 創(chuàng)建一個空的對象,我命名為line。
2.然后點擊 Component -> Effects-> Line Renderer 給line添加一個線渲染器的屬性
二.腳本的方式:
下面是本例的使用代碼:
[csharp]view
plaincopyprint?
publicclasslineScript1:MonoBehaviour{
privateColorc1=Color.red;
privateColorc2=Color.red;
privateLineRendererlineRenderer;
privateVector3v0=newVector3(0.0f,0.0f,3.0f);
privateVector3v1=newVector3(2.0f,2.0f,0.0f);
privateVector3v2=newVector3(4.0f,3.0f,1.0f);
privateVector3v3=newVector3(10.0f,4.0f,0.0f);
privateVector3v4=newVector3(15.0f,6.0f,2.0f);
privateVector3v5=newVector3(20.0f,8.6f,0.0f);
voidStart(){
lineRenderer=lineRenderer=gameObject.AddComponent<LineRenderer>();
lineRenderer.SetColors(c1,c2);
lineRenderer.SetWidth(0.2f,0.2f);
lineRenderer.SetVertexCount(6);
}
voidUpdate(){
lineRenderer.SetPosition(0,v0);
lineRenderer.SetPosition(1,v1);
lineRenderer.SetPosition(2,v2);
lineRenderer.SetPosition(3,v3);
lineRenderer.SetPosition(4,v4);
lineRenderer.SetPosition(5,v5);
}
}
LineRenderer的詳細(xì)使用請參考雨松momo的文章:Unity3D研究院之游戲?qū)ο蟮脑L問繪制線與繪制面詳解(十七)
第四部分——通過手勢/鼠標(biāo)來放大、縮小、旋轉(zhuǎn)坐標(biāo)系
簡單概括為兩個要點:
1.手勢和鼠標(biāo)的識別
在Unity中手勢識別有一個插件:FingerGestures
在本例中當(dāng)然不需要做的這么復(fù)雜,只需要判斷觸摸點來進(jìn)行一些簡單計算就可以。
無論是Android應(yīng)用還是Unity手勢判斷最常用的就是簡單的通過記錄之前的觸摸點位置和之后的觸摸點位置然后再進(jìn)行計算。
計算觸摸點Unity提供如下幾個方法:
Input.touchCount可以判斷當(dāng)前觸摸點的數(shù)量,所以可以通過這個方法來判斷是單點觸摸還是多點觸摸
Input.GetTouch(0).phase方法會返回一個表示當(dāng)前觸摸類型的枚舉(TouchPhase)。
TouchPhase有如下幾種類型:
Began
手指已觸摸屏幕。
Moved
手指在屏幕上移動。
Stationary
手指觸摸屏幕,但并沒有移動。
Ended
手指從屏幕上移開。這是一個觸摸的最后狀態(tài)。
Canceled
系統(tǒng)取消跟蹤觸摸,如用戶把屏幕放到他臉上或超過五個接觸同時發(fā)生。這是一個觸摸的最后狀態(tài)。
更多關(guān)于Input的內(nèi)容強(qiáng)烈建議去看Unity的腳本手冊,英文不好的可以去Unity圣典看中文的。
2.控制物體的放大,縮小,旋轉(zhuǎn)
在Unity中放大,縮小,旋轉(zhuǎn)某個物體其實是通過拉近,拉遠(yuǎn),旋攝像機(jī)來實現(xiàn)的(自動腦補(bǔ))。
對于如何將觸摸判斷和對物體控制的結(jié)合我推薦看雨松momo的這篇文章——Unity3D研究院之IOS觸摸屏手勢控制鏡頭旋轉(zhuǎn)與縮放(八)
最后附上本例的這段代碼,雨松文章里用的是js的我這里給改成c#并且加上了鼠標(biāo)滾輪的放大縮小:
[csharp]view
plaincopyprint?
publicclasscontroll:MonoBehaviour{
publicTransformtarget;
privatefloatdistance=50.0f;
privatefloatxSpeed=250.0f;
privatefloatySpeed=120.0f;
privateintyMinLimit=-20;
privateintyMaxLimit=80;
privatefloatx=0.0f;
privatefloaty=0.0f;
privateintMouseWheelSensitivity=5;
privateintzoomMin=10;
privateintzoomMax=50;
privateVector2oldPosition1;
privateVector2oldPosition2;
voidStart(){
Vector3angles=transform.eulerAngles;
x=angles.y;
y=angles.x;
}
voidUpdate(){
//由于要支持鼠標(biāo)和觸摸,所以還需要加一個鼠標(biāo)的計算
if(Input.GetMouseButton(0)){
//根據(jù)觸摸點計算X與Y位置
if(Mathf.Abs(Input.GetAxis("MouseX")*xSpeed*0.02f)<50){
x+=Input.GetAxis("MouseX")*xSpeed*0.02f;
}
if(Mathf.Abs(Input.GetAxis("MouseY")*ySpeed*0.02f)<50){
y-=Input.GetAxis("MouseY")*ySpeed*0.02f;
}
}
if(Input.touchCount==1)
{
//觸摸類型為移動觸摸
if(Input.GetTouch(0).phase==TouchPhase.Moved)
{
//根據(jù)觸摸點計算X與Y位置
if(Mathf.Abs(Input.GetAxis("MouseX")*xSpeed*0.02f)<50){
x+=Input.GetAxis("MouseX")*xSpeed*0.02f;
}
if(Mathf.Abs(Input.GetAxis("MouseY")*ySpeed*0.02f)<50){
y-=Input.GetAxis("MouseY")*ySpeed*0.02f;
}
}
}
//判斷觸摸數(shù)量為多點觸摸
if(Input.touchCount>1)
{
//前兩只手指觸摸類型都為移動觸摸
if(Input.GetTouch(0).phase==TouchPhase.Moved||Input.GetTouch(1).phase==TouchPhase.Moved)
{
//計算出當(dāng)前兩點觸摸點的位置
Vector2tempPosition1=Input.GetTouch(0).position;
Vector2tempPosition2=Input.GetTouch(1).position;
//函數(shù)返回真為放大,返回假為縮小
if(isEnlarge(oldPosition1,oldPosition2,tempPosition1,tempPosition2))
{
//放大系數(shù)超過3以后不允許繼續(xù)放大
//這里的數(shù)據(jù)是根據(jù)我項目中的模型而調(diào)節(jié)的,大家可以自己任意修改
if(distance>zoomMin)
{
distance-=0.5f;
}
}else
{
//縮小洗漱返回18.5后不允許繼續(xù)縮小
//這里的數(shù)據(jù)是根據(jù)我項目中的模型而調(diào)節(jié)的,大家可以自己任意修改
if(distance<zoomMax)
{
distance+=0.5f;
}
}
//備份上一次觸摸點的位置,用于對比
oldPosition1=tempPosition1;
oldPosition2=tempPosition2;
}
}
//鼠標(biāo)滾輪
if(Input.GetAxis("MouseScrollWheel")!=0)
{
if(distance>=zoomMin&&distance<=zoomMax)
{
distance-=Input.GetAxis("MouseScrollWheel")*MouseWheelSensitivity;
}
if(distance<zoomMin)
{
distance=zoomMin;
}
if(distance>zoomMax)
{
distance=zoomMax;
}
}
}
publicboolisEnlarge(Vector2oP1,Vector2oP2,Vector2nP1,Vector2nP2){
//函數(shù)傳入上一次觸摸兩點的位置與本次觸摸兩點的位置計算出用戶的手勢
floatleng1=Mathf.Sqrt((oP1.x-oP2.x)*(oP1.x-oP2.x)+(oP1.y-oP2.y)*(oP1.y-oP2.y));
floatleng2=Mathf.Sqrt((nP1.x-nP2.x)*(nP1.x-nP2.x)+(nP1.y-nP2.y)*(nP1.y-nP2.y));
if(leng1<leng2)
{
//放大手勢
returntrue;
}else
{
//縮小手勢
returnfalse;
}
}
voidLateUpdate(){
if(target){
//重置攝像機(jī)的位置
y=ClampAngle(y,yMinLimit,yMaxLimit);
Quaternionrotation=Quaternion.Euler(y,x,0);
Vector3position=rotation*newVector3(0.0f,0.0f,-distance)+target.position;
transform.rotation=rotation;
transform.position=position;
}
}
publicfloatClampAngle(floatangle,floatmin,floatmax){
if(angle<-360)
angle+=360f;
if(angle>360)
angle-=360f;
returnMathf.Clamp(angle,min,max);
}
}
寫在最后:
這篇文章由于十分簡單,而且我覺得邏輯講的也算清楚,代碼也貼了幾段,所以源碼我就不上傳了。
寫這個Demo的目的其實是為了下一篇文章,我在9月份的時候參加了一個網(wǎng)站的比賽,做了一個Unity和Android結(jié)合的3D語音的天氣預(yù)報(可惜沒獲獎),過幾天準(zhǔn)備給它分享出來。因為我本來就是Unity菜鳥,而且好幾個月沒看了所以就拿這個demo先熱熱身。。。
PS:我Unity就會一點基礎(chǔ),挺怕寫相關(guān)文章的,有錯請見諒并懇請指正。。。
總結(jié)
以上是生活随笔為你收集整理的Unity制作简单3D图表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 现在万科的房子一个月多少钱呢?我要想租个
- 下一篇: 怎么创建具有真实纹理的CG场景岩石?