Unity官方教程《Tanks》学习笔记(二)

本系列文章是根据官方视频教程而写下的学习笔记,原官方视频教程网址:https://unity3d.com/cn/learn/tutorials/s/tanks-tutorial

系列其他笔记传送门
Unity官方教程《Tanks》学习笔记(一)
Unity官方教程《Tanks》学习笔记(三)
Unity官方教程《Tanks》学习笔记(四)
Unity官方教程《Tanks》学习笔记(五)

一、创建坦克以及控制坦克

首先,在Models文件夹内找到Tank这个model,把它拖拽到Hierarchy内,我们在Tank的inspector视图中,对其层级进行修改,选择Players,并仅对当前对象修改。如下图所示:


image.png

image.png

接着,我们选中Hierarchy中的Tank,为其添加若干个Component,分别是:Rigidbody、Box Collider、Audio Source、Audio Source,并对这些部件进行设置如下:

Tank设置

然后,我们把配置好的Tank从Hierarchy拖拽到Prefabs文件夹下,让它成为一个预制件,这样以后我们可以重复利用该Tank,而不用每次都重新配置。然后保存当前场景。

因为整个游戏场景是在沙漠中的,所以坦克的行驶会有沙尘滚滚的效果,所以我们需要添加这一效果。在Prefabs文件夹内,把DustTrail预制件拖拽到Hierarchy下的Tank内,让其成为Tank的子对象,然后复制粘贴DustTrail,并分别重命名为LeftDustTrail和RightDustTrail,根据下面的官方教程,把两个DustTrail的position进行调节:


DustTrail

DustTrail设置

设置完毕后,接下来就是对Tank的移动脚本进行设置。在/Assets/Scripts/Tank文件夹内,找到TankMoveMent.cs文件,并把它拖拽到Hierarchy下的Tank内。我们打开并编辑该脚本,把里面的注释符号去掉,并添加逻辑如下:

using UnityEngine;

public class TankMovement : MonoBehaviour
{
    public int m_PlayerNumber = 1;         //游戏者的序号
    public float m_Speed = 12f;            //坦克移动速度
    public float m_TurnSpeed = 180f;       //坦克转向的角速度
    public AudioSource m_MovementAudio;    
    public AudioClip m_EngineIdling;       //静止的音效
    public AudioClip m_EngineDriving;      //移动的音效
    public float m_PitchRange = 0.2f;

    
    private string m_MovementAxisName;     
    private string m_TurnAxisName;         
    private Rigidbody m_Rigidbody;         
    private float m_MovementInputValue;    
    private float m_TurnInputValue;        
    private float m_OriginalPitch;         

    /**
     *  Scene加载的时候调用
     */
    private void Awake()
    {
        m_Rigidbody = GetComponent<Rigidbody>();
    }

    /**
     *  在Awake()之后,Update()之前调用
     */
    private void OnEnable ()
    {
        m_Rigidbody.isKinematic = false;
        m_MovementInputValue = 0f;
        m_TurnInputValue = 0f;
    }


    private void OnDisable ()
    {
        m_Rigidbody.isKinematic = true;
    }


    private void Start()
    {
        m_MovementAxisName = "Vertical" + m_PlayerNumber;
        m_TurnAxisName = "Horizontal" + m_PlayerNumber;

        m_OriginalPitch = m_MovementAudio.pitch;
    }


    private void Update()
    {
        // Store the player's input and make sure the audio for the engine is playing.
        m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
        m_TurnInputValue = Input.GetAxis (m_TurnAxisName);

        EngineAudio ();
    }


    private void EngineAudio()
    {
        // Play the correct audio clip based on whether or not the tank is moving and what audio is currently playing.
        // 如果坦克处于静止状态
        if (Mathf.Abs (m_MovementInputValue) < 0.1f && Mathf.Abs (m_TurnInputValue) < 0.1f)
        {
            //如果坦克播放的是行驶状态的音效,则替换
            if (m_MovementAudio.clip == m_EngineDriving)
            {
                // ... change the clip to idling and play it.
                m_MovementAudio.clip = m_EngineIdling;
                m_MovementAudio.pitch = Random.Range (m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
                m_MovementAudio.Play ();
            }
        }
        else
        {
            // 如果坦克播放的是静止状态的音效,则替换
            if (m_MovementAudio.clip == m_EngineIdling)
            {
                // ... change the clip to driving and play.
                m_MovementAudio.clip = m_EngineDriving;
                m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
                m_MovementAudio.Play();
            }
        }
    }

    /**
     *  以固定的时间间隔调用,用于物理上的步骤,比如行走、转向
     */
    private void FixedUpdate()
    {
        // Move and turn the tank.
        Move ();
        Turn ();
    }


    private void Move()
    {
        // Adjust the position of the tank based on the player's input.
        Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
        m_Rigidbody.MovePosition (m_Rigidbody.position + movement);
    }


    private void Turn()
    {
        // Adjust the rotation of the tank based on the player's input.
        float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;

        Quaternion turnRotation = Quaternion.Euler (0f,turn,0f);

        m_Rigidbody.MoveRotation (m_Rigidbody.rotation * turnRotation);
    }
}

修改完毕并保存文件,下一步我们需要初始化在脚本中声明的几个公有变量:Movement Audio、Engine Idling、Engine Driving:


配置

二、控制摄像机

首先在Hierarchy的根目录下创建一个空的GameObject,并重命名为“CameraRig”,修改其部分Transform数据如下:


CameraRig

接着,我们把Main Camera拖拽到CameraRig内,成为它的子对象,并修改Main Camera的Transform数据如下:


Main Camera

接下来我们需要补充一些关于摄像机的知识:

1、perspective视图和orthographic视图
要想了解如何控制摄像机,我们要首先知道摄像机的两种不同视图形式,上一章也有所提及:透视视图和正交视图,下面就用官方教程的一幅图来直观地解释:

左侧为透视视图,右侧为正交视图

2、正交摄像机的尺寸(Size)

正交摄像机的尺寸

调节Main Camera的size参数,如果size变小,那么可视范围变小且物体变大,有放大作用。而size变大,可视范围变大且物体变小,有缩小作用。

3、摄像机的长宽比(aspect)

aspect

那么接下来,我们的摄像机应该做些什么?
1、跟随坦克。

Follow the tanks

找出两辆坦克位置的中心点,把CameraRig移到该点。

2、调整摄像机的尺寸以适应坦克在屏幕上的位置。

摄像机视角

从上面补充的知识可以知道,正交摄像机的视图的长为Size,宽为Size * aspect。接着,在正交摄像机视角看来,坦克的运动可以分解为x轴和y轴的运动,这时,我们需要把坦克的坐标切换成摄像机视角的本地坐标。
官方教程

官方教程

从上面两幅图我们可以知道,size的选择有两种情况,分别是沿y轴方向(size1 = y);以及沿x轴方向,而x轴方向需要做一步计算,即size2 = x / aspect。接着比较这两个size的大小,用大的size值决定摄像机的缩放。当然了,这也需要考虑到另外一个tank的不同size值,总之,取最大的size值作为摄像机的缩放范围。

我们来看一下脚本是如何对摄像机进行控制的,打开/Assets/Scripts/Camera文件夹,选中CameraControl,把它拖拽到CameraRig中,而不是Main Camera。

using UnityEngine;

public class CameraControl : MonoBehaviour
{
    public float m_DampTime = 0.2f;                 //移动Camera到目的position的时间
    public float m_ScreenEdgeBuffer = 4f;           //确保Tanks不会在屏幕边界之外
    public float m_MinSize = 6.5f;                  //Camera的最小尺寸
    /*[HideInInspector]*/ public Transform[] m_Targets; //坦克,先把[HideInInspector]注释掉


    private Camera m_Camera;                        
    private float m_ZoomSpeed;                      
    private Vector3 m_MoveVelocity;                 
    private Vector3 m_DesiredPosition;              //需要移动到的位置


    private void Awake()
    {
        m_Camera = GetComponentInChildren<Camera>();
    }


    private void FixedUpdate()
    {
        Move();
        Zoom();
    }


    private void Move()
    {
        FindAveragePosition();

        /**
         * function Vector3.SmoothDamp(Vector3 current,Vector3 target
         *                 ,ref Vector3 currentVelocity,float smoothTime)
         *  @parameters
         *  current:当前的位置
         *  target:试图接近的位置
         *  currentVelocity:当前速度,这个值由你每次调用这个函数时修改
         *  smoothTime:到达目标的大约时间,较小的值将快速到达目标
         */ 
        transform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
    }


    private void FindAveragePosition()
    {
        Vector3 averagePos = new Vector3();
        int numTargets = 0;

        for (int i = 0; i < m_Targets.Length; i++)
        {
            //判断当前坦克是否已经不是激活状态(死亡),如果未激活,
            //则不需要跟随该坦克
            if (!m_Targets[i].gameObject.activeSelf)
                continue;

            averagePos += m_Targets[i].position;
            numTargets++;
        }

        if (numTargets > 0)
            averagePos /= numTargets;

        //CameraRig的Y position不会被改变
        averagePos.y = transform.position.y;

        m_DesiredPosition = averagePos;
    }


    private void Zoom()
    {
        //根据目标位置来计算合适的Size
        float requiredSize = FindRequiredSize();
        m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, requiredSize, ref m_ZoomSpeed, m_DampTime);
    }


    private float FindRequiredSize()
    {   
        //把目标位置的世界坐标转换成本地坐标
        Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);

        float size = 0f;

        for (int i = 0; i < m_Targets.Length; i++)
        {
            if (!m_Targets[i].gameObject.activeSelf)
                continue;

            //把坦克所在的位置转换成CameraRig的本地坐标
            Vector3 targetLocalPos = transform.InverseTransformPoint(m_Targets[i].position);

            //在CameraRig的本地坐标下,求出坦克与CameraRig的目标位置的距离
            Vector3 desiredPosToTarget = targetLocalPos - desiredLocalPos;

            size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.y));

            size = Mathf.Max (size, Mathf.Abs (desiredPosToTarget.x) / m_Camera.aspect);
        }
        //加上ScreenEdgeBuffer值,即坦克与屏幕边界的距离
        size += m_ScreenEdgeBuffer;

        size = Mathf.Max(size, m_MinSize);

        return size;
    }


    public void SetStartPositionAndSize()
    {
        FindAveragePosition();

        transform.position = m_DesiredPosition;

        m_Camera.orthographicSize = FindRequiredSize();
    }
}

然后,我们返回Unity,把Tank拖拽到如下位置:


拖拽Tank

最后,保存场景并运行。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容