当前位置: 首页 > news >正文

Unity笔记(一)——生命周期函数、Inspector面板、MonoBehavior、GameObject

写在前面

写本系列的目的(自用)是回顾已经学过的知识、记录新学习的知识或是记录心得理解,方便自己以后快速复习,减少遗忘。这里只有部分C#相关语法知识,内容是跟着唐老师学的。

一、生命周期函数

1、帧的概念

游戏本质与帧的关系:游戏的本质就是⼀个死循环,每次循环处理游戏逻辑就会更新⼀次画面、当切换画面的速度达到⼀定时(通常24/秒),⼈眼就会认为画面是流畅的。

常见帧率:

60帧:每帧时间=1000ms / 60 ≈ 16.66ms

30帧:每帧时间=1000ms / 30 ≈ 33.33ms

⼈眼舒适帧率:人眼在放松状态下可视帧数为每秒24

Unity已经内置了死循环机制,开发者不需要自己编写while循环,通过使用Unity提供的生命周期函数来处理游戏逻辑。

2、生命周期函数

(1)Awake函数

AwakeUnity中的生命周期函数,在对象创建时自动调用。类似构造函数的存在(但不能带参数),用于对象创建时的初始化操作。⼀个对象在其生命周期中只会调用⼀次(仅与脚本实例创建有关)。

不写访问修饰符时默认是私有函数。

(2)OnEnable生命周期函数

当脚本依附的GameObject对象每次激活时自动调用。​​​​​​​且相同来源的打印信息会被折叠显示,但不同激活方式(初始创建vs手动激活)会产生不同来源的打印

用处是,当需要对象被激活时执行特定逻辑处理的场景,可以编写OnEnable生命周期函数。

(3)Start生命周期函数

从对象被创建出来后,第⼀次帧更新之前调用。⼀个对象只会调用一次。

与Awake的区别是:

Awake:对象生成时立即调用,类似构造函数

Start:在Awake之后,第⼀次帧更新之前调用

例如,​在Update中动态创建的对象会立即执行Awake,Start会等到当前帧结束后,下⼀帧开始前才执行。

用处是用于对象初始化。

(4)FixedUpdate生命周期函数

主要用于进行物理相关的处理,如碰撞检测等物理计算。如果不做物理相关的游戏开发,基本上很少使用该函数。

Unity内部实现了⼀个死循环,以固定间隔时间(物理帧间隔)执行FixedUpdate

(5)Update生命周期函数

处理游戏核心逻辑更新,如位移计算等;每帧执行,帧率可通过项目设置控制;Unity通过反射在每次游戏循环时自动调用;是编写游戏逻辑的主要接口,会频繁使用。

例如,在Update中,可以通过每帧修改位置实现平滑移动效果。

(6)LateUpdate生命周期函数

处理摄像机位置更新等渲染相关逻辑,在Update之后执行,两者之间Unity会处理动画更新,这样可以避免在Update中更新摄像机导致的渲染错误。

(7)OnDisable生命周期函数

​​​​​​​当依附的GameObject对象每次失活时调⽤,与OnEnable形成对应关系。​​​​​

对象失活后,所有生命周期函数(特别是循环更新类函数)都会停止调用。

核心作用:​​​​​​​用于在对象失活时进行特定逻辑处理,适合存放状态保存、资源释放等代码

(8)OnDestroy生命周期函数

当依附的GameObject对象被销毁(从场景中移除)时调用。

销毁时会先调用OnDisable再调⽤OnDestroy,形成完整生命周期闭环。只会调用一次。

二、Inspector窗口的变量

1、让私有的和保护的也可以被显示和编辑

在脚本中创建的变量,如果不是public的,在Inspector面板上就不会被显示。

但也可以通过特性,让私有的和保护的也可以被显示和编辑。

可以在变量前添加[SerializeField]特性,这样私有的和保护的变量都可以在面板上显示和编辑。

public class inspec : MonoBehaviour
{[SerializeField]private int a = 10;
}

面板:

2、让公共的不能被显示和编辑

在脚本中创建的变量,可以通过特性,让公共的也不可以被显示和编辑。

可以在变量前添加[HideInInspector]特性,这样公共的也不可以在面板上显示和编辑。

public class inspec : MonoBehaviour
{[HideInInspector]public string str = "HELLO";
}

3、小部分类型有显示限制

我们所熟知的各种类:数组、列表、枚举、整型、浮点型、字符串等,只要是公共的或是加了可编辑特性的,都可以在Inspector面板上显示和编辑。

但字典、自定义结构体、自定义类哪怕是公共的也不支持显示。​​​​​​​Unity默认不支持复杂数据结构的序列化显示。

但如果在自定义结构体或是自定义类前,加上特性[System.Serializable],那么自定义的结构体或是自定义类也能够显示并编辑了

[System.Serializable]
public class Myclass
{public string name;public int age;
}public class inspec : MonoBehaviour
{public Myclass myclass;
}

但是字典仍然不可以。

三、MonoBehavior

1、重要成员

(1)获取依附的GameObject

通过MonoBehavior中的成员,可以获取当前脚本依附的对象的各种信息。名字、位置、将当前依附的脚本进行激活、失活。

以下是获取信息的函数。

void Start()
{//获取名字print(this.gameObject.name);//获取位置信息//获取位置print(this.transform.position);//获取角度print(this.transform.eulerAngles);//获取缩放大小print(this.transform.lossyScale);
}

将当前脚本激活/失活可以用this.enabled = true/false:

void Start()
{//激活this.enabled = true;//失活this.enabled = false;
}

2、重要方法

(1)获取挂载的脚本

可以利用函数GetComponent的泛型方法来获取当前对象挂载的脚本。

void Start()
{//根据泛型获取mono t = this.GetComponent<mono>();print(t);
}

(2)获取挂载的多个脚本

假如对象上挂载了多个相同的脚本,可以用this.GetComponents来得到挂载的所有脚本。

void Start()
{mono[] array = this.GetComponents<mono>();print(array.Length);
}

(3)获取子对象挂载的脚本 

可以使用this.GetComponentInChildren<T>()获取挂载到子对象上的脚本(它默认也会找自己身上是否挂载该脚本)

括号内可以传参数,默认不传是false。假如穿了true,那么哪怕是失活的脚本也会去找,false默认失活的不找

同样的,也可以获取子对象挂载的多个脚本,添个s即可:this.GetComponentsInChildren<T>()

void Start()
{mono2 t2 = this.GetComponentInChildren<mono2>(true);print(t2);mono2[] array2 = this.GetComponentsInChildren<mono2>(true);print(array.Length);
}

(4)获取父对象挂载的脚本

可以使用this.GetComponentInParent<T>()获取挂载到父对象上的脚本(它默认也会找自己身上是否挂载该脚本)

需要注意的是,找父对象的脚本时一般不会传入是否找失活对象。因为父对象失活时,子对象也会失活,没有意义。

同样的,也可以获取父对象挂载的多个脚本,添s即可:this.GetComponentsInParent<mono2>()

void Start()
{mono_f t3 = this.GetComponentInParent<mono_f>();print(t3);mono2[] array3 = this.GetComponentsInParent<mono2>();print(array3.Length);
}

四、最小单位GameObject

GameObject是Unity场景中的最小单位,所有脚本都依附在GameObject上运行。

1、GameObject成员变量

(1)获取名字

可以通过this.gameObject.name获取当前对象名称。也可以直接对该值赋值事实修改场景中的对象名称。

    void Start(){print(this.gameObject.name);this.gameObject.name = "MyCube";print(this.gameObject.name);}

执行结果:

(2)是否激活、是否是静态

判断对象是否激活可以用.activeSelf,判断是否是静态可以用.isStatic

    void Start(){print(this.gameObject.activeSelf);print(this.gameObject.isStatic);}

(3)获取层级和标签

获取层级:gameObject.layer,层级默认为0;获取标签:gameObject.tag,未设置标签时返回"Untagged"

    void Start(){print(this.gameObject.layer);print(this.gameObject.tag);}

(4)Transform

可以通过this.gameObject.transform.position获取详细的三维坐标,也可以直接通过this.transform.position获取。推荐后者:

print(this.transform.position);

结果Unity会自行四舍五入。 

2、GameObject静态方法

(1)创建自带几何体

可以通过GameObject.CreatePrimitive(PrimitiveType)静态方法创建Unity内置几何体。如下例,创建一个自带的Cube立方体并为其改名。

void Start()
{GameObject obj =  GameObject.CreatePrimitive(PrimitiveType.Cube);obj.name = "创建的立方体";
}

运行结果: 

(2)查找对象 

①可以通过按名查找和按tag查找。

按名查找:使用GameObject.Find("对象名")静态方法。缺点是效率较低,因为会遍历场景中所有对象,假如没找到,返回null

按tag查找: 有两种方法,效果一样,GameObject.FindWithTag("标签名")、GameObject.FindGameObjectWithTag("标签名")。

需要注意的是,二者都无法查找到失活的对象。 假如场景中有多个同名或是同tag对象时,查找结果是随机的,无法指定找到哪个对象。

void Start()
{GameObject obj2 = GameObject.Find("创建的立方体");if(obj2 != null){print(obj2.name);}GameObject obj3 = GameObject.FindWithTag("Player");if(obj3 != null){print(obj3.name);}GameObject obj4 = GameObject.FindGameObjectWithTag("Player");
}

②查找多个对象 

 查找多个对象只能用GameObject.FindGameObjectWithTag("tag名"),且同样只能找到激活对象

void Start()
{GameObject[] objs = GameObject.FindGameObjectsWithTag("Player");print("查找到的数量" + objs.Length);
}

 ③通过泛型查找对象

可以通过FindObjectOfType<T>():查找挂载特定脚本的对象,返回的是脚本组件实例。

void Start()
{GB_1 o = GameObject.FindObjectOfType<GB_1>();print(o.gameObject.name);
}

(3)实例化(克隆)对象 

实例化(克隆)对象是根据⼀个GameObject对象创建和它完全相同的对象。克隆的对象包含原对象的所有脚本信息和组件。

使用GameObject.Instantiate()方法实现克隆功能。克隆的对象可以选择场景上的对象,也可以是预设体对象。

public GameObject obj;
void Start()
{GameObject.Instantiate(obj);
}

关联预设体:

运行结果:

(4)删除对象

GameObject.Destroy()方法可以删除游戏对象或脚本组件。可以传1~2个参数,第一个参数是需要删除的对象,第二个参数是延迟多少秒删除。

public GameObject obj;public GameObject obj2;
// Start is called before the first frame update
void Start()
{GameObject.Instantiate(obj);GameObject.Destroy(obj2);//延迟5s删除GameObject.Destroy(obj, 5);//删除当前脚本组件,Inspector窗口中该脚本组件会消失GameObject.Destroy(this);
}

GameObject.Destroy()方法实际上不会立即移除对象,只是添加了移除标识。通常会在下一帧时真正移除对象。如果需要立即删除,需要使用:DestroyImmediate()

(5)过场景不移除

默认情况下,在切换场景时,所有场景对象会被自动删除。如果不希望某个对象被删除,就可以使用:

GameObject.DontDestroyOnLoad(this.gameObject);

这样,该对象过场景就不会被移除。

3、GameObject成员方法

(1)创建空物体

创建空物体直接使用new即可,就可以在场景里创建一个空物体。可以传入参数,为空物体命名、挂载脚本。

添加脚本使用泛型方法 obj.AddComponent<T>即可,添加的脚本有返回值,返回值是添加的脚本,可以用变量接收。

 void Start(){GameObject obj = new GameObject();GameObject obj2 = new GameObject("空物体");GameObject obj3 = new GameObject("加脚本的空物体",typeof(temp));temp tp =  obj2.AddComponent<temp>();
}

查看加脚本的Inspector信息:

 (2)标签比较

标签比较有两种方法,第一种是使用gameObject.CompareTag(),会比较当前对象的tag名和括号中传入的tag名

第二种是直接判断gameObject.tag == “tag名”,两种方法完全一样。

void Start()
{if(this.gameObject.CompareTag("Player")){print("对象的标签是 Player");}if(this.gameObject.tag == "Player"){print("对象的标签是 Player");}
}

(3)设置对象激活失活

设置对象激活失活可以使用obj.SetActive(),括号里填false时,对象就会失活。

void Start()
{    GameObject obj = new GameObject();GameObject obj2 = new GameObject("空物体");GameObject obj3 = new GameObject("加脚本的空物体",typeof(temp));obj.SetActive(false);obj2.SetActive(false);obj3.SetActive(false);
}

(4)广播或发送消息

了解即可,不建议使用。

SendMessage()主要是通知自己要执行什么行为。例如下例,通知自己执行函数TestFun。于是会在自己身上挂载的所有脚本中去找这个名字的函数,效率很低。

 void Start(){this.gameObject.SendMessage("TestFun");}void TestFun(){print("执行TestFun");}

gameObject. BroadcastMessage("函数名")是广播行为,会通知自己和自己的子对象去执行这个函数。

this.gameObject.BroadcastMessage("TestFun");

gameObject.SendMessageUpwards("函数名")也是广播行为,会通知自己和自己的父对象去执行这个函数。

this.gameObject.SendMessageUpwards("TestFun");
http://www.dtcms.com/a/317043.html

相关文章:

  • Go语言版JSON转TypeScript接口生成器:支持智能递归解析与命名优化
  • 超细整理,接口测试基础+流程,真实环境下怎么测...
  • [GESP202309 四级] 2023年9月GESP C++四级上机题题解,附带讲解视频!
  • 解锁音频创作新可能:AI 人声伴奏分离神器 Replay 深度解析
  • Python 进行点云ICP(lterative Closest Point)配准(精配准)
  • 【Java String】类深度解析:从原理到高效使用技巧
  • 数论手机辅助:打造便捷高效的移动应用交互体验
  • Wisdom SSH:数据库自动化运维的坚固基石
  • WARNING: Illegal reflective access by org.apache.ibatis.reflection.Reflector
  • 八股——IM项目
  • 多端同步新解法:Joplin+cpolar联合通过开源设计实现跨平台无缝协作?
  • 2025年测绘程序设计模拟赛一--地形图图幅编号及图廓点经纬度计算
  • Python日志记录库——logaid
  • 磁悬浮转子振动控制:主动电磁力如何成为高速旋转的“振动克星”
  • 数据集相关类代码回顾理解 | sns.distplot\%matplotlib inline\sns.scatterplot
  • LeetCode 刷题【31. 下一个排列】
  • Golang 基本数据类型
  • 【vibe coding】Kubernetes + Nginx Ingress 实现云端Workspace容器分配与域名访问方案
  • Linux lvm逻辑卷管理
  • MySQL间隙锁在查询时锁定的范围
  • lesson32:Pygame模块详解:从入门到实战的2D游戏开发指南
  • Python 3.13 预览版:颠覆性特性与实战指南
  • 项目设计模式草稿纸
  • 电感矩阵-信号完整性分析
  • ob数据库是什么
  • 二维数点问题2
  • 计算机视觉的四项基本任务辨析
  • HPE磁盘阵列管理01——MSA和SMU
  • OpenLayers学习(一)-基础
  • 赛灵思ZYNQ官方文档UG585自学翻译笔记:Quad-SPl Flash 闪存控制器