Заставить объект столкнуться с некоторыми другими объектами при перемещении в точку RaycastHit в Unity3D? - PullRequest
0 голосов
/ 07 мая 2020

Я делаю простую строительную систему, используя Raycasting от указателя мыши. Объект, который должен быть размещен, перемещается (и клонируется при щелчке) в RaycastHit.point луча, но я хочу, чтобы он полностью сталкивался с объектами своего собственного типа, смещая свое положение относительно точки попадания. Объект может пересекать местность, как показано на гифке, но не должен находиться внутри уже установленных стеновых блоков. Я попытался использовать hit.distance, но не смог определить обнаружение, поскольку объект уже находится в точке столкновения. Должен ли я попытаться определить, как позиция должна смещаться относительно точки попадания, или я должен каким-то образом заставить работать MeshCollider, когда он постоянно перемещается в RaycastHit.point?

alt text

1 Ответ

2 голосов
/ 07 мая 2020

Это один из способов сделать то, о чем вы просите.

private void Update()
{
    Vector3 hitPosition = Vector3.zero;
    Ray ray = _camera.ScreenPointToRay(Input.mousePosition);

    if (Physics.Raycast(ray, out RaycastHit hit))
    {
        hitPosition = hit.point; // 0
        if (hit.transform.gameObject.CompareTag("cube")) // 1
            hitPosition = ComputeHit(hit, hitPosition);
    }

    _current.transform.position = hitPosition;
}

private Vector3 ComputeHit(RaycastHit hit, Vector3 currentPosition)
{
    var bounds = hit.transform.GetComponent<MeshRenderer>().bounds; // 2

    Faces face = GetFace(hit); // 3
    switch (face)
    {
        case Faces.Up:
            currentPosition += new Vector3(0, bounds.extents.x, 0);
            break;
        case Faces.Down:
            currentPosition += new Vector3(0, -bounds.extents.x, 0);
            break;
        case Faces.East:
            currentPosition += new Vector3(bounds.extents.x, 0, 0);
            break;
        case Faces.West:
            currentPosition += new Vector3(-bounds.extents.x, 0, 0);
            break;
        case Faces.North:
            currentPosition += new Vector3(0, 0, bounds.extents.x);
            break;
        case Faces.South:
            currentPosition += new Vector3(0, 0, -bounds.extents.x);
            break;
    }

    return currentPosition;
}

public Faces GetFace(RaycastHit hit)
{
    Vector3 res = hit.normal - Vector3.up;

    if (res == new Vector3(0, -1, -1))
        return Faces.South;

    if (res == new Vector3(0, -1, 1))
        return Faces.North;

    if (res == new Vector3(0, 0, 0))
        return Faces.Up;

    if (res == new Vector3(1, 1, 1))
        return Faces.Down;

    if (res == new Vector3(-1, -1, 0))
        return Faces.West;

    if (res == new Vector3(1, -1, 0))
        return Faces.East;

    return Faces.Nothing;
}

public enum Faces
{
    Nothing,

    Up,
    Down,
    East,
    West,
    North,
    South
}

Я объясню дальше:

В вашем методе обновления, как только вы обнаружите, где находится hit.point местоположение // 0, вы можете проверить, целитесь ли вы кубиком или нет // 1. Я не знаю, как вы управляете ими после их создания, но я добавляю тег с именем cube. Это может быть любое имя или слой. Это также может быть компонент и проверить, существует ли этот компонент с помощью метода hit.transform.GetComponent<...>().

Затем вы получаете границы целевого куба // 2 и используете нормаль, чтобы определить, в каком направлении вы нацеливаетесь // 3.

Отсюда, в зависимости от цели (одна из 6), вы добавляете смещение к hit.point по одной из 3 осей.

bounds.extents дает вам половину лиц, используя bounds.extents.x, bounds.extents.y и bounds.extents.z. Вы хотите, чтобы смещение составляло только половину длины грани, потому что, когда вы перемещаете куб с помощью мыши, куб находится в его центре.

Вот пример:

Cubes example

...