C#中的数组缩小

时间:2017-09-12 21:49:38

标签: c# arrays unity3d

C#中是否有任何方法可以在不进行复制的情况下调整数组大小?我特别想要的是一种改变数组的Length属性的方法。该数组具有固定的最大长度,并且只会调整为小于或等于最大大小的大小(允许重用该数组)。

实际用例是Unity3D中的程序网格生成。执行此操作的内置方法仅限于使用列表(非常慢)并将Vector3s数组分配给mesh.vertices。我想要的是数组赋值方法,但是当你将一个数组赋给mesh.vertices时,没有办法指定要复制的数组元素的数量(mesh.vertices不是一个数组,并复制了将数组分配给GPU)。

当预先知道程序生成的网格中的顶点数量时,不可能制作正确大小的数组,并且必须采用慢速数组调整大小。

我知道C#通常不会允许我正在寻找的那种数组调整大小,但是我不得不问是否还有一半不错的低级别hackish方式仍然可以获得它完成了。

我现在使用的方法是分配一个最大顶点数的数组(只是为每个生成的网格重用),填充它,然后分配另一个右边的数组大小并将正确数量的数组元素复制到新数组(这比Array.Resize更快,并且肯定比使用列表的任何数据更快)。

编辑:根据要求更新了适当的代码(没有详细记录,要小心)。

这为每个块调用地图生成器(测试代码):

for (int z = 0; z < size; z++)
{
    for (int x = 0; x < size; x++)
    {
        Vector3 position = new Vector3(x * 32, 0, z * 32);

        GameObject a = Instantiate(baseObject);
        a.transform.position = position;

        map.GenMap(position, model); // procedural model generation

        blockMesh.SetModel(model); // set the model to generate the mesh from
        blockMesh.GenMesh(); // generate actual mesh
        blockMesh.CopyMesh(a.GetComponent<MeshFilter>().mesh); // copy mesh data do the Unity mesh
    }
}

简单的测试图生成器:

public void GenMap(Vector3 position, Model map)
{
    map.Clear();

    double par1 = 1000;
    double par2 = 600;
    double par3 = 500;

    double par4 = 1.0 / 50.0;
    double par5 = 1.0 / 25.0;
    double par6 = 1.0 / 12.5;

    double factor = (par1 + par2 + par3) / 16;

    if (factor != 0.0)
    {
        par1 /= factor;
        par2 /= factor;
        par3 /= factor;
    }

    Color32 color150 = map.palette[150];

    for (int z = 0; z < map.realDepth; z++)
    {
        double zPos = position.z + z;

        for (int x = 0; x < map.realWidth; x++)
        {
            double xPos = position.x + x;

            int e = 16 + (int)
            (
                (noise.Noise(xPos * par4, zPos * par4) * par1) +
                (noise.Noise(xPos * par5, zPos * par5) * par2) +
                (noise.Noise(xPos * par6, zPos * par6) * par3)
            );

            for (int y = 0; y < map.realHeight; y++)
            {
                if (y <= e)
                {
                    int a = (255 - (32 - y) * 9);

                    if (a < 1)
                        a = 1;

                    color150.a = (byte)(a | 1);
                    map.voxel[x][y][z] = color150;
                }
            }
        }
    }
}

这是实际的网格生成器代码(针对Mono 3.5进行了优化,目前尚未详细记录):

using UnityEngine;
using System;

public class BlockMesh
{
    public Vector3[] vertices = new Vector3[65532];
    public Vector3[] normals = new Vector3[65532];
    public Vector2[] uvs = new Vector2[65532];
    public Color32[] colors = new Color32[65532];
    public int[] triangles = new int[65532 * 6 / 4];
    public int index = 0;
    public Model model;

    // set model and init arrays

    public void SetModel(Model model)
    {
        this.model = model;
    }

    // copy mesh data to mesh

    public void CopyMesh(Mesh mesh)
    {
        Vector3[] a = new Vector3[index * 4];

        Array.Copy(vertices, a, index * 4);
        mesh.vertices = a;

        Array.Copy(normals, a, index * 4);
        mesh.normals = a;

        Color32[] b = new Color32[index * 4];
        Array.Copy(colors, b, index * 4);
        mesh.colors32 = b;

        int[] c = new int[index * 6];
        Array.Copy(triangles, c, index * 6);
        mesh.triangles = c;

        Vector2[] d = new Vector2[index * 4];

        Array.Copy(uvs, d, index * 4);
        mesh.uv = d;
    }

    // ambient occlusion uv constants

    private const float pixelSize = 1.0f / 4096.0f;
    private const float tileSize = pixelSize * 256;
    private const float adjust1 = pixelSize * 64;
    private const float adjust2 = tileSize - 2 * adjust1;

    // cube faces

    private Vector3[] faceVertices1 = { new Vector3(0, 1, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 0, 1), new Vector3(1, 0, 1) };
    private Vector3[] faceVertices2 = { new Vector3(1, 1, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 0), new Vector3(1, 0, 1), new Vector3(0, 0, 0), new Vector3(1, 0, 0) };
    private Vector3[] faceVertices3 = { new Vector3(0, 1, 1), new Vector3(0, 0, 1), new Vector3(0, 1, 0), new Vector3(0, 1, 1), new Vector3(0, 1, 1), new Vector3(1, 1, 1) };
    private Vector3[] faceVertices4 = { new Vector3(1, 1, 1), new Vector3(1, 0, 1), new Vector3(1, 1, 0), new Vector3(1, 1, 1), new Vector3(0, 1, 0), new Vector3(1, 1, 0) };

    private Vector3[] faceNormals = { Vector3.up, Vector3.down, Vector3.back, Vector3.forward, Vector3.left, Vector3.right };

    private void AddFace(int side, Vector3 position, Color32 color, int aop)
    {
        // mesh array indices

        int index4 = index * 4;
        int index6 = index * 6;

        // ambient occlusion uv cords

        float x1 = tileSize * (aop & 15) + adjust1;
        float y1 = tileSize * (aop >> 4) + adjust1;

        float x2 = x1 + adjust2;
        float y2 = y1 + adjust2;

        // put face into mesh

        vertices[index4 + 0] = position + faceVertices1[side];
        vertices[index4 + 1] = position + faceVertices2[side];
        vertices[index4 + 2] = position + faceVertices3[side];
        vertices[index4 + 3] = position + faceVertices4[side];

        uvs[index4 + 0].x = x1;
        uvs[index4 + 0].y = y1;
        uvs[index4 + 1].x = x2;
        uvs[index4 + 1].y = y1;
        uvs[index4 + 2].x = x1;
        uvs[index4 + 2].y = y2;
        uvs[index4 + 3].x = x2;
        uvs[index4 + 3].y = y2;

        normals[index4 + 0] = faceNormals[side];
        normals[index4 + 1] = faceNormals[side];
        normals[index4 + 2] = faceNormals[side];
        normals[index4 + 3] = faceNormals[side];

        colors[index4 + 0] = color;
        colors[index4 + 1] = color;
        colors[index4 + 2] = color;
        colors[index4 + 3] = color;

        int tri = 0x08790936 >> ((side & 1) << 4);

        triangles[index6 + 0] = index4 + ((tri >> 10) & 3);
        triangles[index6 + 1] = index4 + ((tri >> 08) & 3);
        triangles[index6 + 2] = index4 + ((tri >> 06) & 3);
        triangles[index6 + 3] = index4 + ((tri >> 04) & 3);
        triangles[index6 + 4] = index4 + ((tri >> 02) & 3);
        triangles[index6 + 5] = index4 + (tri & 3);

        index += 1;
    }

    // build mesh

    public void GenMesh()
    {
        index = 0;

        for (int z = 1; z <= model.depth; z++)
        {
            for (int y = 1; y <= model.height; y++)
            {
                for (int x = 1; x <= model.width; x++)
                {
                    Color32 voxelColor = model.voxel[x][y][z];

                    if (voxelColor.a == 0)
                        continue;

                    if (model.voxel[x][y + 1][z].a == 0) // top
                    {
                        AddFace(0, new Vector3(x, y, z), voxelColor,
                            (((model.voxel[x - 1][y + 1][z - 1].a) & 1) << 7) |
                            (((model.voxel[x + 0][y + 1][z - 1].a) & 1) << 6) |
                            (((model.voxel[x + 1][y + 1][z - 1].a) & 1) << 5) |
                            (((model.voxel[x - 1][y + 1][z + 0].a) & 1) << 4) |
                            (((model.voxel[x + 1][y + 1][z + 0].a) & 1) << 3) |
                            (((model.voxel[x - 1][y + 1][z + 1].a) & 1) << 2) |
                            (((model.voxel[x + 0][y + 1][z + 1].a) & 1) << 1) |
                            (((model.voxel[x + 1][y + 1][z + 1].a) & 1)));
                    }

                    if (model.voxel[x][y - 1][z].a == 0) // bottom
                    {
                        AddFace(1, new Vector3(x, y, z), voxelColor,
                            (((model.voxel[x - 1][y - 1][z - 1].a) & 1) << 7) |
                            (((model.voxel[x + 0][y - 1][z - 1].a) & 1) << 6) |
                            (((model.voxel[x + 1][y - 1][z - 1].a) & 1) << 5) |
                            (((model.voxel[x - 1][y - 1][z + 0].a) & 1) << 4) |
                            (((model.voxel[x + 1][y - 1][z + 0].a) & 1) << 3) |
                            (((model.voxel[x - 1][y - 1][z + 1].a) & 1) << 2) |
                            (((model.voxel[x + 0][y - 1][z + 1].a) & 1) << 1) |
                            (((model.voxel[x + 1][y - 1][z + 1].a) & 1)));
                    }

                    if (model.voxel[x][y][z - 1].a == 0) // back
                    {
                        AddFace(2, new Vector3(x, y, z), voxelColor,
                            (((model.voxel[x - 1][y - 1][z - 1].a) & 1) << 7) |
                            (((model.voxel[x + 0][y - 1][z - 1].a) & 1) << 6) |
                            (((model.voxel[x + 1][y - 1][z - 1].a) & 1) << 5) |
                            (((model.voxel[x - 1][y + 0][z - 1].a) & 1) << 4) |
                            (((model.voxel[x + 1][y + 0][z - 1].a) & 1) << 3) |
                            (((model.voxel[x - 1][y + 1][z - 1].a) & 1) << 2) |
                            (((model.voxel[x + 0][y + 1][z - 1].a) & 1) << 1) |
                            (((model.voxel[x + 1][y + 1][z - 1].a) & 1)));
                    }

                    if (model.voxel[x][y][z + 1].a == 0) // front
                    {
                        AddFace(3, new Vector3(x, y, z), voxelColor,
                            (((model.voxel[x - 1][y - 1][z + 1].a) & 1) << 7) |
                            (((model.voxel[x + 0][y - 1][z + 1].a) & 1) << 6) |
                            (((model.voxel[x + 1][y - 1][z + 1].a) & 1) << 5) |
                            (((model.voxel[x - 1][y + 0][z + 1].a) & 1) << 4) |
                            (((model.voxel[x + 1][y + 0][z + 1].a) & 1) << 3) |
                            (((model.voxel[x - 1][y + 1][z + 1].a) & 1) << 2) |
                            (((model.voxel[x + 0][y + 1][z + 1].a) & 1) << 1) |
                            (((model.voxel[x + 1][y + 1][z + 1].a) & 1)));
                    }

                    if (model.voxel[x - 1][y][z].a == 0) // left
                    {
                        AddFace(4, new Vector3(x, y, z), voxelColor,
                            (((model.voxel[x - 1][y - 1][z + 1].a) & 1) << 7) |
                            (((model.voxel[x - 1][y - 1][z + 0].a) & 1) << 6) |
                            (((model.voxel[x - 1][y - 1][z - 1].a) & 1) << 5) |
                            (((model.voxel[x - 1][y + 0][z + 1].a) & 1) << 4) |
                            (((model.voxel[x - 1][y + 0][z - 1].a) & 1) << 3) |
                            (((model.voxel[x - 1][y + 1][z + 1].a) & 1) << 2) |
                            (((model.voxel[x - 1][y + 1][z + 0].a) & 1) << 1) |
                            (((model.voxel[x - 1][y + 1][z - 1].a) & 1)));
                    }

                    if (model.voxel[x + 1][y][z].a == 0) // right
                    {
                        AddFace(5, new Vector3(x, y, z), voxelColor,
                            (((model.voxel[x + 1][y - 1][z + 1].a) & 1) << 7) |
                            (((model.voxel[x + 1][y - 1][z + 0].a) & 1) << 6) |
                            (((model.voxel[x + 1][y - 1][z - 1].a) & 1) << 5) |
                            (((model.voxel[x + 1][y + 0][z + 1].a) & 1) << 4) |
                            (((model.voxel[x + 1][y + 0][z - 1].a) & 1) << 3) |
                            (((model.voxel[x + 1][y + 1][z + 1].a) & 1) << 2) |
                            (((model.voxel[x + 1][y + 1][z + 0].a) & 1) << 1) |
                            (((model.voxel[x + 1][y + 1][z - 1].a) & 1)));
                    }
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:2)

经过大量谷歌搜索后,我偶然发现了我正在寻找的不安全的黑客方法。我是为了完整性而发布它,但它似乎不是任何人都想在生产代码中使用的解决方案。

你不想使用非常脏的黑客:

using System;
using UnityEngine;

public unsafe class DirtyHack
{
    public Vector3[] vertices = new Vector3[65532];
    public int index;

    public void DirtyCopy(Mesh mesh)
    {
        fixed (void* ptr = vertices)
        {
            *((UIntPtr*)ptr - 1) = (UIntPtr)(index * 4);
            mesh.vertices = vertices;
            *((UIntPtr*)ptr - 1) = (UIntPtr)(65532);
        }
    }
}

编辑:更改代码以防止在数组调整大小期间GC移动数组。