Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】
Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】
目录
Unity 设计模式 之 创建型模式 -【单例模式】【原型模式】 【建造者模式】
3、 具体建造者类:木屋建造者 WoodenHouseBuilder 和砖屋建造者 BrickHouseBuilder
一、简单介绍
设计模式 是指在软件开发中为解决常见问题而总结出的一套 可复用的解决方案。这些模式是经过长期实践证明有效的 编程经验总结,并可以在不同的项目中复用。设计模式并不是代码片段,而是对常见问题的 抽象解决方案,它提供了代码结构和模块间交互的一种设计思路,帮助开发者解决特定的设计问题。
设计模式的特点:
- 通用性:设计模式针对的是软件开发中常见的设计问题,适用于各种软件工程项目。
- 可复用性:设计模式可以在不同项目和环境下被重复使用,提高代码的可维护性和扩展性。
- 可扩展性:设计模式有助于让代码结构更加灵活,易于扩展和修改。
- 模块化:通过设计模式,可以减少代码的耦合性,增强模块间的独立性。
- 提高沟通效率:设计模式为开发者提供了一种通用的设计语言,使得团队成员能够快速理解并讨论设计方案。
二、单例模式 (Singleton Pattern)
单例模式 (Singleton Pattern) 是一种创建型设计模式,保证一个类只有一个实例,并提供一个全局访问点来获取该实例。它通过控制类的实例化过程,确保系统中只有一个该类的对象存在。
在单例模式中,类的构造函数通常是私有的,防止外部通过 new
来创建对象,类内部维护一个静态实例,通过公共的静态方法提供访问。
单例模式的特点
- 唯一实例:单例模式确保一个类只有一个实例,无论系统中的组件如何调用它。
- 全局访问点:单例模式为客户端提供一个全局访问点(通常是静态方法),通过这个访问点,所有客户端都能获得相同的实例。
- 延迟初始化:可以在首次访问时创建该实例,节省系统资源(可选)。
1、什么时候使用单例模式
全局唯一的资源管理:如果一个类需要控制对某种共享资源(如数据库连接池、文件系统、日志器、配置管理器等)的访问,并且不允许有多个实例同时存在。
配置或状态共享:当系统中某个类需要持有全局配置或共享状态时,单例模式可以使得这些状态在不同模块中保持一致。
资源开销较大的对象:如果一个类的实例化代价较高(如外部资源初始化、复杂运算等),而且只需要一个实例时,单例模式可以避免多次重复的创建操作。
需要严格控制实例数量:在某些业务逻辑中,严格要求一个类只能有一个实例,否则会引发逻辑混乱或资源竞争(如操作系统中的任务管理器)。
2、单例模式的好处
控制实例数量:确保一个类只生成一个实例,避免不必要的内存占用,尤其对于管理全局状态或资源的对象(如数据库连接、日志系统、配置管理等),控制实例数量可以提高系统的稳定性。
全局访问:单例模式提供了一个全局访问点,这使得某些系统级别的服务(如日志系统、资源管理器等)能够方便地被全局访问。
节省资源:避免重复创建相同对象,减少了资源的消耗,尤其是对于耗费较大的对象(如文件系统、数据库连接等)。
线程安全:通过实现线程安全的单例,可以确保在多线程环境下,多个线程对该对象的操作是安全的。
3、使用单例模式的注意事项
-
全局状态的复杂性:虽然单例模式提供全局访问,但这也意味着它引入了全局状态,可能导致系统的状态管理变得复杂,特别是在大型系统中。
-
并发和线程安全:在多线程环境下,需要确保单例的实现是线程安全的,否则可能会出现多个线程同时创建实例的情况,导致数据不一致。
-
难以扩展:由于单例模式限制了对象的创建数量,在某些情况下,可能不利于系统的扩展或测试。比如,在单元测试中,单例模式可能难以进行模拟。
-
滥用风险:单例模式提供全局访问点,容易被滥用为全局变量,违背面向对象设计中高内聚、低耦合的原则。因此,使用时应考虑是否真的有必要将类设计为单例。
总之,单例模式 主要用于控制对象的实例化,确保系统中只有一个类的实例,并通过全局访问点来控制对象的使用。它适用于需要全局共享资源、统一管理的场景,如日志系统、数据库连接池等。尽管单例模式在某些场景下有助于提升系统的稳定性和效率,但也应谨慎使用,以避免全局状态管理复杂化或滥用全局访问带来的耦合问题。
三、在 Unity 中使用 单例模式
在 Unity 中,实现一个线程安全的普通类和MonoBehaviour 类的泛型单例时,必须考虑以下几点:
- 普通类单例:不能被
new
,并且在多线程环境下线程安全。- MonoBehaviour 单例:由于 MonoBehaviour 的实例是通过 Unity 的
AddComponent
创建的,不能直接通过new
,也需要保证在多线程环境下的安全性。
1、普通型(new)泛型单例模式
方法一:
public abstract class Singleton<T> where T : class, new()
{
private static T instance = null;
// 多线程安全机制
private static readonly object locker = new object();
public static T Instance
{
get
{
lock (locker)
{
if (instance == null)
instance = new T();
return instance;
}
}
}
}
方法二:
using System;
using System.Reflection;
/// <summary>
/// 单例
/// 继承需要一个非公有的无参构造函数
/// </summary>
/// <typeparam name="T">类名的泛型</typeparam>
public abstract class Singleton<T> where T : class
{
private static T instance = null;
// 多线程安全机制
private static readonly object locker = new object();
public static T Instance
{
get
{
// 线程锁
lock (locker)
{
if (null == instance)
{
// 反射获取实例
var octors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic) ;
// 获取无参数的非公共构造函数
var octor = Array.Find(octors, c => c.GetParameters().Length == 0);
// 没有则提示没有私有的构造函数
if (null == octor)
{
throw new Exception("No NonPublic constructor without 0 parameter");
}
// 实例化
instance = octor.Invoke(null) as T;
}
return instance;
}
}
}
/// <summary>
/// 构造函数
/// 避免外界new
/// </summary>
protected Singleton() { }
}
2、继承 MonoBehaviour 的泛型单例模式
using UnityEngine;
public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T instance = null;
private static readonly object locker = new object();
private static bool bAppQuitting;
public static T Instance
{
get
{
if (bAppQuitting)
{
instance = null;
return instance;
}
lock (locker)
{
if (instance == null)
{
// 保证场景中只有一个 单例
T[] managers = Object.FindObjectsOfType(typeof(T)) as T[];
if (managers.Length != 0)
{
if (managers.Length == 1)
{
instance = managers[0];
instance.gameObject.name = typeof(T).Name;
return instance;
}
else
{
Debug.LogError("Class " + typeof(T).Name + " exists multiple times in violation of singleton pattern. Destroying all copies");
foreach (T manager in managers)
{
Destroy(manager.gameObject);
}
}
}
var singleton = new GameObject();
instance = singleton.AddComponent<T>();
singleton.name = "(singleton)" + typeof(T);
singleton.hideFlags = HideFlags.None;
DontDestroyOnLoad(singleton);
}
instance.hideFlags = HideFlags.None;
return instance;
}
}
}
protected virtual void Awake()
{
bAppQuitting = false;
}
protected virtual void OnDestroy()
{
bAppQuitting = true;
}
}
3、单例使用方式
继承泛型单例模式即可
public class Test1 : MonoSingleton<Test1>{}
// 方法一
public class Test2 : Singleton<Test2>{}
// 方法二
public class Test2 : Singleton<Test2>{{private Test2(){}}
4、实现分析
-
普通类单例:
- 通过 lock 实现线程安全,避免了多线程环境下重复创建实例的问题。
- 将构造函数设为
protected
,确保外部不能通过new
关键字实例化该类。 - 在 Unity 中,适合管理游戏状态、配置数据等不需要与 Unity 引擎生命周期强绑定的对象。
-
MonoBehaviour 类单例:
- 使用了
lock
机制确保多线程环境中的安全性。 - 使用
FindObjectOfType<T>
检查场景中是否已经有该类型的实例,如果没有则创建新的对象并添加该组件。 - 通过
DontDestroyOnLoad
确保实例在场景切换时不被销毁,适合音频管理器、游戏控制器等需要跨场景保持的对象。
- 使用了
这两种单例的模式在 Unity 中广泛适用,可以有效地管理全局对象和跨场景对象。
四、原型模式 (Prototype Pattern)
原型模式 (Prototype Pattern) 是一种创建型设计模式,它允许通过复制现有的对象来创建新对象,而不是通过直接实例化类。这意味着你可以通过克隆原型对象来生成新的实例,而不必依赖类的构造函数。该模式的核心思想是,通过创建一个对象的副本(即原型)来避免昂贵的初始化操作。
在 C# 中,通常通过实现 ICloneable
接口或者自定义的克隆方法来实现原型模式。
1、原型模式的原理
原型模式的工作原理是:
- 定义一个原型接口或基类,用来提供克隆的方法(如
Clone
)。 - 具体的类实现该接口,提供自己的克隆方法,该方法返回当前对象的深拷贝或浅拷贝。
- 客户端可以通过克隆原型对象来创建新对象,而无需知道如何构造对象。
拷贝的类型可以分为:
- 浅拷贝 (Shallow Copy):只复制对象的基本数据类型,对于引用类型的字段,只复制引用地址。
- 深拷贝 (Deep Copy):不仅复制对象的所有值,还递归地复制所有引用对象,创建独立的副本。
2、什么时候使用原型模式
-
需要频繁创建相似对象时:如果系统需要创建大量相似或结构复杂的对象时,通过克隆原型对象来生成新对象可以提高性能。
-
对象的创建代价高昂时:如果对象的初始化非常复杂,包含很多属性设置或涉及昂贵的资源操作(如网络连接、文件系统操作),使用原型模式避免了重复创建过程。
-
避免对象过多的子类化:当不希望为了创建新对象而引入更多的子类或扩展类时,原型模式可以避免通过继承创建不同类型对象的繁琐过程。
-
需要保存对象的历史状态或备份时:如果需要将某个对象的状态备份或在某个时刻进行复制(如撤销操作、快照功能),可以通过原型模式来克隆对象。
3、使用原型模式的好处
-
提高对象创建效率:对于某些对象的创建过程非常复杂或耗时时,通过克隆现有对象(即原型)可以避免重复的复杂初始化,节省时间和资源。
-
避免子类膨胀:通过原型模式,程序不需要通过继承或工厂模式来生成对象,这可以减少子类的数量,避免继承层次的膨胀。
-
动态生成对象:原型模式允许在运行时动态生成对象,不需要事先知道具体类的名称。
-
减少对象依赖:克隆对象是独立于具体类的,原型模式不需要直接依赖构造函数,这降低了对象之间的耦合度。
-
灵活性高:通过克隆,可以对现有的对象进行修改后创建新的对象,特别适合那些需要频繁修改对象属性的场景。
4、使用原型模式的注意事项
-
克隆的深浅拷贝:对于引用类型字段,使用浅拷贝时需要特别小心,浅拷贝只复制引用,而不复制对象本身,可能导致两个对象共用同一份数据。若需要完全独立的对象,必须使用深拷贝。
-
复杂对象的克隆:当对象包含复杂的嵌套结构或其他依赖时,确保实现合适的深拷贝逻辑,否则可能会出现数据共享或数据覆盖问题。
-
性能考虑:尽管原型模式避免了对象的重复构造,但深拷贝可能引入较大的性能开销,特别是在对象嵌套复杂时。因此,在性能敏感的场景下要慎重考虑深拷贝的实现。
-
原型的可变性:当通过原型模式创建对象时,如果不慎修改了原型本身的状态,所有基于该原型创建的对象也可能受到影响。因此在设计时,需要确保原型对象的状态是安全的。
总之,原型模式 通过复制现有对象来生成新对象,避免了类构造的开销,特别适用于对象创建代价高昂或需要动态创建对象的场景。它提供了灵活的对象创建方式,减少了类的复杂性和耦合度。使用时需要根据具体需求选择浅拷贝或深拷贝,并确保对象的可复制性和独立性。
五、在 Unity 中使用 原型模式
在 Unity 中,原型模式可以应用于场景中需要频繁生成的对象,比如 3D 模型(如立方体、球体等)。通过原型模式,我们可以避免每次都从头实例化对象,而是通过复制现有的对象来创建新的实例。
我们将创建一个简单的原型模式应用,用于克隆 Unity 中的 3D 对象(立方体、球体等),并根据用户输入生成多个克隆对象。
步骤:
- 创建一个基类
ShapePrototype
,它提供克隆接口。- 创建不同的形状类,如立方体和球体,继承自
ShapePrototype
。- 使用原型模式通过克隆现有对象来创建新对象,而不是直接创建新对象。
1、 定义基类 ShapePrototype
using UnityEngine;
// 定义一个抽象的原型基类
public abstract class ShapePrototype : MonoBehaviour
{
// 定义一个抽象的克隆方法
public abstract ShapePrototype Clone();
// 通用的显示形状信息的方法
public abstract void DisplayInfo();
}
2、创建具体的形状类
2.1 立方体类
using UnityEngine;
public class Cube : ShapePrototype
{
// 重写克隆方法
public override ShapePrototype Clone()
{
// 实现浅拷贝,复制当前对象的属性
GameObject clone = Instantiate(this.gameObject);
return clone.GetComponent<ShapePrototype>();
}
public override void DisplayInfo()
{
Debug.Log("This is a Cube.");
}
}
2.2 球体类
using UnityEngine;
public class Sphere : ShapePrototype
{
// 重写克隆方法
public override ShapePrototype Clone()
{
// 实现浅拷贝,复制当前对象的属性
GameObject clone = Instantiate(this.gameObject);
return clone.GetComponent<ShapePrototype>();
}
public override void DisplayInfo()
{
Debug.Log("This is a Sphere.");
}
}
3、创建 ShapeSpawner
负责克隆对象
using UnityEngine;
public class ShapeSpawner : MonoBehaviour
{
public ShapePrototype cubePrototype; // 立方体原型
public ShapePrototype spherePrototype; // 球体原型
private void Update()
{
// 按下 C 键克隆立方体
if (Input.GetKeyDown(KeyCode.C))
{
ShapePrototype cubeClone = cubePrototype.Clone();
cubeClone.transform.position = new Vector3(Random.Range(-5, 5), 1, Random.Range(-5, 5));
cubeClone.DisplayInfo();
}
// 按下 S 键克隆球体
if (Input.GetKeyDown(KeyCode.S))
{
ShapePrototype sphereClone = spherePrototype.Clone();
sphereClone.transform.position = new Vector3(Random.Range(-5, 5), 1, Random.Range(-5, 5));
sphereClone.DisplayInfo();
}
}
}
4、设置场景中的原型对象
- 创建一个 Unity 场景,并添加一个空物体
ShapeSpawner
,将上面的ShapeSpawner
脚本挂载到该物体上。 - 在场景中创建一个立方体和一个球体,并将它们的
Cube
和Sphere
脚本分别挂载到立方体和球体上。 - 在
ShapeSpawner
脚本的cubePrototype
和spherePrototype
变量中,分别拖拽场景中的立方体和球体作为原型对象。
5、运行效果
- 按下
C
键:克隆一个新的立方体,随机放置在场景中,并在控制台输出 "This is a Cube." - 按下
S
键:克隆一个新的球体,随机放置在场景中,并在控制台输出 "This is a Sphere."
每次克隆都是基于场景中的原型对象,这样可以避免重新生成对象的开销,并且保证克隆对象与原型对象的所有属性相同。
节省资源,通过克隆现有对象,而不是每次都从头实例化对象,可以提高性能,尤其是在场景中需要大量生成对象时;灵活性,可以动态调整原型对象的属性,并通过克隆生成新的实例,而不需要修改对象的创建逻辑;方便扩展,如果需要新的形状,可以轻松扩展原型类,无需修改现有代码。
这种模式在 Unity 的游戏开发中非常适合用于生成大量相似对象(如敌人、物品、特效等)。
六、建造者模式 (Builder Pattern)
建造者模式 (Builder Pattern) 是一种创建型设计模式,它将复杂对象的构建过程与对象的表示分离,使得同样的构建过程可以创建不同的对象。建造者模式特别适用于那些构建步骤复杂且具有多种配置的对象。
在建造者模式中,通常有以下几个关键部分:
- Builder (建造者):定义了创建产品对象的步骤,通常是一个抽象类或接口。
- Concrete Builder (具体建造者):实现了
Builder
接口的类,负责具体的构建细节,创建具体的产品对象。- Director (指挥者):负责控制构建过程,按照一定的顺序调用
Builder
的方法来构建对象。- Product (产品对象):最终构建的复杂对象。
1、什么时候使用建造者模式
构建复杂对象时:如果一个对象的创建步骤非常复杂,需要按照一定的顺序构建多个部件,建造者模式可以帮助你将这些步骤组织清晰,避免直接调用复杂的构造函数。
需要生成多种类型对象时:当需要通过相同的构建过程生成不同类型或配置的对象时,建造者模式可以帮助你在同一个框架下灵活构建不同的产品。
构造函数参数过多时:如果一个类的构造函数有很多可选参数或者参数组合非常复杂,使用建造者模式可以避免构造函数的复杂性,通过链式调用来简化参数设置。
对象的创建流程固定但表示方式多样时:在一些场景下,产品的创建过程是相同的,但是生成的产品可能有不同的表现方式或配置。此时,建造者模式可以将构建流程抽象化,从而生成不同的产品。
2、使用建造者模式的好处
简化对象构建过程:建造者模式将复杂对象的构建步骤集中在一个地方,减少了创建对象的复杂度,尤其是当对象拥有多个可选参数或需要按特定步骤构建时。
灵活构建复杂对象:通过建造者模式,可以将对象的构建过程拆分成多个步骤,允许构建者在不同的构建过程中创建不同类型的对象或者配置不同的参数。
代码清晰、易于维护:建造者模式使代码更清晰,因为对象的构建逻辑被独立封装在
Builder
中,而不是混杂在产品对象内部。这使得代码易于维护和扩展。支持不同的产品变化:同样的构建过程可以生成不同的产品,通过使用不同的建造者,能够灵活应对产品的变化需求。
简化对象的可变参数管理:如果对象有很多可选参数,使用建造者模式可以避免复杂的构造函数,提供链式调用来设置参数,使得代码更加简洁。
3、建造者模式的注意事项
多个建造者的协作:如果你需要构建多个复杂对象,可以为每种对象提供不同的具体建造者,并通过同一个指挥者来协调构建过程。
对象的不可变性:建造者模式可以配合不可变对象使用,确保对象在构建完成后无法被修改。
建造顺序的灵活性:确保建造者的接口设计支持灵活的建造顺序,允许客户端根据需要选择不同的建造步骤。
总之,建造者模式通过分离对象的构建过程和表示方式,使得构建复杂对象的流程更加清晰和灵活。它非常适合用于那些具有多个可选参数、构建过程复杂或者有不同表示方式的对象。建造者模式的使用可以提高代码的可读性和可维护性,同时减少对象构建中的复杂性。
七、在 Unity 中使用 建造者模式
在 Unity 中,建造者模式可以应用于构建复杂的 3D 场景或对象。假设我们要在场景中建造一个由多个部分组成的复杂建筑(如房子),包括地基、墙壁、屋顶等,这正是使用建造者模式的典型场景。
我们将设计一个简单的场景,展示如何通过建造者模式生成不同配置的房屋。房屋由三部分组成:地基、墙壁和屋顶。通过建造者模式,我们可以灵活地创建不同样式的房屋。
步骤:
- 定义一个
House
类,包含地基、墙壁和屋顶。- 定义一个
IHouseBuilder
接口,声明创建房屋各部分的方法。- 创建具体的建造者类,如
WoodenHouseBuilder
和BrickHouseBuilder
,实现不同材料的房屋建造。- 创建一个
Director
类,用于控制房屋的建造流程。- 在 Unity 场景中通过建造者模式生成不同风格的房屋。
1、定义房屋类 House
using UnityEngine;
// 房屋类,包含房屋的各个部分
public class House
{
public GameObject Foundation { get; set; }
public GameObject Walls { get; set; }
public GameObject Roof { get; set; }
public void ShowHouseInfo()
{
Debug.Log("House has been built with Foundation, Walls, and Roof.");
}
}
2、定义建造者接口 IHouseBuilder
public interface IHouseBuilder
{
void BuildFoundation();
void BuildWalls();
void BuildRoof();
House GetResult();
}
3、 具体建造者类:木屋建造者 WoodenHouseBuilder
和砖屋建造者 BrickHouseBuilder
3.1 木屋建造者
using UnityEngine;
public class WoodenHouseBuilder : IHouseBuilder
{
private House house = new House();
public void BuildFoundation()
{
house.Foundation = GameObject.CreatePrimitive(PrimitiveType.Cube);
house.Foundation.transform.localScale = new Vector3(5, 0.5f, 5);
house.Foundation.GetComponent<Renderer>().material.color = Color.gray;
}
public void BuildWalls()
{
house.Walls = GameObject.CreatePrimitive(PrimitiveType.Cube);
house.Walls.transform.localScale = new Vector3(5, 2.5f, 5);
house.Walls.transform.position = new Vector3(0, 1.5f, 0);
house.Walls.GetComponent<Renderer>().material.color = Color.yellow; // 木墙
}
public void BuildRoof()
{
house.Roof = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
house.Roof.transform.localScale = new Vector3(5, 1, 5);
house.Roof.transform.position = new Vector3(0, 3f, 0);
house.Roof.GetComponent<Renderer>().material.color = Color.red;
}
public House GetResult()
{
return house;
}
}
3.2 砖屋建造者
using UnityEngine;
public class BrickHouseBuilder : IHouseBuilder
{
private House house = new House();
public void BuildFoundation()
{
house.Foundation = GameObject.CreatePrimitive(PrimitiveType.Cube);
house.Foundation.transform.localScale = new Vector3(5, 0.5f, 5);
house.Foundation.GetComponent<Renderer>().material.color = Color.gray;
}
public void BuildWalls()
{
house.Walls = GameObject.CreatePrimitive(PrimitiveType.Cube);
house.Walls.transform.localScale = new Vector3(5, 2.5f, 5);
house.Walls.transform.position = new Vector3(0, 1.5f, 0);
house.Walls.GetComponent<Renderer>().material.color = Color.red; // 砖墙
}
public void BuildRoof()
{
house.Roof = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
house.Roof.transform.localScale = new Vector3(5, 1, 5);
house.Roof.transform.position = new Vector3(0, 3f, 0);
house.Roof.GetComponent<Renderer>().material.color = Color.green;
}
public House GetResult()
{
return house;
}
}
4、定义指挥者 Director
public class Director
{
private IHouseBuilder houseBuilder;
public Director(IHouseBuilder builder)
{
houseBuilder = builder;
}
// 控制房屋的建造流程
public void ConstructHouse()
{
houseBuilder.BuildFoundation();
houseBuilder.BuildWalls();
houseBuilder.BuildRoof();
}
// 获取构建好的房屋
public House GetHouse()
{
return houseBuilder.GetResult();
}
}
5、 在 Unity 场景中使用建造者模式
using UnityEngine;
public class HouseBuilderExample : MonoBehaviour
{
void Start()
{
// 创建一个木屋建造者
IHouseBuilder woodenHouseBuilder = new WoodenHouseBuilder();
Director director = new Director(woodenHouseBuilder);
director.ConstructHouse();
House woodenHouse = director.GetHouse();
woodenHouse.ShowHouseInfo();
// 创建一个砖屋建造者
IHouseBuilder brickHouseBuilder = new BrickHouseBuilder();
Director brickDirector = new Director(brickHouseBuilder);
brickDirector.ConstructHouse();
House brickHouse = brickDirector.GetHouse();
brickHouse.ShowHouseInfo();
}
}
6、运行效果
- 当运行场景时,系统会根据不同的建造者生成不同的房屋类型(木屋和砖屋),并将房屋的各个部分放置在场景中。
WoodenHouseBuilder
会生成带有灰色地基、黄色墙壁和红色屋顶的木屋。BrickHouseBuilder
会生成带有灰色地基、红色砖墙和绿色屋顶的砖屋。
通过这两种建造者类的不同实现,我们展示了如何通过建造者模式灵活创建不同类型的复杂对象。
建造者模式允许分步骤创建对象,并且能够复用构建过程生成不同类型的产品。它也提高了代码的可读性和维护性,特别是在对象构建逻辑复杂时。
当对象的构建步骤固定,但最终产品可能有不同的表现方式时,建造者模式尤其适用。在 Unity 中,这种模式可以用于场景中复杂对象(如建筑物、场景)的灵活构建。
原文地址:https://blog.csdn.net/u014361280/article/details/142367836
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!