列表的深拷贝

6
这应该是一个相当简单的问题,但我已经尝试了几种方法来解决它,但结果总是一样的。
我正在尝试将包含游戏对象的列表复制到另一个列表中。问题是,似乎我正在复制引用,因为对原始列表中游戏对象所做的任何更改也会影响新列表中的对象,这是我不想发生的事情。从我读到的内容来看,我正在进行浅层复制而不是深层复制,因此我尝试使用以下代码克隆每个对象:
public static class ObjectCopier
    {
        /// <summary>
        /// Perform a deep Copy of the object.
        /// </summary>
        /// <typeparam name="T">The type of object being copied.</typeparam>
        /// <param name="source">The object instance to copy.</param>
        /// <returns>The copied object.</returns>
        public static GameObject Clone<GameObject>(GameObject source)
        {

            if (!typeof(GameObject).IsSerializable)
            {
                throw new ArgumentException("The type must be serializable ", "source: " + source);
            }

            // Don't serialize a null object, simply return the default for that object
            /*if (Object.ReferenceEquals(source, null))
            {
                return default(GameObject);
            }*/

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();
            using (stream)
            {
                formatter.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (GameObject)formatter.Deserialize(stream);
            }
        }
    }

我遇到了以下错误:

ArgumentException: 类型必须可序列化 参数名:source:SP0 (UnityEngine.GameObject) ObjectCopier.Clone[GameObject] (UnityEngine.GameObject source) (位于 Assets/Scripts/ScenarioManager.cs:121)

上面贴出的函数在这里被调用:

    void SaveScenario(){
        foreach(GameObject obj in sleManager.listOfSourcePoints){
            tempObj = ObjectCopier.Clone(obj);

            listOfScenarioSourcePoints.Add(tempObj);
            Debug.Log("Saved Scenario Source List Point");
        }
        foreach(GameObject obj in sleManager.listOfDestPoints){
            tempObj = ObjectCopier.Clone(obj);
            listOfScenarioDestPoints.Add(tempObj);
            Debug.Log("Saved Scenario Dest List Point");
        }
    }

    void LoadScenario(){
        sleManager.listOfSourcePoints.Clear();
        sleManager.listOfDestPoints.Clear ();
        foreach(GameObject obj in listOfScenarioSourcePoints){
            tempObj = ObjectCopier.Clone(obj);
            sleManager.listOfSourcePoints.Add(tempObj);
            Debug.Log("Loaded Scenario Source List Point");
        }
        foreach(GameObject obj in listOfScenarioDestPoints){
            tempObj = ObjectCopier.Clone(obj);
            sleManager.listOfDestPoints.Add(tempObj);
            Debug.Log("Loaded Scenario Dest List Point");
        }
    }

现在,在这里创建原始列表:
if (child.name == "DestinationPoints")
{
     parentDestinationPoints = child.gameObject;
     foreach (Transform grandChildDP in parentDestinationPoints.transform)
     {
          //Debug.Log("Added DP object named: " + grandChildDP.name);
          tempObj = grandChildDP.gameObject;
          listOfDestPoints.Add(tempObj);
          tempObj.AddComponent<DestinationControl>();
          tempObj.transform.renderer.material.color = Color.white;
     }
}

// Hide all SourcePoints in the scene
if (child.name == "SourcePoints")
{
    parentSourcePoints = child.gameObject;
    foreach (Transform grandChildSP in parentSourcePoints.transform)
    {
         tempObj = grandChildSP.gameObject;
         listOfSourcePoints.Add(tempObj);
         tempObj.transform.renderer.enabled = false;
    }
}

这个"tempObj"有[SerializeField]属性,所以我可能漏掉了什么。任何帮助都将不胜感激。

编辑:忘记提到,这个应用程序是在Unity3D中的。


1
据我所知,你不能简单地按照你尝试的方式创建一个GameObject的副本。我认为你需要实例化一个新的GameObject,然后将其添加到第二个列表中。 - Steven Mills
3个回答

3

我认为你需要使用 [Serializable] 属性将尝试克隆的类 GameObject 进行标记。同时,我也会让克隆方法变成通用的,这样它就不会被类型所限制:

/// <summary>
/// Creates a deep clone of an object using serialization.
/// </summary>
/// <typeparam name="T">The type to be cloned/copied.</typeparam>
/// <param name="o">The object to be cloned.</param>
public static T DeepClone<T>(this T o)
{
    using (MemoryStream stream = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(stream, o);
        stream.Position = 0;
        return (T)formatter.Deserialize(stream);
    }
}

我希望这可以帮到你。

编辑。也许可以使用类似以下方式的深度拷贝而不需要序列化:

class A
{
    // copy constructor
    public A(A copy) {}
}

// A referenced class implementing 
class B : IDeepCopy
{
    object Copy() { return new B(); }
}

class C : IDeepCopy
{
    A A;
    B B;
    object Copy()
    {
        C copy = new C();

        // copy property by property in a appropriate way
        copy.A = new A(this.A);
        copy.B = this.B.Copy();
     }
}

我还发现了一个名为Copyable的工具,它使用反射来提供对象的深度拷贝。希望这个信息对你有所帮助...


不幸的是,Grant是正确的,它不是一个可序列化的类。看起来这并不像我最初想的那么简单。 - user3019217
啊,非常抱歉,我还没有发布。我很快会删除我的回答。这将是寻找另一种执行深度克隆的方法的问题。 - MoonKnight

0

我觉得我把事情搞复杂了,最终在 XML 文件中保存了我需要的值(游戏对象位置)(反正迟早要做的)。不过还是感谢大家的帮助。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接