Unity3D - 使用光线投射检测游戏对象。自上而下的视图
Unity3D - Using Raycasting to detect Game Objects. Top-down view
我正在制作一款自上而下的 space 资源收集游戏,使用 3D 模型。
我正在尝试向我的鼠标位置投射光线,以检测与游戏中对象的碰撞,
让我的小 spaceship 知道去哪个方向寻找资源。
资源以小行星的形式存在,玩家必须打破才能收集。
在代码中,我使用“中心”向量找到 screen/player 位置的中间位置,因为相机会跟随玩家。所有运动都沿 X、Y 轴发生。 center.Z 设置为 15 只是为了抵消世界中主摄像机的 -15Z 位置 space。
到目前为止,代码“工作”到可以检测到命中的程度,但是光线不会朝鼠标的方向传播。我必须让它远离它才能击中它,而且它很难复制,以至于它几乎看起来是随机的。光线可能无法理解鼠标位置吗?
以防我措辞不当,这种搜索能力并不是要破坏小行星,只是简单地定位它。破坏能力是一个单独的脚本。
代码:
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);
}
}
}
提前致谢
当使用 Input.mousePosition
时,该位置是屏幕上的像素坐标,因此如果您的鼠标位于屏幕的左下角,则该位置为 0,0。这会导致 direction
不准确。当你需要的是使用游戏space坐标时,以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;
}
您使用了 ViewportToWorldPoint 方法,该方法需要在 0 到 1 范围内的标准化视口坐标,但您提供了在我看来是相机的世界坐标作为其参数。
您只需要将光线从相机投射到鼠标指针世界位置(请参阅方法 FindAsteroid 中的第一行代码)以检查与小行星的碰撞。返回的 RaycastHit 为您提供有关碰撞的信息 - 命中位置、游戏对象、对撞机 - 您可以将其用于其他游戏逻辑,例如从宇宙飞船向小行星命中点发射弹丸。
我编辑了你的 class 并在下面附上了我的简单场景的屏幕截图,其中显示了两种不同的“光线”:
- 黄色光线从相机到小行星撞击点
- 洋红色射线从飞船位置到小行星命中点。
我还建议过滤光线投射以仅影响特定的碰撞器 -
请参阅 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;
}
}
Sample spaceship scene
我正在制作一款自上而下的 space 资源收集游戏,使用 3D 模型。
我正在尝试向我的鼠标位置投射光线,以检测与游戏中对象的碰撞, 让我的小 spaceship 知道去哪个方向寻找资源。 资源以小行星的形式存在,玩家必须打破才能收集。
在代码中,我使用“中心”向量找到 screen/player 位置的中间位置,因为相机会跟随玩家。所有运动都沿 X、Y 轴发生。 center.Z 设置为 15 只是为了抵消世界中主摄像机的 -15Z 位置 space。 到目前为止,代码“工作”到可以检测到命中的程度,但是光线不会朝鼠标的方向传播。我必须让它远离它才能击中它,而且它很难复制,以至于它几乎看起来是随机的。光线可能无法理解鼠标位置吗?
以防我措辞不当,这种搜索能力并不是要破坏小行星,只是简单地定位它。破坏能力是一个单独的脚本。
代码:
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);
}
}
}
提前致谢
当使用 Input.mousePosition
时,该位置是屏幕上的像素坐标,因此如果您的鼠标位于屏幕的左下角,则该位置为 0,0。这会导致 direction
不准确。当你需要的是使用游戏space坐标时,以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;
}
您使用了 ViewportToWorldPoint 方法,该方法需要在 0 到 1 范围内的标准化视口坐标,但您提供了在我看来是相机的世界坐标作为其参数。
您只需要将光线从相机投射到鼠标指针世界位置(请参阅方法 FindAsteroid 中的第一行代码)以检查与小行星的碰撞。返回的 RaycastHit 为您提供有关碰撞的信息 - 命中位置、游戏对象、对撞机 - 您可以将其用于其他游戏逻辑,例如从宇宙飞船向小行星命中点发射弹丸。
我编辑了你的 class 并在下面附上了我的简单场景的屏幕截图,其中显示了两种不同的“光线”:
- 黄色光线从相机到小行星撞击点
- 洋红色射线从飞船位置到小行星命中点。
我还建议过滤光线投射以仅影响特定的碰撞器 - 请参阅 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;
}
}
Sample spaceship scene