Unity制作简单3D图表
轉載請注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),謝謝支持!
開篇廢話:
在大學時稍微自學過一段時間Unity3D,雖然現在在做安卓,但一直對游戲開發很感興趣,所以平時偶爾有空也會稍微看看,不過水平還是未入門菜鳥級的。
下面這個demo是看了雨松MOMO大神所寫的幾篇基礎文章后,寫的一個練習demo,用來展示簡易的3D圖表。
這個Demo非常初級,純粹是為了練習知識點,但是所應用到的知識點非常基礎,非常重要,適合初學者學習。截圖如下:
簡介:
這個Demo可以分為4個部分
第一部分——建立3D坐標系:
外框首先由四個平面所圍成的一個“3D坐標系”,和X、Y、Z三個“坐標”組成。
1.四個平面
這四個平面所用的就是系統自帶的cube,創建并調整cube的大小和方向組成上面的形狀。
2.創建坐標系prefab
創建一個預設,并將左平面,右平面和后平面添加到下平面中最后添加到預設里,這樣他們就變成了一個整體。如下圖所示:
3.調整prefab在世界中的位置和他的scale
由于要設立坐標點,所以需要使剛才建立的坐標系的零點對應世界坐標的零點。
在本例中:假設每個單位長度為1,X坐標需要分成12份(12個月)坐標系scale的x值為24(每兩份代表一個刻度)。Y軸代表百分比,分為5份(每份一個刻度)。Z軸同理。
通過上面的3個步驟,3D坐標系的樣子已經出來了,下面開始為這個坐標系添加“刻度名稱”。
第二部分——為坐標添加刻度名稱:
刻度名稱使用兩個知識點:
1.世界坐標轉換為屏幕坐標:
添加刻度名稱其實就是將世界坐標的坐標值(因為我們已經在上一步中將坐標系和世界坐標同步了)轉換為屏幕坐標值。
2.使用GUI.Label寫出坐標名稱:
通過剛才算出的屏幕坐標處使用GUI.Label函數寫出當前坐標點的名稱。
下面是本例中這個方法的代碼:
[csharp]view
plaincopyprint?
//isY代表是不是Y軸
voiddrawCoordinate(Vector3point,stringname,boolisY){
//將世界坐標轉換為屏幕坐標
Vector2position=camera.WorldToScreenPoint(point);
position=newVector2(position.x,position.y);
//設置刻度的大小和顏色
Vector2nameSize=GUI.skin.label.CalcSize(newGUIContent(name));
GUI.color=Color.yellow;
//根據X,Y軸的不同加上適當偏移量畫出刻度
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);
}
}
調用方法如下:
[csharp]view
plaincopyprint?
voidOnGUI()
{
drawCoordinate(newVector3(0,0,0),"0.00%",true);
drawCoordinate(newVector3(2,0,0),"Jan",false);
}
最后我貼上從網上找的關于Unity中坐標系的知識點(原文實在找不到了我就不貼連接了):
【Unity中四種坐標系】
1、World Space(世界坐標):
我們在場景中添加物體(如:Cube),他們都是以世界坐標顯示在場景中的。
transform.position可以獲得該位置坐標。
2、Screen Space(屏幕坐標):
以像素來定義的,以屏幕的左下角為(0,0)點,右上角為(Screen.width,Screen.height),Z的位置是以相機的世界單位來衡量的。
Screen.width = Camera.pixelWidth
Screen.height = Camera.pixelHeigth
鼠標位置坐標屬于屏幕坐標,Input.mousePosition可以獲得該位置坐標,
手指觸摸屏幕也為屏幕坐標,Input.GetTouch(0).position可以獲得單個手指觸摸屏幕坐標。
3、ViewPort Space(視口坐標):
視口坐標是標準的和相對于相機的。相機的左下角為(0,0)點,右上角為(1,1)點,Z的位置是以相機的世界單位來衡量的。
4、繪制GUI界面的坐標系:
這個坐標系與屏幕坐標系相似,不同的是該坐標系以屏幕的左上角為(0,0)點,右下角為(Screen.width,Screen.height)。
【四種坐標系的轉換】
1、世界坐標→屏幕坐標:
camera.WorldToScreenPoint(transform.position);
這樣可以將世界坐標轉換為屏幕坐標。其中camera為場景中的camera對象。
2、屏幕坐標→視口坐標:
camera.ScreenToViewportPoint(Input.GetTouch(0).position);
這樣可以將屏幕坐標轉換為視口坐標。其中camera為場景中的camera對象。
3、視口坐標→屏幕坐標:
camera.ViewportToScreenPoint();
4、視口坐標→世界坐標:
camera.ViewportToWorldPoint();
第三部分——在3D坐標系中畫折線
坐標系建立好以后我們就可以在這個坐標系中畫折線來啦。
坐標系已經和世界坐標系同步,所以每個坐標的坐標點我們已經知道,下面的問題就是如何將點連成線。
Unity提供了LineRenderer來畫線,可以通過如下兩種方法創建它:
一.Unity編輯器的方式:
1.Unity -> GameObject -> Create Empty 創建一個空的對象,我命名為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的詳細使用請參考雨松momo的文章:Unity3D研究院之游戲對象的訪問繪制線與繪制面詳解(十七)
第四部分——通過手勢/鼠標來放大、縮小、旋轉坐標系
簡單概括為兩個要點:
1.手勢和鼠標的識別
在Unity中手勢識別有一個插件:FingerGestures
在本例中當然不需要做的這么復雜,只需要判斷觸摸點來進行一些簡單計算就可以。
無論是Android應用還是Unity手勢判斷最常用的就是簡單的通過記錄之前的觸摸點位置和之后的觸摸點位置然后再進行計算。
計算觸摸點Unity提供如下幾個方法:
Input.touchCount可以判斷當前觸摸點的數量,所以可以通過這個方法來判斷是單點觸摸還是多點觸摸
Input.GetTouch(0).phase方法會返回一個表示當前觸摸類型的枚舉(TouchPhase)。
TouchPhase有如下幾種類型:
Began
手指已觸摸屏幕。
Moved
手指在屏幕上移動。
Stationary
手指觸摸屏幕,但并沒有移動。
Ended
手指從屏幕上移開。這是一個觸摸的最后狀態。
Canceled
系統取消跟蹤觸摸,如用戶把屏幕放到他臉上或超過五個接觸同時發生。這是一個觸摸的最后狀態。
更多關于Input的內容強烈建議去看Unity的腳本手冊,英文不好的可以去Unity圣典看中文的。
2.控制物體的放大,縮小,旋轉
在Unity中放大,縮小,旋轉某個物體其實是通過拉近,拉遠,旋攝像機來實現的(自動腦補)。
對于如何將觸摸判斷和對物體控制的結合我推薦看雨松momo的這篇文章——Unity3D研究院之IOS觸摸屏手勢控制鏡頭旋轉與縮放(八)
最后附上本例的這段代碼,雨松文章里用的是js的我這里給改成c#并且加上了鼠標滾輪的放大縮小:
[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(){
//由于要支持鼠標和觸摸,所以還需要加一個鼠標的計算
if(Input.GetMouseButton(0)){
//根據觸摸點計算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)
{
//根據觸摸點計算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||Input.GetTouch(1).phase==TouchPhase.Moved)
{
//計算出當前兩點觸摸點的位置
Vector2tempPosition1=Input.GetTouch(0).position;
Vector2tempPosition2=Input.GetTouch(1).position;
//函數返回真為放大,返回假為縮小
if(isEnlarge(oldPosition1,oldPosition2,tempPosition1,tempPosition2))
{
//放大系數超過3以后不允許繼續放大
//這里的數據是根據我項目中的模型而調節的,大家可以自己任意修改
if(distance>zoomMin)
{
distance-=0.5f;
}
}else
{
//縮小洗漱返回18.5后不允許繼續縮小
//這里的數據是根據我項目中的模型而調節的,大家可以自己任意修改
if(distance<zoomMax)
{
distance+=0.5f;
}
}
//備份上一次觸摸點的位置,用于對比
oldPosition1=tempPosition1;
oldPosition2=tempPosition2;
}
}
//鼠標滾輪
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){
//函數傳入上一次觸摸兩點的位置與本次觸摸兩點的位置計算出用戶的手勢
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){
//重置攝像機的位置
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月份的時候參加了一個網站的比賽,做了一個Unity和Android結合的3D語音的天氣預報(可惜沒獲獎),過幾天準備給它分享出來。因為我本來就是Unity菜鳥,而且好幾個月沒看了所以就拿這個demo先熱熱身。。。
PS:我Unity就會一點基礎,挺怕寫相關文章的,有錯請見諒并懇請指正。。。
總結
以上是生活随笔為你收集整理的Unity制作简单3D图表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 现在万科的房子一个月多少钱呢?我要想租个
- 下一篇: 南宁美丽南方可以燃放烟花吗