Unity3D — Использование Raycasting для обнаружения игровых объектов. Вид сверху вниз

#c# #unity3d #vector #raycasting

#c# #unity3d #вектор #raycasting

Вопрос:

Я создаю игру для сбора ресурсов в пространстве сверху вниз с 3D-моделями.

Я пытаюсь направить луч в направлении моего положения мыши, чтобы обнаружить столкновения с объектами в моей игре, чтобы мой маленький космический корабль знал, в каком направлении двигаться, чтобы найти ресурсы. Ресурсы представлены в виде астероидов, которые игрок должен разбить, чтобы собрать.

В коде я использую вектор «центр», чтобы найти середину экрана / положение игрока, поскольку камера следует за игроком по кругу. Все перемещения происходят вдоль осей X, Y. центр.Z устанавливается равным 15 просто для того, чтобы нейтрализовать положение основной камеры -15Z в мировом пространстве. Пока что код «работает» в той степени, в какой он может обнаружить попадание, но луч не движется в направлении мыши. Я должен удерживать его подальше, чтобы он попал, и его трудно воспроизвести, до такой степени, что это кажется почти случайным. Может ли луч не иметь смысла в положении мыши?

На случай, если я неправильно сформулировал себя, эта функция поиска предназначена не для того, чтобы разбить астероид, просто найдите его. Возможность взлома — это отдельный скрипт.

Код:

 public class AbilitySearch : MonoBehaviour
{
Vector3 center;
Vector3 mousePos;
Vector3 direction;
private float range = 100f;

void Update()
{
    center = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 15.0f));
    mousePos = Input.mousePosition;
    direction = mousePos - transform.position;

    if (Input.GetMouseButton(1))
    {
        Search();
    }
}

void Search()
{
    RaycastHit hit;
    if (Physics.Raycast(center, direction, out hit, range))
    {
        Debug.Log("You hit "   hit.transform.name);
    }
}
}
  

Заранее спасибо

Ответ №1:

При использовании Input.mousePosition позиция — это координата пикселя на вашем экране, поэтому, если бы ваша мышь находилась в нижнем левом углу экрана, это было бы 0,0. Это приводит к direction неточности. Когда вам нужно использовать координаты игрового пространства, что делается с помощью Camera.ScreenToWorldPoint(Input.mousePosition) , в качестве примера.

Это решение позволяет щелкнуть по игровому объекту (при условии, что к нему подключен коллайдер), переместить текущий игровой объект в направлении выбранного объекта, а также собрать массив возможных игровых объектов (RaycastHit[s]) в направлении выбранного объекта.

 Vector3 destination;
RaycastHit[] possibleTargets;
bool isTravelling;
float searchDistance = 5f;

private void Update()
{
    //If right mouse button has been pressed down
    if (Input.GetMouseButtonDown(1))
    {
        RaycastHit hit;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit)) //If ray collides with something
        {
            Search(hit);
        }
    }
    //If going to destination
    if (isTravelling)
    {
        float step = 2f * Time.deltaTime;
        transform.position = Vector3.MoveTowards(transform.position, destination, step);
        //If approx arrived
        if (Vector3.Distance(transform.position, destination) < 0.1f)
        {
            isTravelling = false; //Stop moving to destination
            Debug.Log("You've arrived");
        }
    }
}

private void Search(RaycastHit _hit)
{
    //Normalise directional vectors, so (if needed) they can be multipled as expected
    Vector3 direction = (_hit.point - transform.position).Normalized();
    RaycastHit secondHit;
    //Grab objects in direction of clicked object (including the one clicked)
    possibleTargets = Physics.RaycastAll(transform.position, direction, searchDistance);
    Debug.Log($"There are {possibleTargets.Length} possible targets ahead");
    Debug.Log($"You hit {_hit.transform.name}");
    //Set destination, and set to move
    destination = _hit.point;
    isTravelling = true;
}
  

Комментарии:

1. Спасибо, это сработало при использовании на астероидах. Но что, если я хочу, чтобы луч продолжался вдоль направления моего указателя мыши, чтобы найти астероиды, не видимые на экране? Кстати, я хотел, чтобы это был не сценарий перемещения, а просто возможность находить ресурсы. Все еще работал 🙂 Извините за неудачную формулировку в основном сообщении.

2. @JeyesElite Нет проблем, я обновил свой первоначальный ответ, включив проверку направления на наличие астероидов, возможно, вы захотите скорректировать searchDistance значение. Движение было просто небольшим дополнением, удачи с ним!

Ответ №2:

Вы использовали метод ViewportToWorldPoint, который ожидает нормализованные координаты видового экрана в диапазоне от 0 до 1, но вы указали в качестве параметра то, что мне кажется мировыми координатами вашей камеры.

Вам нужно только направить луч с камеры на мировое положение указателя мыши (см. Первую строку кода в методе FindAsteroid), чтобы проверить столкновение с астероидом. Возвращенный RaycastHit предоставляет вам информацию о месте столкновения — месте попадания, игровом объекте, коллайдере — которую вы можете использовать для другой игровой логики, например, для стрельбы снарядом с космического корабля в точку попадания астероида.

Я отредактировал ваш класс и включил скриншот из моей простой сцены ниже, на котором показаны два разных «луча»:

  1. Желтый луч проходит от камеры до точки попадания астероида
  2. Пурпурный луч переходит из положения космического корабля в точку попадания астероида.

Я бы также рекомендовал фильтровать raycast, чтобы влиять только на определенные коллайдеры — см. LayerMask (раздел, отбирающий лучи выборочно)

 public class AbilitySearch : MonoBehaviour
{
    private float  range = 100f;
    private Camera mainCam;

    private void Awake()
    {
        // TIP: Save reference to main camera - avoid internal FindGameObjectWithTag call
        mainCam = Camera.main;
    }

    private void Update()
    {
        if (Input.GetMouseButton(1))
        {
            if (FindAsteroid(out var asteroidHit))
            {
                // Draw a line from spaceship to asteroid hit position
                Debug.DrawLine(transform.position, asteroidHit.point, Color.magenta, Time.deltaTime);

                Debug.Log($"You hit {asteroidHit.transform.name}");
            }
        }
    }

    private bool FindAsteroid(out RaycastHit hit)
    {
        // Create a ray going from camera position to mouse position in world space
        var ray = mainCam.ScreenPointToRay(Input.mousePosition);

        if (Physics.Raycast(ray, out hit, range))
        {
            // Draw a line from camera to world mouse position
            Debug.DrawLine(ray.origin, hit.point, Color.yellow, Time.deltaTime);

            // An asteroid was found
            return true;
        }

        // An asteroid was NOT found
        return false;
    }
}
  

Пример сцены космического корабля