unity的特性AttriBute曾经令我大为头疼。因为不动使用的法则,但是教程都是直接就写,卡住就不能继续学下去。令我每一次看到,直接不敢看了。
今天使用文心一言搜索一番,发现,恐惧都是自己想象的,实际上这个很简单。
这个小故事,告诉我们想千次万次,不如实地走一次。越是畏惧,就需要越去了解他。
克服恐惧的心里就是每天去见它。
在Unity中,特性(Attributes)是一种强大的工具,用于为代码元素(如类、方法、字段等)添加元数据。这些元数据可以在运行时或编译时被其他代码或工具读取和使用,以改变代码的行为或提供额外的信息。Unity的特性有很多使用场景,以下是一些常见的场景和例子分析:
1. 序列化(Serialization)与数据持久化
使用场景:
- 当需要保存和加载对象的状态时,可以使用序列化特性。例如,保存游戏进度、玩家数据或配置设置。
- 序列化特性用于将对象状态转换为可存储或传输的格式,这对于保存游戏状态、玩家数据或配置信息至关重要。
例子分析:
使用[Serializable]
特性来标记一个类,使其可以被序列化。
这样,你就可以将该类的实例转换为字节流,保存到文件或通过网络发送,然后再将其反序列化为对象。
[Serializable]
特性用于标记类,使其可以被序列化。
这对于保存和加载游戏对象的状态、玩家进度等信息非常有用。
如果是字段的【serializedField】 就是将私有的字段 公开在面板方便操作。
[Serializable]
特性用于标记类,
1.1类被序列化怎么应用
序列化是将对象的状态转换为可以存储或传输的格式的过程,
而反序列化则是将序列化后的数据转换回对象状态的过程。
在这个例子中,PlayerData
类被标记为[Serializable]
,
这意味着它的实例可以被序列化。
该类包含了玩家的名称、分数和位置信息,这些信息可以在游戏过程中被保存,并在需要时重新加载。
1.2序列化对象
在序列化对象时,需要注意以下几点:
- 不是所有的类型都可以被序列化。例如,Unity的某些特定类型(如
GameObject
或Transform
)不能被直接序列化。你需要将它们的状态转换为可以序列化的格式(如使用它们的位置、旋转和缩放属性)。 - 序列化可能会影响性能,特别是在处理大量数据或复杂对象时。因此,在设计序列化策略时要考虑性能影响。
- 安全性也是一个需要考虑的因素。确保你不会序列化敏感信息,并且在反序列化数据时验证其来源和完整性。
2. 自定义Inspector面板
使用场景:
- 在Unity编辑器中,特性可以用来自定义脚本组件在Inspector面板中的显示方式,提高开发效率。
例子分析:
使用[Header]
、[Tooltip]
、[Range]
等特性来改善Inspector面板的布局和交互性。
这个应该是可以在面板上看到这一组的字段,太多字段,不好阅读
2.1. 自定义属性与Inspector展示
使用场景:
在Unity的Inspector面板中,特性可以用来控制自定义属性的显示和编辑方式,提高易用性。
例子分析:
[HideInInspector]
特性用于隐藏公共字段在Inspector面板中的显示,这对于那些不需要用户直接编辑的内部状态变量非常有用。
[Header]
特性则用于在Inspector面板中为属性组添加标题,使得属性的组织更加清晰。
2.2. 编辑器扩展与自动化
使用场景:
Unity的特性在编辑器扩展中起到了关键作用,允许开发者自定义编辑器的行为和外观,从而提供更为便捷的开发体验。
例子分析:
[InitializeOnLoad]
特性用于在编辑器启动时或脚本重新编译后自动执行指定的操作。这在初始化编辑器扩展、注册事件监听器或设置默认配置时非常有用。
(重要)3. 依赖注入和类型映射
使用场景:
- 在实现依赖注入或类型映射时,特性可以用来标记类或接口,以便在运行时解析和实例化对象。
例子分析:
使用自定义特性来标记接口或类,并在运行时通过反射或其他机制来创建和配置对象。这常用于实现插件系统、模块化架构或解耦代码。
这个没有看懂????
在Unity中,依赖注入和类型映射是两种重要的特性,它们有助于实现代码的解耦和重用,提高应用程序的可维护性和可扩展性。下面,我将通过具体的例子来讲解这两种特性。
3.1、依赖注入
依赖注入是一种将依赖关系(通常是对象或服务)从它们的客户端(通常是调用者)中分离出来,并由外部实体(如框架或容器)将它们注入到客户端中的技术。在Unity中,依赖注入通常通过构造函数注入、属性注入或方法注入来实现。
构造函数注入
以构造函数注入为例,假设我们有一个游戏角色(Player)类,它依赖于一个武器(Weapon)类来进行攻击。我们可以这样设计这两个类:
--我持有你的引用,
--我的构造函数的时候(你的类 ){
引用 = 你的类
} //构造的时候,传入你的类
--我调用的时候{
你的引用。你的方法()
}
通过这种方式,Player
类不再需要负责创建Weapon
对象,
它的职责被转移到了外部实体(这里是Unity的IoC容器)。
这使得Player
类更加专注于自己的业务逻辑,同时也提高了代码的可测试性和可维护性。
属性注入
一、属性注入
属性注入是通过设置对象的公共属性来注入依赖的。在Unity中,你可以使用[Dependency]
特性来标记需要注入的属性,然后在 容器配置时告诉Unity进行属性注入。
下面是一个属性注入的例子:
在这个例子中,MyService
类有一个(接口)IMyDependency
类型的属性MyDependency
,它被标记为(被标记)[Dependency]
。
在Unity容器中注册MyService
时,我们使用InjectionProperty
来告诉Unity对MyDependency
属性进行注入。
当解析MyService
对象时,Unity会自动创建一个MyDependency
的实例并将其注入到MyService
的MyDependency
属性中。
方法注入
方法注入是通过调用对象的某个方法来注入依赖的。在Unity中,你可以使用InjectionMethod
特性来指定需要注入的方法。
3.2、类型映射
类型映射在Unity中通常指的是将一种类型映射到另一种类型,以便在解析时返回特定的实现。这类似于接口与实现之间的映射关系。在Unity中,我们可以使用RegisterType
方法来实现类型映射。
例如,假设我们有一个ILogger
接口和它的一个实现ConsoleLogger
:
将ILogger接口映射到ConsoleLogger实现 .
RegisterType<>()
Resolve<>
这些是什么东西????映射 解析 容器 ioc
通过这种方式,我们可以在不改变客户端代码的情况下替换ILogger
的实现。例如,如果我们想将日志输出到文件而不是控制台,我们只需要创建一个新的日志实现(如FileLogger
),并在Unity中更新映射关系即可:
这样,所有依赖ILogger
的客户端代码都会自动使用新的FileLogger
实现,而无需修改任何客户端代码。这大大提高了代码的可扩展性和可维护性。
4. 特殊行为控制
使用场景:
- 有些特性用于控制Unity的特殊行为,如场景加载、资源管理等。
例子分析:
使用[PostProcessScene]
特性来标记一个方法,使其在场景加载后自动执行某些操作,如初始化场景数据或设置特殊效果。
5. 调试和性能分析
使用场景:
- 特性也可以用于调试和性能分析,帮助开发者识别和解决问题。
例子分析:
使用[Conditional]
特性来标记一个方法,使其只在特定条件下编译和执行,通常用于调试目的。
6. 自定义编辑器窗口与工具
使用场景:
通过特性,开发者可以创建自定义的编辑器窗口和工具,提供额外的功能或改进现有的工作流程。
例子分析:
使用EditorWindow
类结合特性可以创建自定义的编辑器窗口。这些窗口可以包含自定义的UI元素,用于执行特定的编辑任务。
7. 脚本编译与平台特定行为
使用场景:
某些特性可以控制脚本的编译行为或针对特定平台的行为。
例子分析:
[RequireComponent]
特性用于自动为附加该特性的脚本组件添加其他组件。这在确保组件之间的依赖关系时非常有用。
当MyMovementScript
被附加到一个GameObject上时,Unity会自动为该GameObject添加一个Rigidbody
组件(如果尚未存在)。
这些只是Unity特性的一些常见使用场景和例子。实际上,随着Unity的不断发展和社区的创新,特性的使用场景也在不断扩展。开发者可以根据具体需求和项目特点来选择合适的特性,以提高代码的可读性、可维护性和性能。