尽管坐标设置为 0、0、0,但具有许多子项的对象不会出现在中间

Object with many children does not show up in the middle although it has coordinates set to 0, 0, 0

我正在用unity做一个魔方生成器。每个部分基本上都是一个 1x1 的立方体,它将在我的代码中以更大的立方体的形状重复作为空对象的子对象。空物恰好位于棋子的中间,所有棋子的起点都在中间。然而,当我把空的放在场景的中心时 (0, 0, 0) 它出现在不同的地方。 以下是小编的部分图片:

As you can see, the empty is in the center with coordinates set to 0, 0, 0

Now ,when it has children and the coordinates are all still 0, it shows in a different place

编辑: @derHugo 帮助了我,但现在我创建立方体并将空对象设置到立方体中间的代码不起作用。 这是完整的代码:

    public GameObject PiecePrefab;
    public int CubeSize;
    Vector3 avg;
    Vector3 ijk;
    int cubeCount = 0;

    // Start is called before the first frame update
    void Start()
    {
        //Vector3 orgpos = gameObject.transform.position;

        if (CubeSize <= 0)
        {
            CubeSize = 1;
            Debug.LogError("The cube can not be smaller than 1!");
        }
        else if (CubeSize > 30)
        {
            CubeSize = 30;
            Debug.LogError("The cube should not be bigger than 30!");
        }

        avg = new Vector3(0, 0, 0);

        for (float k = 0; k < CubeSize; k++)
        {
            for (float j = 0; j < CubeSize; j++)
            {
                for (float i = 0; i < CubeSize; i++)
                {
                    if (i == CubeSize - 1 || i == 0)
                    {
                        CreatePiece(i, j, k);
                    }
                    else if (j == CubeSize - 1 || j == 0)
                    {
                        CreatePiece(i, j, k);
                    }
                    else if (k == CubeSize - 1 || k == 0)
                    {
                        CreatePiece(i, j, k);
                    }
                }
            }
        }

        avg /= cubeCount;
        gameObject.transform.position = avg;

        var _Go = GameObject.FindGameObjectsWithTag("KuutionPala");

        foreach (GameObject KuutionPala in _Go)
        {
            KuutionPala.transform.SetParent(transform);
        }

        //gameObject.transform.localPosition = orgpos;
       

        void CreatePiece(float x, float y, float z)
        {
            ijk = new Vector3(x, y, z);
            avg += ijk;
            cubeCount++;

            Vector3 offset3D;
            offset3D = new Vector3(x / CubeSize, y / CubeSize, z / CubeSize);
            var Piece = Instantiate(PiecePrefab, offset3D, transform.rotation);
            Piece.transform.localScale /= CubeSize;

            //Debug.LogFormat("x:" + x);
            //Debug.LogFormat("y:" + y);
            //Debug.LogFormat("z:" + z);
        }
    }
}

我认为错误在这一行:

gameObject.transform.position = avg;

(抱歉,如果代码不好)

如上所述,Unity 中有两种枢轴模式(参见 Positioning GameObjects → Gizmo handle position toggles

  • Pivot: positions the Gizmo at the actual pivot point of the GameObject, as defined by the Transform component.
  • Center: positions the Gizmo at a (geometrical) center position based on the selected GameObjects.

您的设置为 Center,因此要更改该设置,请单击显示 Center 的按钮。


然后到你的代码

您目前 hoping/assuming 您的 parent 被正确放置在 0,0,0

然后你生成了 0(CubeSize - 1)/2 范围内的所有方块,然后想要将中心向后移动。

我宁愿反其道而行之,预先计算出正确的本地偏移量,并直接将瓦片生成为具有正确偏移量的根的 children。分正反方向。

第 1 步:那个本地位置是什么?

要了解一般数学知识,请看两个例子。

假设您有 3 个索引为 0、1、2 的立方体。它们有 1/3 的延伸,所以实际上位置需要看起来像

-0.5       0       0.5
  |  .  |  .  |  .  |

假设您有 4 个立方体,索引为 0、1、2、3 并延伸 1/4,那么位置需要看起来像

-0.5          0          0.5
  |  .  |  .  |  .  |  .  |

正如您所见,最简单的方法是

  1. 从最小位置开始(例如-0.5f * Vector3.one
  2. 始终为第一个偏移添加一半的扩展(例如 1/CubeSize * 0.5f * Vector3.one
  3. 添加扩展的偏移量乘以顶部的索引(例如1/CubeSize * new Vector3(x,y,z)

所以在一起像

// be sure to cast to float here otherwise you get rounded ints
var extends = 1 / (float)CubeSize;
var offset = (-0.5f + extends * 0.5f) * Vector3.one + extends * new Vector3(x,y,z);

第 2 步:直接以 children 生成并具有正确的偏移量

void CreatePiece(float x, float y, float z)
{
    var extends = 1 / (float)CubeSize;
    var offset = (-0.5f + extends * 0.5f) * Vector3.one + extends * new Vector3(x,y,z);
    var Piece = Instantiate(PiecePrefab, transform, false);
    // This basically equals doing something like
    //var Piece = Instantiate(PiecePrefab, transform.position, transform.rotation, transform);

    Piece.transform.localPosition = offset;
    Piece.transform.localScale = extends * Vector3.one;
}

然后您可以将代码缩减为

// Use a range so you directly clamp the value in the Inspector
[Range(1,30)]
public int CubeSize = 3;

// Start is called before the first frame update
void Start()
{
    UpdateTiles();
}

// Using this you can already test the method without entering playmode
// via the context menu of the component
[ContextMenu(nameof(UpdateTiles)])
public void UpdateTiles()
{
    // Destroy current children before spawning the new ones
    foreach(var child in GetComponentsInChildren<Transform>().Where(child => child != transform)
    {
        if(!child) continue;

        if(Application.isPlaying)
        {
            Destroy(child.gameObject);
        }
        else
        {
            DestroyImmediate(child.gameObject);
        }
    }

    if (CubeSize < 1)
    {
        CubeSize = 1;
        Debug.LogError("The cube can not be smaller than 1!");
    }
    else if (CubeSize > 30)
    {
        CubeSize = 30;
        Debug.LogError("The cube should not be bigger than 30!");
    }

    // For making things easier to read I would use x,y,z here as well ;)
    for (float x = 0; x < CubeSize; x++)
    {
        for (float y = 0; y < CubeSize; y++)
        {
            for (float z = 0; z < CubeSize; z++)
            {
                if (x == CubeSize - 1 || x == 0)
                {
                    CreatePiece(x, y, z);
                }
                else if (y == CubeSize - 1 || y == 0)
                {
                    CreatePiece(x, y, z);
                }
                else if (z == CubeSize - 1 || z == 0)
                {
                    CreatePiece(x, y, z);
                }
            }
        }
    }
}

private void CreatePiece(float x, float y, float z)
{
    var extends = 1 / (float)CubeSize;
    var offset = (-0.5f + extends * 0.5f) * Vector3.one + extends * new Vector3(x,y,z);
    var Piece = Instantiate(PiecePrefab, transform, false);
    Piece.transform.localPosition = offset;
    Piece.transform.localScale = extends * Vector3.one;
}