首页 > 解决方案 > 尽管坐标设置为 0、0、0,但具有许多子对象的对象不会显示在中间

问题描述

我正在做一个统一的魔方生成器。每个部分基本上都是一个 1x1 的立方体,在我的代码中,它会以一个更大的立方体的形状重复,作为一个空对象的子对象。空物在棋子的正中间,所有棋子的起源都在正中间。但是,当我将空白放置在场景的中心 (0, 0, 0) 时,它会出现在不同的位置。以下是编辑提供的一些图片:

如您所见,空的位于中心,坐标设置为 0, 0, 0

现在,当它有孩子并且坐标都为0时,它显示在不同的地方

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

    公共 GameObject PiecePrefab;
    公共 int CubeSize;
    Vector3 平均;
    矢量3 ijk;
    整数立方体计数 = 0;

    // 在第一帧更新之前调用 start
    无效开始()
    {
        //Vector3 orgpos = gameObject.transform.position;

        如果(立方体尺寸 <= 0)
        {
            立方体尺寸 = 1;
            Debug.LogError("立方体不能小于1!");
        }
        else if (CubeSize > 30)
        {
            立方体尺寸 = 30;
            Debug.LogError("立方体不应大于 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);
                    }
                    否则 if (k == CubeSize - 1 || k == 0)
                    {
                        CreatePiece(i, j, k);
                    }
                }
            }
        }

        平均/=立方体计数;
        gameObject.transform.position = avg;

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

        foreach(_Go 中的游戏对象 KuutionPala)
        {
            KuutionPala.transform.SetParent(transform);
        }

        //gameObject.transform.localPosition = orgpos;
       

        无效 CreatePiece(浮动 x,浮动 y,浮动 z)
        {
            ijk = new Vector3(x, y, z);
            平均 += ijk;
            立方体计数++;

            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;

(抱歉,如果代码错误)

标签: unity3dparent-child

解决方案


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

  • Pivot:将 Gizmo 定位在 GameObject 的实际轴心点,由 Transform 组件定义。
  • Center:根据选定的游戏对象将 Gizmo 定位在(几何)中心位置。

您的设置为Centerso 以更改单击按钮上的Center.


然后到你的代码

您目前只是希望/假设您的父母正确放置在0,0,0.

然后你生成从0到范围内的所有图块(CubeSize - 1)/2,然后想要将中心移回。

我宁愿反过来,预先计算正确的局部偏移量,并直接将瓦片作为具有正确偏移量的根的子节点生成。分为正反两个方向。

第 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 步:直接生成为具有正确偏移的子代

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;
}

在此处输入图像描述


推荐阅读