Unity脚本--01-脚本书写规则-脚本生命周期-脚本调试-常用API

一、脚本书写规则

脚本:.cs的文本文件  类文件

作用:附加到游戏物体中,定义游戏对象行为指令的代码

与C#类的区别:

脚本只有字段和方法,没有自动属性和构造函数

public int A { get { return a; } set {  a = value; } }

属性定义了在unity中不会显示

    public Lifecycle()
    {
        Debug.Log("构造函数")
        //b=Time.time;
    }

不要在脚本中写构造函数,因为不能在子线程中访问主线程成员

public 定义字段 编译器中会看到,private不会看到,如果想进行其他操作如下:

//序列化字段  作用:在编译器中显示私有变量

[SerializeField]
private int a;

//作用:在编译器中隐藏字段

[HideInInspector]
public float b;

//作用:限制范围

[Range(0,100)]
public int c;

二、脚本生命周期(必然事件)

Unity脚本从唤醒到被销毁的过程

消息:当满足某种条件Unity引擎自动调用的函数

Unity中生命周期是单线程,先做一个再做一个

1.初始阶段

//执行时机:创建游戏对象(物体被载入时)  立即执行1次(早于Start)
//作用:初始化 (构造函数的功能来赋值)this.enable=true,可以来判断是否创建对象

private void Awake()
{
    Debug.Log("Awake--" + Time.time+"--"+this.name);  
}

//执行时机:创建游戏对象(物体被载入时) --》脚本启用--》才执行(1次)
//作用:初始化

private void Start()
{
    Debug.Log("Start--" + Time.time+"--"+this.name);
}

//执行时机:每当脚本对象启用时调用

private void OnEnable()
{
  
}

在初始阶段肯定是先Awake再Start,多个物体也是多个物体Awake执行完毕后再执行Start

2.物理阶段

//执行时机:脚本启用后,固定时间被执行。0.02s
//适用性:适用于对游戏对象做物理操作(移动,旋转),不会受到渲染影响
//渲染时间不固定是因为每帧渲染量不同,机器性能不同

private void FixedUpdate()
{
    
}

3.输入事件

//执行时机:OnMouseEnter 鼠标移入  OnMouseOver 鼠标经过  OnMouseExit  鼠标离开: OnMouseDown鼠标按下   OnMouseUp 鼠标抬起

private void OnMouseDown()
{
    Debug.Log("OnMouseDown" );
}

4.游戏逻辑

//执行时机:渲染帧执行,执行间隔不固定
//适用性:处理游戏逻辑

private void Update()
{
    
}

//在Update函数被调用后执行(但在同一帧)
//适用于跟随逻辑(相机)

private void LateUpdate()
{
    
}

5.场景渲染

//执行时机:当Mesh Renderer在任何相机中可见时被执行 被看见那一帧执行

private void OnBecameVisible()
{
}

//执行时机:当Mesh Renderer在任何相机中不可见时被执行

private void OnBecameInvisible()
{
}

6.结束阶段

//执行时机:对象变为不可用或附属游戏对象非激活状态时此函数被调用

private void OnDisable()
{
    
}

//执行时机:当脚本或附属游戏对象被销毁时执行

private void OnDestroy()
{
    
}

//执行时机:当程序结束,应用程序退出时被调用

private void OnApplicationQuit()
{
    
}

三、脚本调试

1.简单调试

①.控制台调试

Debug.Log(变量)     print(变量)

②.定义共有变量,程序运行后在检测面板查看数据

2.复杂调试

①.不是在Update中时:先加断点,再F5  调试过程中,输入代码:右键--快速监视  查看即时窗口

②.在Update中,单帧调试:启动调试,运行场景   暂停游戏  加断点  单帧执行  结束调试

四、常用API

MonoBehaviour父类成员与函数都可以使用

1.Component类

component类提供了查找(在当前物体、后代、先辈)组件的功能

①GetComponent:如果游戏对象附加有type类型的组件,则返回,如果没有则为空

GetComponent<MeshRenderer>().material.color = Color.red;

②GetComponents:获取游戏对象所有组件

var allComponent = this.GetComponents<Component>(); 

③GetComponentsInChildren:获取游戏对象后代组件

//获取后代物体的指定类型组件(从自身开始)
//后代中有某个组件,不知道是哪个的情况下使用

var allComponentChildren = this.GetComponentsInChildren<MeshRenderer>();

foreach ( var child in allComponentChildren)
{
    child.material.color = Color.red;
}

④GetComponentsInParent:获取先辈物体的指定类型组件(从自身开始)

var allComponentParent = this.GetComponentsInParent<MeshRenderer>();

foreach (var parent in allComponentChildren)
{
    parent.material.color = Color.red;
}

2.Transform类

Transform类提供了查找(父、根、子)变换组件功能,改变位置、角度、大小功能

①查找变换组件

if (GUILayout.Button("foreach--transform"))
{
    foreach (Transform child in this.transform)
    {
        //child 为每个子物体的transform组件
        print(child.name);
    }
}

if (GUILayout.Button("root"))
{
    //获取根物体的Transform组件    根物体就是最顶层的根源的游戏对象
    Transform rootTF = this.transform.root;
}

if (GUILayout.Button("parent"))
{
    //获取父物体的Transform组件
    Transform parentTF = this.transform.parent;
}

if (GUILayout.Button("SetParent"))
{
    //设置父物体  将该游戏对象的父Transform设置为tf,
    //默认设置true 则原先该游戏对象的transform为世界坐标系,体现为不发生变化
    //若为false,则原先该游戏对象的transform为local坐标系,会相对于父物体的transform进行相应变化
    transform.SetParent(tf,false);
}

if (GUILayout.Button("findchild"))
{
    //根据子物体的名称获取子物体的Transform组件
    Transform childTF = transform.Find("子物体名称");
    //查找孙子
    Transform childTF02 = transform.Find("子物体名称/子物体名称");//确保路径不会发生改变,建议不这样写
}

if (GUILayout.Button("Getchild"))
{
    //根据子物体的索引获取子物体的Transform组件
    int count = this.transform.childCount;
    for (int i = 0;i<count; i++)
    {
        Transform childTF = this.transform.GetChild(i);
    }
}

练习:在层级未知的情况下找到特定名字的子物体(递归)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

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

②改变游戏对象位置、角度、大小功能

if (GUILayout.Button("pos/scale"))
{
    foreach (Transform child in this.transform)
    {
        //物体相对于世界坐标系原点的位置
        //this.transform.positiion

        //物体相对于父物体的轴心点的位置
        //this.transform.localposition

        //相对于父物体缩放比例
        //this.transform.localScale

        //理解为:物体与模型缩放比例(自身缩放比例*父物体缩放比例)
        //this.transform.lossyScale (只能读,想改通过localScale改)
    }
}

if (GUILayout.Button("Translate")) 
{
    //向自身坐标系z轴移动1米
    transform.Translate(0, 0, 1);
    //向世界坐标系z轴移动1米
    transform.Translate(0, 0, 1,Space.World);
}

if (GUILayout.Button("Rotate"))
{
    //沿自身坐标系y轴旋转10度
    transform.Rotate(0, 10, 0);
    //沿世界坐标系y轴旋转10度
    transform.Rotate(0, 10, 0, Space.World);
}

if (GUILayout.RepeatButton("Rotate"))
{
    //围绕旋转
    transform.RotateAround(Vector3.zero,Vector3.up,1);
}

3. GameObject类

activeSelf :只读,该游戏对象的局部激活状态,若父未激活,它局部激活,仍然未激活

activeInHierarchy:场景内游戏对象是否激活

设置物体启用/禁用:this.gameObject.SetActive(true);

添加组件步骤:

if (GUILayout.Button("添加光源"))
{
    //添加组件步骤
    //创建物体
    GameObject lightGO = new GameObject();
    //添加组件
    Light light = lightGO.AddComponent<Light>();
    light.color = Color.red;
    light.type = LightType.Point;
}

查找物体方法:

//在场景中根据名称查找物体(不建议使用)
GameObject.Find("游戏对象名称")

//获取所有使用该标签的物体
GameObject[] allEnemy = GameObject.FindGameObjectsWithTag("Enemy");

//获取单个使用该标签的物体
GameObject playerGO = GameObject.FindGameObjectWithTag("player");

4.object类

Destory:删除一个游戏对象,组件或资源

Destory(GameObject,5)//5秒后消除

DontDestroyOnLoad:加载新场景的适合使目标对象不被自动销毁

FindObjectOfType:根据类型找第一个激活的对象

FindObjectsOfType:根据类型找所有激活的对象

练习:查找血量最低的敌人

首先给所有敌人挂上Enemy脚本,脚本只有一个公开字段HP

再定义新的脚本去查找:

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;

/// <summary>
///查找HP最小的敌人
/// </summary>
public class FindMinHPEnemy : MonoBehaviour
{
    private void OnGUI()
    {
        if(GUILayout.Button("查找血量最低敌人"))
        {
            //查找场景中所有Enemy类型的引用
            Enemy[] allEnemy = Object.FindObjectsOfType<Enemy>();
            //获取血量最低的对象的引用
            Enemy min = FindEnemyByMinHP(allEnemy);
            //根据Enemy组件类型引用,获取其他组件类型引用
            min.GetComponent<MeshRenderer>().material.color = Color.red;
        }
    }

    public Enemy FindEnemyByMinHP(Enemy[] enemies)
    {
        Enemy minEnemy = enemies[0];
        for (int i = 1; i < enemies.Length; i++)
        {
            if (minEnemy.HP > enemies[i].HP)
            {
                minEnemy = enemies[i];
            }
        }
        return minEnemy;
    }
}

5.time类

①Time.time:从开始到目前

②Time.deltaTime:帧间消耗的时间

public void Update 每渲染帧执行一次,想要一个物体均匀旋转

 public void Update()
 {
     this.transform.Rotate(0, 1 * Time.deltaTime, 0);
 }

帧多,1秒旋转速度块,希望1帧旋转量小;帧少相反

旋转速度*每帧消耗时间,可以保证旋转速度不受机器性能和渲染影响

③Time.scaledTime:缩放游戏时间

public void FixedUpdate 固定0.02秒执行一次,可以直接写this.transform.Rotate(0, 1 , 0);

它与渲染无关,但受到TimeScale影响,Time.deltaTime也会受影响,只要在渲染帧*了

//游戏暂停 个别物体不受影响,在渲染更新里乘以Time.unscaledDeltaTime 

练习:在屏幕上显示倒计时 02:00

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
///时钟显示
/// </summary>
public class PrinttSeconds : MonoBehaviour
{
    public int second = 120;
    private float timer = 0.0f;
    private float nextTime = 0;
    private TextMeshProUGUI txtTimer;

    private void Start()
    {
        txtTimer = GetComponent<TextMeshProUGUI>();

        //重复调用(被执行的方法名称,第一次执行时间,每次执行间隔)
        InvokeRepeating("CalculateTime", 1, 1);
        //Invoke(被执行方法名称,执行时间)  
        //可以延迟调用
    }

    public void Update()
    {
        操作Time.deltaTime
        //DeltaTimer();
        操作Time.time
        //TimeTimer();
    }
    private void TimeTimer()
    {
        if (Time.time > nextTime)
        {
            CalculateTime();
            nextTime = Time.time + 1;
        }
    }

    private void DeltaTimer()
    {
        timer += Time.deltaTime;
        if (timer >= 1.0f)
        {
            CalculateTime();
            timer = 0.0f;
        }
    }

    private void CalculateTime()
    {
        txtTimer.text = string.Format("{0:d2}:{1:d2}", second / 60, second % 60);
        second--;
        if (second < 10)
        {
            txtTimer.color = Color.red;
        }
        if (second < 0)
        {
            CancelInvoke("CalculateTime");//取消调用
        }
    }
}

隔一段时间调用方法的方法:

①InvokeRepeating():每隔固定时间执行

若不是固定时间或者必须在UpDate中实现使用以下方法:

②Time.time:适合满足另外条件立马执行(先做再等),例如:按下鼠标发射子弹

③Time.deltaTime:适合到满足条件后等待规定时间再执行(先等再做),例如:到一个点等待再去下一个点。

五、小知识

预制件:如果一个游戏对象在多个场景中可以使用,将其拽到Project中

对预制件修改会对所有在场景中的游戏对象进行修改

若对场景中游戏对象进行修改了点Apply,会对所有使用预制件进行修改,若不点Apply,修改的地方就不归预制件管

若点击Select,会找到预制件位置

若点击Revert,会返回到预制件的属性

  • 13
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦果

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值