Unity Class深拷贝问题分析
- 前言
- 常用解决方案
- 1.手动复制字段
- 2.使用序列化工具
- 3.使用Instantiate方法(只能用于MonoBehaviour)
- 4.重写运算符赋值
- 5.使用Visual Scripting中提供的拷贝函数(推荐)
前言
在Unity项目中,我们面临一个读取数据表并深拷贝该类的问题。具体情况是这样的:我们需要从数据表中读取人物的数据,但在战斗过程中,人物的数据会不断发生变化。因此,我们需要一个数据类来存储人物的数据,并且希望在不修改原始数据表的情况下,创建一个副本用于战斗。
为了实现这一逻辑,我们采取了以下步骤:首先,我们将Json或Xml格式的数据表反序列化为原始数据类,使用工具进行反序列化操作。然后,我们对原始数据类进行深拷贝,创建一个战斗数据类的副本。通过这种方式,我们既可以使用原始数据表中的数据,又能在战斗过程中对战斗数据类进行修改,而不会影响原始数据表的功能。
这样做的好处是,我们可以在战斗中独立使用战斗数据类,而不会影响原始数据表的完整性。同时,通过深拷贝的方式,我们确保战斗数据类是一个全新的对象,可以独立于原始数据类进行修改,避免了对象引用带来的问题。
常用解决方案
1.手动复制字段
如果类的字段较少且结构简单,可以手动复制每个字段来创建新的对象。这需要逐个复制类的每个字段,并确保复制的是字段的值而不是引用。
public class MyClass
{
public int myInt;
public string myString;
public MyClass DeepCopy()
{
MyClass newObject = new MyClass();
newObject.myInt = myInt;
newObject.myString = myString;
return newObject;
}
}
2.使用序列化工具
使用JsonUtility、MsgPack、Protobuf 等工具库进行序列化和反序列化功能,并能够处理更复杂的类结构。通过将对象序列化为字节流,然后再反序列化为新的对象,可以实现深拷贝。
比如下面这种:
TIP:别用这个,这是我当前项目的,没有对应类用不了
/// <summary>
/// Json文件转实体类
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static List<T> JsonToObject<T>(string value) where T : ConfigJsonBase
{
List<T> lst = new List<T>();
var test = JsonConvert.DeserializeObject<ConfigJsonContainer>(value, settings);
test.CopyListToDic();
foreach (var item in test.dataMap)
{
T line1 = (T)item.Value;
lst.Add(line1);
}
return lst;
}
相当于再次从表中序列化一次获得一个全新的拷贝
3.使用Instantiate方法(只能用于MonoBehaviour)
使用 UnityEngine.Object.Instantiate
方法:如果需要复制 Unity 引擎的 GameObject
或 MonoBehaviour
,可以使用 Instantiate
方法来创建它们的副本。这个方法会创建一个全新的实例,包括所有的组件和属性,并将它们与原始对象解耦。
public class CopyExample : MonoBehaviour
{
public GameObject originalObject;
private GameObject copiedObject;
public void PerformCopy()
{
copiedObject = Instantiate(originalObject);
// 对复制对象进行进一步操作...
}
}
4.重写运算符赋值
下面这个案例是通过重写&
运算符实现创建一个新的类并将所有字段赋值。
通过使用&
运算符创建新对象时,可以通过将原始对象作为第一个参数传递给运算符,并忽略第二个参数,以触发运算符的重载。新的 newCard
对象将具有与原始对象相同的字段值。
这种方法依赖于运算符的重写,并且在使用时需要注意运算符的语义和正确使用方式。另外,由于 & 运算符通常与按位与操作相关,重写它来创建新对象可能会使代码的可读性降低。因此,在实际使用时,建议谨慎选择是否使用这种重写运算符的方法,以确保代码的清晰性和可维护性。
public class CfgCardProperties : ConfigJsonBase
{
public string _CardName;
public List<string> _CardIconPath;
public bool _IsShowCardUnder;
public static CfgCardProperties operator &(CfgCardProperties card, CfgCardProperties cfg)
{
CfgCardProperties newCard = new CfgCardProperties();
card.ID = cfg.ID;
card._CardName = cfg._CardName;
card._CardIconPath = cfg._CardIconPath != null ? new List<string>(cfg._CardIconPath) : null;
return newCard;
}
}
用法是
var cfg = new CfgCardProperties();
cfg &= originalCfgs[i];
5.使用Visual Scripting中提供的拷贝函数(推荐)
在 Unity Visual Scripting
中,CloneViaFakeSerialization
节点可以用于实现对象的深拷贝。该节点在 Bolt 或其他 Unity 可视化脚本工具中提供,它通过序列化和反序列化对象来创建其副本。
CloneViaFakeSerialization
节点的工作原理如下:
- 将要克隆的对象进行序列化,将其转换为字节流。
- 将字节流反序列化为一个新的对象。
在上面例子中只需要一行就可以实现类的深拷贝
using Unity.VisualScripting;
cfg= originalCfgs[i].CloneViaFakeSerialization();