繁体   English   中英

Unity - 对象定位所需的帮助

[英]Unity - Help needed with Object Positioning

在统一对象定位方面需要一些帮助,我一直在尝试玩了将近两天但没有任何结果。

因此,如果您查看下图,则有 2 个对象。

在此处输入图片说明

一种是板材(红色)

一种是Cube(蓝色)

我从脚本创建了下面的整个对象。 生成板(红色对象)时,比例为 (4 x 0.4 x 4)。

至于立方体,请看这张图片,所以有 5 种可能的立方体类型,每种都有自己的矢量大小在此处输入图片说明

我想要实现的是每个立方体(蓝色)都有自己预定义的矢量大小,当脚本运行时,我想以整齐的方式将立方体相应地放置在板的顶部。

如果你看第一张图片,现在一切都很混乱,立方体在盘子外面,等等。有人知道什么是实现我想要的最好的方法吗?

下图是我想要实现的,一切都很整洁,放在盘子上。

在此处输入图片说明

谢谢,非常感谢!

编辑:立方体的数量不是固定的,基本上我们通过读取 TXT 文件来填充立方体,它告诉系统每个板的立方体数量以及立方体的尺寸是多少,我们只需要正确定位它。 例如,在读取 TXT 文件时,它可以告诉系统第一个板将有 4 个立方体和 X 大小。 (所以我们有 5 种不同类型的尺寸,根据尺寸,我们需要将其相应地放在盘子上)

我认为您需要根据板块的边界计算立方体(蓝色)的位置。 在这里检查:边界

假设您只将它们放置在 xz 平面中,您可以量化立方体的大小(0.8 的倍数),而忽略 y 维度。 然后你有一个 5 x 5 槽的盘子,你可以迭代地放置可用的立方体。

这是一个测试脚本,首先填充盘子的行(也就是水平),如有必要,首先拟合剩余的立方体列(也就是垂直)。 您可以简化此操作,但我认为根据您的需求进行调整很简单。

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

public class PositioningTest : MonoBehaviour {
    private const char FREE = 'x';

    private readonly Vector3Int _quantizedPlateSize = new Vector3Int(5, 1, 5);

    private readonly Vector3Int[] _quantizedCubeSize = new[]{
        new Vector3Int(1, 1, 1),
        new Vector3Int(1, 1, 1),
        new Vector3Int(2, 1, 1),
        new Vector3Int(2, 2, 2),
        new Vector3Int(4, 2, 2),
    };

    private Transform _plate;

    [SerializeField]
    private int[] _availableCubes;

    private void OnGUI() {
        if (GUI.Button(new Rect(10, 10, 100, 100), "Run")) {
            if (_availableCubes == null || _availableCubes.Length == 0)
                return;
            List<Vector3Int> cubes = new List<Vector3Int>();
            for (int i = 0; i < _availableCubes.Length; i++) {
                int sizeIndex = _availableCubes[i];
                if (sizeIndex < 0 || sizeIndex >= _quantizedCubeSize.Length) {
                    Debug.LogError("invalid size index " + sizeIndex);
                    return;
                }
                cubes.Add(_quantizedCubeSize[sizeIndex]);
            }
            PlaceCubesOnPlate(cubes, _quantizedPlateSize.x, _quantizedPlateSize.z);
        }
    }

    private void PlaceCubesOnPlate(List<Vector3Int> allCubes, int plateRows, int plateColumns) {
        if (allCubes == null || allCubes.Count == 0) {
            Debug.LogError("no cubes to position");
            return;
        }

        // reset the plate
        ResetPlate();
        char[,] plate = CreateEmptyPlate(plateRows, plateColumns);

        // sort them so we fit the biggest cubes first
        SortCubesDescending(ref allCubes);

        // index of the cube to be fit in next
        int currentCubeIndex = 0;

        // fit the cubes row first
        for (int z = 0; z < plate.GetLength(0); z++) {
            for (int x = 0; x < plate.GetLength(1); x++) {
                if (plate[z, x] != FREE)
                    continue;
                Vector3Int cube = allCubes[currentCubeIndex];
                if (TryPositionCubeRowFirst(ref plate, cube, z, x, GetChar(currentCubeIndex))) {
                    currentCubeIndex++;
                    if (currentCubeIndex == allCubes.Count) {
                        PrintPlate(plate);
                        return;
                    }
                }
            }
        }

        // fit remaining cubes column first
        for (int x = 0; x < plate.GetLength(1); x++) {
            for (int z = 0; z < plate.GetLength(0); z++) {
                if (plate[z, x] != FREE)
                    continue;
                Vector3Int cube = allCubes[currentCubeIndex];
                if (TryPositionCubeColumnFirst(ref plate, cube, z, x, GetChar(currentCubeIndex))) {
                    currentCubeIndex++;
                    if (currentCubeIndex == allCubes.Count) {
                        PrintPlate(plate);
                        return;
                    }
                }
            }
        }

        Debug.LogError("could not fit all cubes");
    }

    private char[,] CreateEmptyPlate(int rows, int columns) {
        char[,] plate = new char[columns, rows];
        for (int z = 0; z < plate.GetLength(0); z++) {
            for (int x = 0; x < plate.GetLength(1); x++) {
                plate[z, x] = FREE;
            }
        }
        return plate;
    }

    private void SortCubesDescending(ref List<Vector3Int> cubes) {
        cubes.Sort((c1, c2) => {
            int indexC1 = Array.IndexOf(_quantizedCubeSize, c1);
            int indexC2 = Array.IndexOf(_quantizedCubeSize, c2);
            return indexC2.CompareTo(indexC1);
        });
    }

    private void PrintPlate(char[,] plate) {
        StringBuilder sb = new StringBuilder("plate:\n");
        for (int z = 0; z < plate.GetLength(0); z++) {
            for (int x = 0; x < plate.GetLength(1); x++) {
                sb.Append(plate[z, x]);
                sb.Append(" ");
            }
            sb.Append("\n");
        }
        Debug.Log(sb.ToString());
    }

    private char GetChar(int index) {
        if (index >= 0 && index <= 15)
            return index.ToString("X")[0];
        return (char) (Math.Abs(index) - 16 + 97);
    }

    private bool TryPositionCubeRowFirst(ref char[,] plate, Vector3Int cubeSize, int columnIndex, int rowIndex, char representingChar) {
        for (int z = 0; z < cubeSize.z; z++) {
            for (int x = 0; x < cubeSize.x; x++) {
                if (columnIndex + z >= plate.GetLength(0)
                    || rowIndex + x >= plate.GetLength(1)
                    || plate[columnIndex + z, rowIndex + x] != FREE)
                    return false;
            }
        }

        for (int z = 0; z < cubeSize.z; z++) {
            for (int x = 0; x < cubeSize.x; x++) {
                plate[columnIndex + z, rowIndex + x] = representingChar;
            }
        }

        Transform newCube = CreateCube(representingChar.ToString(), _plate, new Vector3(rowIndex, 0, columnIndex));
        newCube.localScale = cubeSize;
        return true;
    }

    private bool TryPositionCubeColumnFirst(ref char[,] plate, Vector3Int cubeSize, int columnIndex, int rowIndex, char representingChar) {
        for (int z = 0; z < cubeSize.z; z++) {
            for (int x = 0; x < cubeSize.x; x++) {
                if (columnIndex + x >= plate.GetLength(0)
                    || rowIndex + z >= plate.GetLength(1)
                    || plate[columnIndex + x, rowIndex + z] != FREE)
                    return false;
            }
        }

        for (int z = 0; z < cubeSize.z; z++) {
            for (int x = 0; x < cubeSize.x; x++) {
                plate[columnIndex + x, rowIndex + z] = representingChar;
            }
        }

    
        Transform newCube = CreateCube(representingChar.ToString(), _plate, new Vector3(rowIndex, 0, columnIndex));
        newCube.localScale = new Vector3(cubeSize.z, cubeSize.y, cubeSize.x);
        return true;
    }

    private void ResetPlate() {
        if (_plate != null) {
            Destroy(_plate.gameObject);
        }
        _plate = new GameObject("plate").transform;
    }

    private Transform CreateCube(string name, Transform parent, Vector3 localPosition) {
        Transform pivot = new GameObject(name).transform;
        Transform cube = GameObject.CreatePrimitive(PrimitiveType.Cube).transform;
        cube.SetParent(pivot);
        cube.localPosition = new Vector3(0.5F, 0.5F, 0.5F);
        cube.localScale = new Vector3(0.9F, 0.9F, 0.9F);
        pivot.SetParent(parent);
        pivot.localPosition = localPosition;
        return pivot;
    }
}

注意:生成的调试输出是颠倒的(与统一场景相比),因为两者都只是理解算法的表示。

如果您只有一个立方体并且希望将其放置在盘子的中心,您可以在定位方法中添加一个检查,允许提前返回一个立方体(就像它已经有 0 个立方体一样)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM