[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;
        }
    }
}

先到这里~

猜你喜欢

转载自blog.csdn.net/yanchezuo/article/details/105035351