水族箱
1.工程創建
本次教程使用Unity3D 5.5.5f1版本
首先,打開工程,創建一個名爲Aquarium(水族館)的工程:
在工程中,創建Editor、Scenes、Scripts三個文件夾,分別用來放編輯工具、場景和遊戲腳本。
默認場景中,有主相機Main Camera和一個平行光Directional Light。先保存當前場景到Scenes,取名爲Entry。
2.創建水箱
爲實現一個水族箱遊戲,首先我們需要一個水族箱,魚的活動範圍,將在水族箱內。
在場景中創建一個空的GameObject,名字改爲Tank。
在Scripts文件夾中,創建一個名爲Tank的c#文件,水箱相關的邏輯將在寫在此腳本中。
雙擊Tank腳本,我們在visual studio中進行編輯。
Tank.cs作爲唯一管理水箱的腳本,我們將它寫成一個靜態的GameObject,之後掛在Tank GameObject上,並且以後可以方便的調用,將Tank的Transform用變量_tr記錄下來。
public class Tank : MonoBehaviour
{
private static Tank _instance;
public static Tank instance;
private Transform _tr;
private void Awake()
{
_instance = this;
_tr = transform;
}
}
添加三個float變量width、height、depth,分別表示水箱的寬度、高度、深度。
public float width;
public float height;
public float depth;
將Tank腳本掛在Tank GameObject上,設置Width爲50,Height爲14,Depth爲24。
此時,可以看到在場景中,並不能清楚的展現出水箱的情況,因此,在Tank腳本中,根據width、Height、Depth畫出當前水箱的輪廓。在OnDrawGizmos方法中可以將要觀察的信息繪製在場景中。注意,如果腳本在Inspector中被摺疊,OnDrawGizmos就不會被調用啦。
在OnDrawGizmos中,首先,只在腳本enable的情況下起作用。當_tr爲空的時候,重新爲_tr賦值。先記下Gizmos的顏色,並且設置新顏色爲綠色,在繪製完畢的時候,恢復Gizmos的顏色。
記錄Gizmos的Matrix4x4,賦值爲Tank Transform的localToWorldMatrix,這樣我們就可以繪製Tank Local空間的內容,而當Tank進行平移、旋轉、縮放操作時,Gizmos就會自行轉換到世界空間了。
使用Gizmos繪製線框立方體,中心位置爲local位置的center Vector3.zero,size爲變量width、height、depth值。
private void OnDrawGizmos()
{
if (this.enabled)
{
if (_tr == null)
{
_tr = this.transform;
}
//記錄顏色
Color c = Gizmos.color;
Gizmos.color = Color.green;
//記錄matrix
Matrix4x4 m = Gizmos.matrix;
//賦值爲transform的matrix
Gizmos.matrix = _tr.localToWorldMatrix;
//繪製線框立方體
Gizmos.DrawWireCube(Vector3.zero, new Vector3(width,height,depth));
//恢復matirx和color
Gizmos.matrix = m;
Gizmos.color = c;
}
}
現在,在場景中,就可以看到水箱在哪裏、有多大啦!
Tank.cs當前的完整代碼爲:
using UnityEngine;
public class Tank : MonoBehaviour {
private static Tank _instance;
public static Tank instance;
private Transform _tr;
public float width;
public float height;
public float depth;
private void Awake()
{
_instance = this;
_tr = transform;
}
private void OnDrawGizmos()
{
if (this.enabled)
{
if (_tr == null)
{
_tr = this.transform;
}
//記錄顏色
Color c = Gizmos.color;
Gizmos.color = Color.green;
//記錄matrix
Matrix4x4 m = Gizmos.matrix;
//賦值爲transform的matrix
Gizmos.matrix = _tr.localToWorldMatrix;
//繪製線框立方體
Gizmos.DrawWireCube(Vector3.zero, new Vector3(width,height,depth));
//恢復matirx和color
Gizmos.matrix = m;
Gizmos.color = c;
}
}
}
3.相機
使用透視相機(Main Camera的默認模式就是透視模式),並且調整相機的位置,使得水箱在相機範圍內。
相機Position的z值調整爲-40。Clipping Panels Far值調整爲100,因爲用不到1000那麼遠。效果如下圖所示。
當選中相機的時候,在scene中才能看到相機的位置和視野範圍,不是很直觀。在此,開發一個顯示相機視野範圍的小工具腳本。
在Scripts文件夾中創建名爲CameraOutline的c#腳本,雙擊打開編輯。
和繪製水箱輪廓一樣,在OnDrawGizmos中,繪製相機的視野範圍。
記錄Gizmos的顏色,並使用黃色進行繪製。
分別處理正交模式和透視模式兩種情況。
正交模式比較簡單,視野範圍是個立方體。需要注意的是,Camera繪製的前方實際上是Camera的back,而不是forward。相對於Camera的Local座標系中,中心點是(nearClipPanel+farClipPanel)*0.5。相機的寬高比爲aspect,而size爲視野範圍高度的一半。因此,相機的視野高度=orthographicSize*2,寬度=(視野高度*aspect)。深度=遠截面-近截面。然後,存儲Gizmos的matrix,賦值爲相機cameraToWorldMatrix,進行繪製,當相機有平移、旋轉、縮放的時候,不用我們再去進行計算了,因爲Gizmos使用的cameraToWorldMatrix已經爲我們計算過了。最後,恢復Gizmos的matrix。
正交模式代碼如下:
if (cam.orthographic)
{
Vector3 center = Vector3.back * (cam.nearClipPlane + cam.farClipPlane) * 0.5f;
Vector3 size = new Vector3(cam.orthographicSize * 2 * cam.aspect, cam.orthographicSize * 2, cam.farClipPlane - cam.nearClipPlane);
Matrix4x4 m = Gizmos.matrix;
Gizmos.matrix = cam.cameraToWorldMatrix;
Gizmos.DrawWireCube(center, size);
Gizmos.matrix = m;
}
透視模式相對複雜一些,我們需要分別獲得近截面的四個點和遠截面的四個點,最後通過繪製線的方式繪製出平截頭。但是,Gzimos給我們提供了DrawFrustum方法,只需要按參數傳入就好了。
Gizmos.matrix = cam.cameraToWorldMatrix;
// center是平截頭的頂端,即攝像機的位置。相對於自己是zero.
Vector3 center = Vector3.zero;
Gizmos.DrawFrustum(center, cam.fieldOfView, cam.farClipPlane, cam.nearClipPlane, cam.aspect);
然而,此時的結果,正好是和正確表現反向的。黃色是正確結果,藍色是反向的。
我們知道,肯定是和cameraToWorldMatrix中,z軸的方向相關,和世界座標系是相反方向的。
我們只需要改變matrix中z軸的方向就可以了。
已知,矩陣相乘可以看做是一個對另外一個矩陣進行平移、旋轉和縮放操作,因此,我們只需要對cameraToWorldMatrix進行z軸相反方向的縮放即可。
Matrix4x4 matrixCam = cam.cameraToWorldMatrix;
Matrix4x4 nagtiveZ = Matrix4x4.identity;
nagtiveZ.SetTRS(Vector3.zero, Quaternion.Euler(0, 0, 0), new Vector3(1, 1, -1));
Gizmos.matrix = matrixCam * nagtiveZ;
// center是平截頭的頂端,即攝像機的位置。相對於自己是zero.
Vector3 center = Vector3.zero;
Gizmos.DrawFrustum(center, cam.fieldOfView, cam.farClipPlane, cam.nearClipPlane, cam.aspect);
最終代碼如下:
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class CameraOutline : MonoBehaviour
{
private void OnDrawGizmos()
{
if (this.enabled)
{
Color c = Gizmos.color;
Gizmos.color = Color.yellow;
Camera cam = this.GetComponent<Camera>();
if (cam.orthographic)
{
Vector3 center = Vector3.back * (cam.nearClipPlane + cam.farClipPlane) * 0.5f;
Vector3 size = new Vector3(cam.orthographicSize * 2 * cam.aspect, cam.orthographicSize * 2, cam.farClipPlane - cam.nearClipPlane);
Matrix4x4 m = Gizmos.matrix;
Gizmos.matrix = cam.cameraToWorldMatrix;
Gizmos.DrawWireCube(center, size);
Gizmos.matrix = m;
}
else
{
Matrix4x4 m = Gizmos.matrix;
Matrix4x4 matrixCam = cam.cameraToWorldMatrix;
Matrix4x4 nagtiveZ = Matrix4x4.identity;
nagtiveZ.SetTRS(Vector3.zero, Quaternion.Euler(0, 0, 0), new Vector3(1, 1, -1));
Gizmos.matrix = matrixCam * nagtiveZ;
// center是平截頭的頂端,即攝像機的位置。相對於自己是zero.
Vector3 center = Vector3.zero;
Gizmos.DrawFrustum(center, cam.fieldOfView, cam.farClipPlane, cam.nearClipPlane, cam.aspect);
Gizmos.matrix = m;
}
Gizmos.color = c;
}
}
}
先到這裏~