【问题标题】:Unity C# - Class within Array is Overriden instead of creating copy (mutiple scripts)Unity C# - 数组中的类被覆盖而不是创建副本(多个脚本)
【发布时间】:2016-10-12 15:39:17
【问题描述】:

我正在尝试统一制作一个瓦片网格图,其中每个瓦片都是一个包含某些信息的类。所有的瓦片信息都保存在一个名为 TileData 的脚本中。我有四种瓷砖类型,每一种都有 4 个变量。 (平铺图形、平铺名称、平铺位置 X 和平铺位置 Z。)用于构建地图的所有信息都包含在第二个脚本中。 (我在地图脚本中调用了本地版本的 tiledata 脚本。)

我可以通过使用一个多维数组来成功构建带有瓷砖的网格,该数组从瓷砖类数组中随机选择一个瓷砖类。 我的问题是,当我尝试为图块位置分配位置 x 和位置 z 时,它会覆盖图块数组中的信息。最终结果是每个图块显示所有其他类似图块的 x、z,而不是单独显示。

我想要做的是构建一个随机类瓦片的二维数组,但为每个新瓦片分配自己的 x 和 z 坐标。 (我打算稍后将其用于移动/寻路等)

我尝试了所有我能想到的方法,甚至尝试了一个多维锯齿状数组,但无法让它工作。

下面的代码(去掉了不相关的部分):

TileData 脚本

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TileData {

[System.Serializable]
public class Tile                                                                   //Public class Tile to act as Tile Template.
{
    public int tileGraphic = 0;                                                     //An int to point to our Tile Graphic
    public string tileName = "Unknown";                                             //Name of the Tile.

    public int tilePositionX = 0;
    public int tilePositionZ = 0;
}

public Tile[] tileTypes = new Tile[4];                                              //Makes a new Tile Array from the Tile Class

public void makeArray()                                                             //Constructs the array.
{
    tileTypes[0] = new Tile { tileGraphic = 0, tileName = "Grassland"};            //Makes the new Tile and puts them in the array.
    tileTypes[1] = new Tile { tileGraphic = 1, tileName = "Water"};
    tileTypes[2] = new Tile { tileGraphic = 2, tileName = "Forest"};
    tileTypes[3] = new Tile { tileGraphic = 3, tileName = "Mountain"};
}

地图脚本

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;

    [ExecuteInEditMode]
    [RequireComponent(typeof(MeshFilter))]                                                          //Ensures the object has a filter, Renderer and Collider when created.
    [RequireComponent(typeof(MeshRenderer))]
    [RequireComponent(typeof(MeshCollider))]

   public class missionMap : MonoBehaviour
   {

    //Map Variables

    public int sizeX = 25;                                                                      //The left/right size of the map (in tiles). Public so it can be changed from the editor.
    public int sizeZ = 25;                                                                      //The forward/backward size of the map (in tiles).

    public float tileSize = 1.0f;                                                               //The size of each tile. (How many vertices wide the tile is.)

    //Tile Variables

    public TileData localTileData = new TileData();                                             //Pulls the tile types in from their own script.
    public TileData.Tile[,] localTileArray = new TileData.Tile[25, 25];                         //Builds an Array of our Tile class the size of our map, so each tile on the map has a Tile Class corresponding to it.    

    void Start()
    {
        localTileData.makeArray();                                                              //Calls the makeArray from the Tile Data script, so we can access the array later.

        //localTileArray[0, 0] = localTileArray[sizeX,sizeZ];

        BuildMap();                                                                             //Builds the map when the code starts.
        //BuildPathGraph();
    }
   }

我省略了地图的实际构建并跳到图块。

public void BuildTiles()
{
    for (int z = 0; z < sizeZ; z++)                                                     //A double for loop to populate our tile position array with the size of the our map in x and z.
    {
        for (int x = 0; x < sizeX; x++)
        {

            localTileArray[x, z] = localTileData.tileTypes[Random.Range(0, 4)];          //Fills the Array with Tile Copies of type 0 - 3 (0 = grass, 2 = water, 3 = forest, 4 = mountain.)

        }
    }

【问题讨论】:

    标签: c# arrays class multidimensional-array unity3d


    【解决方案1】:

    问题可能出在您忽略的部分:地图的构建。 你在做这样的事情吗?

    for (int z = 0; z < sizeZ; z++)
    {
        for (int x = 0; x < sizeX; x++)
          {
    
            localTileArray[x, z] = localTileData;
        }
    }
    

    如果是这种情况,您正在做的是将localTileData 的引用 分配给localTileArray 中的所有元素。这就是为什么修改一个引用会影响数组中的所有元素:它们都是相同的。这是因为在 C# 中,类是 Reference Types

    解决方案是@graju256 谈到的:使用 .MemberwiseClone() 创建 localTileArray 引用的副本

    另一种解决方案是将 Tile 从类更改为 Struct 并将其转换为 Value Type,因此当您将 localTileData 分配给数组中的元素时,会创建一个副本。

    【讨论】:

    • .MemberwiseClone() 有一个错误为无效,所以我查看了 Structs。我很犹豫,因为我以前从未使用过它们。但是,到目前为止它运行良好。我将不得不花更多时间在文档上来理解结构和类之间的区别,因为它们在我看来在很大程度上是可以互换的。谢谢。
    • 看看我在答案中给出的关于值/引用类型的链接,在学习类和结构之间的区别的过程中,你将完成一半的工作;)
    【解决方案2】:

    在构建 localTileArray 时克隆选定的 tile 实例。这样,您就可以避免在程序中修改/引用相同的实例。

    您可以重新考虑您的 TILE 设计。为您的 TILE 类型声明枚举并在创建时传递它。在构建内部数组时使用它的类型创建新的 TILE。这样,您就可以避免维护 TILE 类型的实例。

    【讨论】:

    • 我不太清楚你的意思,你能举个代码例子吗?如果我制作一个新的 TILE,它不会是四种类型之一,它只是一个基本的 TILE。
    • 使用 MemberwiseClone 方法克隆您的 TILE 对象。 localTileArray[x, z] = localTileData.tileTypes[Random.Range(0, 4)].MemberwiseClone(); 由于 TILE 类是由值类型组成的,所以可以通过浅拷贝克隆对象。否则,我们应该通过实现 ICloneable 接口来考虑深拷贝。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 2012-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多