问题需求
其实就是某个物体在随机运动,如果运动到正方形的某个面的上方的话,就在这个面上随机出来一个点降落下去。
解决方案
步骤1:物体向六个方向发射射线检测正方体位置。
射线检测,很简单,一句代码就不说了。
步骤2:确定物体下方所在正方体相对应的面(即获得在该面的四个坐标)。
//通过Boxcollider获取正方体实际八个顶点
Vector3[] GetBoxColliderVertexPositions(BoxCollider boxcollider)
{
var vertices = new Vector3[8];
//下面4个点
vertices[0] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, -boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[1] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, -boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[2] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, -boxcollider.size.y, -boxcollider.size.z) * 0.5f);
vertices[3] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, -boxcollider.size.y, -boxcollider.size.z) * 0.5f);
//上面4个点
vertices[4] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[5] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[6] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, boxcollider.size.y, -boxcollider.size.z) * 0.5f);
vertices[7] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, boxcollider.size.y, -boxcollider.size.z) * 0.5f);
return vertices;
}
//通过Boxcollider获取正方体最大范围的八个顶点
Vector3[] NewMaxBoxColliderPositions(BoxCollider boxcollider)
{
var vertices = new Vector3[8];
Bounds bounds = boxcollider.bounds;
vertices[0] = bounds.min;
vertices[1] = bounds.max;
vertices[2] = new Vector3(vertices[0].x, vertices[0].y, vertices[1].z);
vertices[3] = new Vector3(vertices[0].x, vertices[1].y, vertices[0].z);
vertices[4] = new Vector3(vertices[1].x, vertices[0].y, vertices[0].z);
vertices[5] = new Vector3(vertices[0].x, vertices[1].y, vertices[1].z);
vertices[6] = new Vector3(vertices[1].x, vertices[0].y, vertices[1].z);
vertices[7] = new Vector3(vertices[1].x, vertices[1].y, vertices[0].z);
Debug.DrawLine(vertices[5], vertices[1], Color.green, int.MaxValue);
Debug.DrawLine(vertices[1], vertices[7], Color.green, int.MaxValue);
Debug.DrawLine(vertices[7], vertices[3], Color.green, int.MaxValue);
Debug.DrawLine(vertices[3], vertices[5], Color.green, int.MaxValue);
// bottom of rectangular cuboid (3-7-5-1)
Debug.DrawLine(vertices[2], vertices[6], Color.red, int.MaxValue);
Debug.DrawLine(vertices[6], vertices[4], Color.red, int.MaxValue);
Debug.DrawLine(vertices[4], vertices[0], Color.red, int.MaxValue);
Debug.DrawLine(vertices[0], vertices[2], Color.red, int.MaxValue);
// legs (6-3, 2-7, 8-5, 4-1)
Debug.DrawLine(vertices[5], vertices[2], Color.yellow, int.MaxValue);
Debug.DrawLine(vertices[1], vertices[6], Color.yellow, int.MaxValue);
Debug.DrawLine(vertices[7], vertices[4], Color.yellow, int.MaxValue);
Debug.DrawLine(vertices[3], vertices[0], Color.yellow, int.MaxValue);
return vertices;
}
这两个方法的区别在于方法1是通过Boxcollider获取正方体实际八个顶点,即无论你如何操作正方体(移动或者旋转,当然因为是Boxcollider,这个也可能是长方体),但这八个顶点的位置都是准确的。
方法2呢则是Boxcollider获得最大和最小的顶点,然后通过最大和最小的顶点来画出一个对应的正方体。这个正方体旋转一定为0,这八个顶点是根据做大和最小的坐标计算出来的,所以位置并不一定和原正方体的的位置重合(除非原物体是正方体且没有偏转),当然,这八个顶点一定是正方体。
简单来讲,方法1获得的是下图红色箭头所指的实体正方体的的八个顶点。
方法二获得的是下图绿色箭头所指的画线正方体的的八个顶点。
然后从里面找到距离最近的四个点即为最近的面。
步骤3:随机目标点。
这个贼TM难,在方法1得到的正确的八个顶点的情况下,只有在正方体没有旋转的情况下
(即transform.localRotation = Quaternion.Euler(Vector3.zero))才能正确随机到点。
在方法2得到的正确的八个顶点的情况下,随机不存在问题,但很有可能物体无法无缝到达正方体表面。
当然两个方法都能通过新增判断比如碰到正方体停止或者直接设定物体旋转为0来达成需求。
但我仍然想知道在方法1的情况下,怎么能随机到一个正确的点。
即在3D场景的一个面上随机一个点。我查了很多资料,很少有相关的描述,也可能是是有相关的描述我也看不懂,希望有大佬能给个思路或者方法。
以下是我的代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//脚本在需要降落的物体上即可
public class TestMove : MonoBehaviour
{
/// <summary>
/// 是否发现可降落物体
/// </summary>
public bool IsFindPos;
/// <summary>
/// 球移动速度
/// </summary>
public float MoveSpeed = 1.0f;
/// <summary>
/// 获得的平面的四个点
/// </summary>
public Vector3[] GetPos = new Vector3[4];
// Update is called once per frame
void Update()
{
// Cube.transform.Rotate(Vector3.up);
if (IsFindPos == false)
{
//模拟位移
transform.Translate(Vector3.down * Time.deltaTime * MoveSpeed, Space.World);
//6个方向检测
FireRay(transform.TransformDirection(Vector3.up));
FireRay(transform.TransformDirection(Vector3.down));
FireRay(transform.TransformDirection(Vector3.right));
FireRay(transform.TransformDirection(Vector3.left));
FireRay(transform.TransformDirection(Vector3.forward));
FireRay(transform.TransformDirection(Vector3.back));
}
}
//通过Boxcollider获取正方体实际八个顶点
Vector3[] GetBoxColliderVertexPositions(BoxCollider boxcollider)
{
var vertices = new Vector3[8];
//下面4个点
vertices[0] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, -boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[1] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, -boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[2] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, -boxcollider.size.y, -boxcollider.size.z) * 0.5f);
vertices[3] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, -boxcollider.size.y, -boxcollider.size.z) * 0.5f);
//上面4个点
vertices[4] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[5] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, boxcollider.size.y, boxcollider.size.z) * 0.5f);
vertices[6] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(-boxcollider.size.x, boxcollider.size.y, -boxcollider.size.z) * 0.5f);
vertices[7] = boxcollider.transform.TransformPoint(boxcollider.center + new Vector3(boxcollider.size.x, boxcollider.size.y, -boxcollider.size.z) * 0.5f);
return vertices;
}
//通过Boxcollider获取正方体最大范围的八个顶点
Vector3[] NewMaxBoxColliderPositions(BoxCollider boxcollider)
{
var vertices = new Vector3[8];
Bounds bounds = boxcollider.bounds;
vertices[0] = bounds.min;
vertices[1] = bounds.max;
vertices[2] = new Vector3(vertices[0].x, vertices[0].y, vertices[1].z);
vertices[3] = new Vector3(vertices[0].x, vertices[1].y, vertices[0].z);
vertices[4] = new Vector3(vertices[1].x, vertices[0].y, vertices[0].z);
vertices[5] = new Vector3(vertices[0].x, vertices[1].y, vertices[1].z);
vertices[6] = new Vector3(vertices[1].x, vertices[0].y, vertices[1].z);
vertices[7] = new Vector3(vertices[1].x, vertices[1].y, vertices[0].z);
Debug.DrawLine(vertices[5], vertices[1], Color.green, int.MaxValue);
Debug.DrawLine(vertices[1], vertices[7], Color.green, int.MaxValue);
Debug.DrawLine(vertices[7], vertices[3], Color.green, int.MaxValue);
Debug.DrawLine(vertices[3], vertices[5], Color.green, int.MaxValue);
// bottom of rectangular cuboid (3-7-5-1)
Debug.DrawLine(vertices[2], vertices[6], Color.red, int.MaxValue);
Debug.DrawLine(vertices[6], vertices[4], Color.red, int.MaxValue);
Debug.DrawLine(vertices[4], vertices[0], Color.red, int.MaxValue);
Debug.DrawLine(vertices[0], vertices[2], Color.red, int.MaxValue);
// legs (6-3, 2-7, 8-5, 4-1)
Debug.DrawLine(vertices[5], vertices[2], Color.yellow, int.MaxValue);
Debug.DrawLine(vertices[1], vertices[6], Color.yellow, int.MaxValue);
Debug.DrawLine(vertices[7], vertices[4], Color.yellow, int.MaxValue);
Debug.DrawLine(vertices[3], vertices[0], Color.yellow, int.MaxValue);
return vertices;
}
RaycastHit hit;
//指定方向发射射线
public void FireRay(Vector3 dir)
{
if (Physics.Raycast(transform.position, dir, out hit, 20f))
{
//是否找到着陆点
if (hit.collider.name == "Cube")
{
IsFindPos = true;
Debug.DrawLine(transform.position, hit.point, Color.green, int.MaxValue);
Vector3[] veces;
//因为如果不规则的话并不能找到随机点的方法,所以判断以适应我自己粗糙的方法。
if (hit.collider.transform.localRotation == Quaternion.Euler(Vector3.zero))
{
veces = GetBoxColliderVertexPositions(hit.collider.GetComponent<BoxCollider>());
}
else
{
veces = NewMaxBoxColliderPositions(hit.collider.GetComponent<BoxCollider>());
}
Dictionary<Vector3, float> PosArry = new Dictionary<Vector3, float>();
float[] FloatAry = new float[8];
for (int i = 0; i < veces.Length; i++)
{
Debug.LogError(veces[i]);
FloatAry[i] = Vector3.Distance(transform.position, veces[i]);
PosArry.Add(veces[i], Vector3.Distance(transform.position, veces[i]));
}
Array.Sort(FloatAry);
//通过字典和数组排序找出所需平面的四个点
for (int i = 0; i < 4; i++)
{
foreach (var item in PosArry)
{
if (item.Value == FloatAry[i])
{
GetPos[i] = item.Key;
PosArry.Remove(item.Key);
break;
}
}
}
RangePos();
}
}
}
//无旋转情况下,随机获得面的一个点,
public Vector3 RangePos()
{
Vector3 Pos = new Vector3();
float[] X_ary = new float[4];
float[] Y_ary = new float[4];
float[] Z_ary = new float[4];
for (int i = 0; i < GetPos.Length; i++)
{
X_ary[i] = GetPos[i].x;
Y_ary[i] = GetPos[i].y;
Z_ary[i] = GetPos[i].z;
}
Array.Sort(X_ary);
Array.Sort(Y_ary);
Array.Sort(Z_ary);
if (X_ary[0] == X_ary[1] && X_ary[1] == X_ary[2])
{
Pos.x = X_ary[0];
Pos.y = UnityEngine.Random.Range(Y_ary[0], Y_ary[3]);
Pos.z = UnityEngine.Random.Range(Z_ary[0], Z_ary[3]);
}
else if (Y_ary[0] == Y_ary[1] && Y_ary[1] == Y_ary[2])
{
Pos.y = Y_ary[0];
Pos.x = UnityEngine.Random.Range(X_ary[0], X_ary[3]);
Pos.z = UnityEngine.Random.Range(Z_ary[0], Z_ary[3]);
}
else
{
Pos.z = Z_ary[0];
Pos.x = UnityEngine.Random.Range(X_ary[0], X_ary[3]);
Pos.y = UnityEngine.Random.Range(Y_ary[0], Y_ary[3]);
}
Debug.LogError("随机位置测试" + Pos);
return Pos;
}
}
以上。