#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 предоставляет вам информацию о месте столкновения — месте попадания, игровом объекте, коллайдере — которую вы можете использовать для другой игровой логики, например, для стрельбы снарядом с космического корабля в точку попадания астероида.
Я отредактировал ваш класс и включил скриншот из моей простой сцены ниже, на котором показаны два разных «луча»:
- Желтый луч проходит от камеры до точки попадания астероида
- Пурпурный луч переходит из положения космического корабля в точку попадания астероида.
Я бы также рекомендовал фильтровать 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;
}
}