Unity脚本基础

脚本的生命周期

在这里插入图片描述
Awake:
作用:充当构造函数,初始化数据成员
时机:游戏物体加载 --> 立即执行(仅一次)

Start:
时机:游戏物体加载 --> 等待脚本启用 --> 才执行(仅一次)

FixeUpdate:
作用:对物体执行物理操作(移动、旋转、施加力…)
时机:固定时间(默认0.02s)执行一次

Update:
作用:执行游戏逻辑
时机:每渲染帧(大概0.02s)执行一次

LateUpdate:
作用:适合执行跟随Updae移动的代码
时机:在Update之后执行

/// <summary>
/// 生命周期、消息、可重写函数、必然事件
/// </summary>
public class Lifecycle : MonoBehaviour
{
    /*
     c# 类
     {
         字段
         属性
         构造函数
         方法
     } 
  
     脚本
     {
          private/public 字段 
          方法
     } 
      
     */

    //字段直接初始化(在构造函数中进行,调用Unity方法会产生异常)
    //本质:跨线程访问被禁止
    //解决方案:放到Start 或 Awake中
    //private int a = Random.Range(1, 101);
     
    private string a = "abc";

    [Range(1,100)]
    public int b = 10;

    //在编译器中显示当前字段
    [SerializeField]
    private bool c = true;

    //在编译器中隐藏当前字段
    [HideInInspector]
    public float d = 1.0f;

    //***************生命周期*********************
    //Unity脚本 从唤醒 到 最后销毁的过程。
    //必然事件:当满足某种条件自动执行的方法。
    //重点:执行时机、作用、方法名称

   
    //(前提:物体启用、脚本启用)Unity 引擎会在一开始先调用所有对象的Awake  再调用 所有对象的 Start
    //游戏物体加载    --->    立即执行(仅1次)
    //作用:充当构造函数,初始化数据成员。
    //         如果初始化有明确的先后顺序,需要先执行的放到Awake中,后执行的放到Start中
    private void Awake()
    {
        //Debug.Log(this.name + ":Awake:Lifecycle");
    }
    //游戏物体加载   ---> 等待脚本启用  --> 才执行(仅1次)
    private void Start()
    {
        //Debug.Log(this.name + ":Start:" + Time.time); 
    } 
    private void OnMouseDown()
    {
        Debug.Log("执行喽");
    }

    //固定更新
    //固定时间(默认0.02s)  执行 1次
    //作用:对物体执行物理操作(移动、旋转、施加力……)
    //
    private void FixedUpdate()
    {
        //向前移动1m
    } 
    //每渲染帧(大概0.02s)   执行1次
    //作用:执行游戏逻辑
    //执行时机:渲染帧执行,执行间隔不同
    private void Update()
    {//渲染时间不固定(每帧渲染量不同、 机器性能不同)

        //向前移动1m
        
        //查看某一帧程序执行情况
        //启动调试 F5  -->  运行场景  -->  暂停  -->  在可能出错的行加断点  -->  单帧运行 --> 调试……  --> 停止调试 Shift + F5
        int a = 1;
        int b = 2;
        int c = a + b;
        //调式过程中,输入代码:
        //右键---快速监视
        //查看“即时窗口” 
    } 
    //延迟更新:在Update之后执行
    //作用:适合执行跟随Update移动的代码
    private void LateUpdate()
    { 
    
    }

 
}

脚本的核心类图

在这里插入图片描述
我们自己写的脚本继承于MonoBehaviour类,所以我们可以直接使用Behaviour类、Component类、object类的内容。

Compoent:

作用:提供了查找组件的功能(从自身、从后代、从先辈)
常见方法:GetCompoent、GetCompoents、GetCompoentsInChildren、GetCompoentsInChildren、GetCompoentsInParent
常见属性:gameObject、tranform、collider、renderer

Transform:

作用:提供了查找物体(根据名字、索引获取子物体)、移动、旋转、缩放物体的功能
常见方法:Find、GetChild、Lookat 、Translate、Rotate
常见属性:position、localPosition、parent、forward

GameObject:

作用: 创建游戏对象、给游戏对象增加组件、禁用/启用游戏对象、以及通过标签查找物体(单个、所有)和通过名称找物体(慎用)
常见方法:AddCompoent、Find、FindGameObjectWithTag
常见属性:transform、activeInHierarchy、activeSelf、tag

Object:

常见方法:Instantiate、Destory、FindObjectOfType、FindObjectOfType
常见属性:name

案例:

1.查找血量最低的敌人

2.查找距离最近的敌人

FindEnemyByMinHP()函数常见需要注意,替换的是引用

public class FindEnemyDemo : MonoBehaviour
{
    private void OnGUI()
    {
        if (GUILayout.Button("查找血量最低的敌人"))
        {
            //1.通过标签获取敌人
            //GameObject[] allEnemyGO = GameObject.FindGameObjectsWithTag("Enemy");
            //Enemy[] allEnemy = new Enemy[allEnemyGO.Length];
            //for (int i = 0; i < allEnemyGO.Length; i++)
            //{
            //    allEnemy[i] = allEnemyGO[i].GetComponent<Enemy>();
            //}

            //2.通过类型获取敌人
            //查找场景中所有Enemy类型的引用
            Enemy[] allEnemy = FindObjectsOfType<Enemy>();
            //获取血量最低的对象引用
            Enemy min = FindEnemyByMinHP(allEnemy);
            //根据Enemy类型引用 获取其他类型的引用
            min.GetComponent<MeshRenderer>().material.color = Color.red;
        }
        if (GUILayout.Button("查找距离最近的敌人"))
        {
            Enemy[] allEnemy = FindObjectsOfType<Enemy>();
            Enemy minDistance = FindeEnemyByMinDistance(allEnemy,transform);
            minDistance.GetComponent<MeshRenderer>().material.color = Color.red;
        }
    }

    public Enemy FindEnemyByMinHP(Enemy[] enemys)
    {
        //假设第一个就是血量最低的敌人
        Enemy min = enemys[0];
        //依次与后面比较
        for (int i = 1; i < enemys.Length; i++)
        {
            if (min.HP > enemys[i].HP)
                min = enemys[i];
        }
        return min;
    } 

    //练习2:获取最近的敌人
    //float distance = Vector3.Distance(物体1.位置,物体2.位置);
    public Enemy FindeEnemyByMinDistance(Enemy[] enemys,Transform targetTF)
    {
        Enemy min = enemys[0];
        float minDistance =  Vector3.Distance(min.transform.position,targetTF.position);
         
        for (int i = 1; i < enemys.Length; i++)
        {
            float distance = Vector3.Distance(enemys[i].transform.position,targetTF.position);
            if (minDistance > distance)
            {
                min = enemys[i];
                minDistance = distance;
            }
        }

        return min;
    }

}

3.在层级未知的情况下查找子物体
childTF = GetChild(parentTF.GetChild(i), childName);使用了递归的思想,使得在函数内部调用函数

/// <summary>
/// 变换组件助手类
/// </summary>
public class TransformHelper
{
    /// <summary>
    /// 在层级未知情况下查找子物体
    /// </summary>
    /// <param name="parentTF">父物体变换组件</param>
    /// <param name="childName">子物体名称</param>
    /// <returns></returns>
    public static Transform GetChild(Transform parentTF, string childName)
    {
        //在子物体中查找
        Transform childTF = parentTF.Find(childName);
        if(childTF != null) return childTF;

        //将问题交给子物体
        int count = parentTF.childCount;
        for(int i = 0; i < count; i++)
        {
            childTF = GetChild(parentTF.GetChild(i), childName);
            if (childTF != null)
                return childTF;
        }
        return null;
    }

	

}

Time类

time: 从游戏开始到现在所用时间。
timeScale: 时间缩放。
deltaTime: 表示每帧的经过时间。
unscaledDeltaTime: 不受缩放时间影响的每帧经过的时间。

总结:如果在Update中对物体做物理操作(移动/旋转/力) 需要在速度上 乘以 每帧消耗时间,用以得到恒定的速度。

using UnityEngine;
using System.Collections;

/// <summary>
/// 
/// </summary>
public class TimeDemo : MonoBehaviour
{ 
    public float speed = 10;
    private void OnGUI()
    {
        if (GUILayout.Button("游戏暂停"))
        {
            Time.timeScale = 0;
        }
        if (GUILayout.Button("游戏继续"))
        {
            Time.timeScale = 1;
        }
        if (GUILayout.Button("慢动作"))
        {
            Time.timeScale = 0.1f;
        }
        /*
         Time.timeScale 
         不影响渲染  所以 Update 执行间隔不受影响
         影响物理更新 所以FidexUpdate执行间隔受间隔
         Time.deltaTime  受影响
         */
    }

    public float deltaTime, unscaledDeltaTime, time, unscaledTime;
    private void Update()
    { 
        //本帧时间  -   上一帧时间
        //上一帧消耗时间
        deltaTime = Time.deltaTime;

        unscaledDeltaTime = Time.unscaledDeltaTime;

        time = Time.time;

        unscaledTime = Time.unscaledTime;

        //机器性能差/渲染量大    每帧执行间隔大   每秒渲染次数少(Update执行次数少)  希望 每次多旋转
        //机器性能好/渲染量小    每帧执行间隔小   每秒渲染次数多(Update执行次数多)  希望 每次少旋转
          
        //每帧沿Y轴旋转1度
        //transform.Rotate(0, 1, 0);

        //如果Update执行次数少  那么deltaTime数值大
        //                              多                               小
        //总结:如果在Update中对物体做物理操作(移动/旋转/力)
        //         需要在速度上 乘以 每帧消耗时间,用以得到恒定的速度。
        //transform.Rotate(0, speed * Time.deltaTime, 0);

        //如果希望游戏暂停后,当前物体不受影响
        transform.Rotate(0, speed * Time.unscaledDeltaTime, 0);
    }

    private void FixedUpdate()
    {  
        transform.Rotate(0, speed * Time.fixedDeltaTime, 0);
    }
 
}

案例(制作倒计时预制件):

1.使用Text制作倒计时预制件 。从 02:00 开始,最后 10 秒字体为红色,时间为 00:00 后停止计时。

预制体:
如果单独修改实例的属性值,则该值不在随预制体变化
Revert键:放弃实例属性值,还原预制体属性值
Apply键:将某一实例的修改应用到所有实例。

using UnityEngine;
using System.Collections;
using UnityEngine.UI;

/// <summary>
/// 倒计时器
/// </summary>
public class CountdownTimer : MonoBehaviour
{
    public int second = 120;

    private Text txtTimer;
    private void Start()
    {
        txtTimer = GetComponent<Text>();
         
        //重复调用(要执行的方法名称,开始调用时间,调用间隔)
        InvokeRepeating("Timer", 1, 1); 
        //延迟调用
        //Invoke("需要调用的方法名称", 调用时间);
    }

    private float nextTime = 1;//下一次改变时间
    private void Update()
    {
        //如果(按住鼠标左键 &&  nextTime <= Time.time)
        // 则发射子弹   nextTime = Time.time + 0.1f;
         
        //如果(按住鼠标左键)
        //totalTime += Time.deltaTime;
        // if(totalTime >=0.1f)
        // 则发射子弹  totalTime = 0;

        //沿多个路点移动,到达目标等待一段时间。
        //如果到达目标点
        //totalTime += Time.deltaTime;
        // if(totalTime >=3)
        // 设置目标点   totalTime = 0
          
        //Timer();
    }
     
    //Time.time 实现
    //试用场合:发射子弹
    private void Timer1()
    {
        //如果时间到了 
        if (nextTime <= Time.time)
        {
            second--;//119       1:59 
            txtTimer.text = string.Format("{0:d2}:{1:d2}", second / 60, second % 60);
            nextTime = Time.time + 1;//在当前时间上增加1s

            if (second <= 10) txtTimer.color = Color.red;
            if (second <= 0) enabled = false;
        }
    }

    //Time.deltaTime 实现
    //试用场合:沿多个路点移动,每次到达路点等待一段时间。
    private float totalTime = 0;
    private void Timer2()
    {
        //累加每帧消耗时间
        totalTime += Time.deltaTime;

        //如果1s
        if (totalTime>=1)
        {
            second--;//119       1:59 
            txtTimer.text = string.Format("{0:d2}:{1:d2}", second / 60, second % 60);
            totalTime = 0;//清空累加的时间
        }
    }

    //InvokeRepeating 实现
    //试用场合:计时器。每间隔固定时间 执行1次
    private void Timer()
    {
        second--;//119       1:59 
        txtTimer.text = string.Format("{0:d2}:{1:d2}", second / 60, second % 60);

        if (second <= 0)
            CancelInvoke("Timer");//取消调用
    }
}

Animation

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

录制动画片段步骤

1.点击录制按钮,开始录制动画
2.添加关键帧Add Property,选择组件类型
3. 选择关键帧,调整时间点。
4. 在 Scene 或 Inspector 面板设置属性。
5. 点击录制按钮,结束录制动画。

Animation常见API函数

bool isPlay = animation.IsPlaying(“动画名”);
animation.Play(“动画名”);
animation.CrossFade("动画名”);

动画片段属性

播放一次 Once,播放到头后停止;
循环播放 Loop,播放到头后再重头播放;
乒乓播放 PingPong,播放到头后再反向播放;
固定永久 Clamp Forever,播放到头后永远播放最后一帧;

Play和CrossFade的区别:
Play:直接切换动画,如果人物之前处于倾斜跑步状态,则会立即变成站立状态,表现上比较不真实,特别是当两个动画姿势差别较大时。
CrossFade:通过动画融合来切换动画,第二个参数可以指定融合的时间,如果人物之前处于倾斜跑步状态,则会在指定的融合时间内逐渐变成站立状态,表现上接近真实的人物动作切换效果。

Input类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

using UnityEngine;
using System.Collections;

/// <summary>
/// 
/// </summary>
public class CameraZoom : MonoBehaviour
{
    //同时按下A   +    B
    private void Update1()
    {
        if (Input.GetKey(KeyCode.A) && Input.GetKeyDown(KeyCode.B))
            Debug.Log("按了");
    }

    private Camera camera;
    private void Start()
    {
        //camera = GetComponent<Camera>();
        camera = Camera.main;//GameObject.FindWithTag("MainCamera")
    }

    private void Update()
    {
        Zoom();
    }

    private bool isFar = true;
    private void Zoom1()
    {
        if (Input.GetMouseButtonDown(1))
        {
            //修改缩放等级
            isFar = !isFar;
            if (isFar)
            {
                //拉远 20  --》 60
                camera.fieldOfView = 60;
            }
            else
            {
                //拉近 60 --》 20
                camera.fieldOfView = 20;
            }
        }
    }

    private void Zoom2()
    {
        if (Input.GetMouseButtonDown(1))
        {
            //修改缩放等级
            isFar = !isFar; 
        } 
        if (isFar)
        {
            //拉远 20  --》 60
            if (camera.fieldOfView < 60)
                camera.fieldOfView += 2;
        }
        else
        {
            //拉近 60 --》 20
            if (camera.fieldOfView > 20)
                camera.fieldOfView -= 2;
        }
    }

    private void Zoom3()
    {
        if (Input.GetMouseButtonDown(1))
        {
            //修改缩放等级
            isFar = !isFar;
        }
        if (isFar)
        {
            //拉远 20  --》 60        Lerp(起点、终点、比例)
            camera.fieldOfView = Mathf.Lerp(camera.fieldOfView, 60, 0.1f);
            //Vector3.Lerp
            //Quaternion.Lerp
            //Color.Lerp
        }
        else
        {
            //拉近 60 --》 20
            camera.fieldOfView = Mathf.Lerp(camera.fieldOfView, 20, 0.1f);
        }
    }

    //60    50  40  30    20
    public float[] zoomLevels;
    private int currentLevel;
    private void Zoom()
    {
        if (Input.GetMouseButtonDown(1))
        {
            //修改缩放等级
            //currentLevel++;
            //currentLevel = currentLevel < zoomLevels.Length - 1 ? currentLevel + 1 : 0;
            currentLevel = (currentLevel + 1) % zoomLevels.Length;
        }
        camera.fieldOfView = Mathf.Lerp(camera.fieldOfView, zoomLevels[currentLevel], 0.1f); 
    }
}

注意:
(1) GetMouseButtonDown只在用户按下第一帧的时候返回true, ,所以注意在Zoom2()中若只按下一次鼠标右键,状态标志位只改变一次,但是拉远或拉近的操作在update中每帧都会执行。
(2)对于需求3而言,当类型是确定的,个数是不确定的时候,我们考虑数组。
(3)zoom()中缩放等级的确定注意有两种不同的写法。
(4)建议在Update中监测用户的输入(可持续检测

InputManager(输入管理器)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

using UnityEngine;
using System.Collections;

/// <summary>
/// 鼠标控制相机旋转
/// </summary>
public class DoRotation : MonoBehaviour
{
    private void Update()
    {
        float x = Input.GetAxis("Mouse X");
        float y = Input.GetAxis("Mouse Y");

        if (x != 0 || y != 0)
            RotateView(x, y);

        //需要限制沿X轴旋转角度
    }

    public float speed = 10;
    private void RotateView(float x, float y)
    {
        x *= speed * Time.deltaTime;
        y *= speed * Time.deltaTime;

        transform.Rotate(-y, 0, 0);
        transform.Rotate(0, x, 0, Space.World); 
    }
 
}

注意: transform.Rotate(x,y,z)中x:绕x轴旋转的角度,y:绕y轴旋转的角度,z:绕z轴旋转的角度。

using UnityEngine;
using System.Collections;

/// <summary>
/// 控制飞机的移动
/// </summary>
public class PlayerController : MonoBehaviour 
{
    private void Update()
    {
        float hor = Input.GetAxis("Horizontal");
        float ver = Input.GetAxis("Vertical");

        if (hor != 0 || ver != 0)
            Movement(hor, ver);
    }

    public float moveSpeed = 10;
    private void Movement(float hor, float ver)
    {
        hor *= moveSpeed * Time.deltaTime;
        ver *= moveSpeed * Time.deltaTime;

        //限制
        Vector3 screentPos = Camera.main.WorldToScreenPoint(transform.position);

        //如果移动到最下边 并且 还想向下   或者  移动到最上边 并且 还想向上
        if (screentPos.y <= 0 && ver < 0 || screentPos.y >= Screen.height && ver > 0)
            ver = 0;//停

        this.transform.Translate(hor, 0, ver);
    }

}

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值