[英]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.