[Unity3D]水族館遊戲教程一

水族箱

1.工程創建

本次教程使用Unity3D 5.5.5f1版本

首先,打開工程,創建一個名爲Aquarium(水族館)的工程:

image-20200321174341501

在工程中,創建Editor、Scenes、Scripts三個文件夾,分別用來放編輯工具、場景和遊戲腳本。

image-20200321174801361

默認場景中,有主相機Main Camera和一個平行光Directional Light。先保存當前場景到Scenes,取名爲Entry。

image-20200321175051138

2.創建水箱

爲實現一個水族箱遊戲,首先我們需要一個水族箱,魚的活動範圍,將在水族箱內。

在場景中創建一個空的GameObject,名字改爲Tank。

image-20200321175600217

在Scripts文件夾中,創建一個名爲Tank的c#文件,水箱相關的邏輯將在寫在此腳本中。

image-20200321175904189

雙擊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。

image-20200321181620429

此時,可以看到在場景中,並不能清楚的展現出水箱的情況,因此,在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;
        }
    }

現在,在場景中,就可以看到水箱在哪裏、有多大啦!

image-20200321183829084

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那麼遠。效果如下圖所示。

image-20200321220519048

當選中相機的時候,在scene中才能看到相機的位置和視野範圍,不是很直觀。在此,開發一個顯示相機視野範圍的小工具腳本。

在Scripts文件夾中創建名爲CameraOutline的c#腳本,雙擊打開編輯。

image-20200321221008717

和繪製水箱輪廓一樣,在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);

然而,此時的結果,正好是和正確表現反向的。黃色是正確結果,藍色是反向的。

image-20200322172456423

我們知道,肯定是和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;
        }
    }
}

先到這裏~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章