Обязательно добавьте смещение к начальной позиции луча, чтобы избежать проблем, когда луч начинается изнутри искомого объекта (что может произойти, если, например, игрок упирается в вашу платформу, проникая в нее незначительным количеством).
Это добавляет смещение к Лучу, чтобы гарантировать обнаружение земли / платформ, даже если они слегка проникают:
private float distance = 0.5f;
private float offset = 0.5f;
void Update()
{
RaycastHit hit;
Ray footstepRay = new Ray(transform.position + (Vector3.up * offset), Vector3.down); // FIX: added an offset
if(Physics.Raycast(footstepRay, out hit, distance + offset, LayerMask.GetMask("Ground", "Platform"))) // FIX: added a LayerMask
{
if(hit.collider.tag == "Ground")
{
Debug.Log ("Player is standing on the ground");
}
else if(hit.collider.tag == "Platform")
{
Debug.Log ("Player is standing on the platform");
}
}
}
Смещение предотвращает пропущенные обнаружения при случайном запуске луча изнутри искомого объекта.
Использование явного LayerMask гарантирует, что вы случайно не обнаружите проигрыватель или другие нежелательные объекты.
Не обнаружение платформы в самом ее краю - это совершенно другая проблема. В результате ваш Луч, начиная с центра Игрока, просто проходит мимо платформы, когда центр вашего Игрока больше не находится над платформой. Это можно исправить, отправив несколько лучей в разные позиции, например, по кругу вокруг центра игрока.
Используется несколько лучей для улучшения обнаружения земли / платформы в краевых случаях (буквально):
private float distance = 0.5f;
private float yOffset = 0.5f;
private float playerRadius = 0.3f;
void Update()
{
string hitTag = DetectGround(Vector3.zero);
if (hitTag != null)
{
OnFound(hitTag);
return;
}
const int rays = 10;
for (int i = 0; i < rays; ++i)
{
float angle = (360.0f / rays) * i;
Vector3 posOffset = Quaternion.AngleAxis(angle, Vector3.up) * (Vector3.forward * playerRadius);
hitTag = DetectGround(posOffset);
if (hitTag != null)
{
OnFound(hitTag);
return;
}
}
}
void OnFound(string tag)
{
if(tag == "Ground")
{
Debug.Log ("Player is standing on the ground");
}
else if(tag == "Platform")
{
Debug.Log ("Player is standing on the platform");
}
}
string DetectGround(Vector3 posOffset)
{
RaycastHit hit;
Ray footstepRay = new Ray(transform.position + posOffset + (Vector3.up * yOffset), Vector3.down); // FIX: added an offset
if(Physics.Raycast(footstepRay, out hit, distance + yOffset, LayerMask.GetMask("Ground", "Platform"))) // FIX: added a LayerMask
{
return hit.collider.tag;
}
return null;
}
Выше кода предполагается, что у вас есть слои с именами "Ground" и "Platform" в дополнение к тегам. Вы можете изменить это по мере необходимости. Цель LayerMask - убедиться, что Raycast не рассматривает никакие объекты, кроме площадок и платформ. Вы можете разместить их в отдельных слоях или в каком-то отдельном слое "Мир" или в любом другом месте, если игрок не находится в том же слое, что и основания или платформы.
Редактировать: Возможны ситуации, когда иногда на земле (на краю) платформы обнаруживается Земля. Это может произойти, если для поля distance
установлено значение, превышающее минимальное расстояние от земли до платформы. Если это расстояние является постоянным, проблему можно решить, настроив поле distance
соответствующим образом. Однако если платформы движутся, такой подход, скорее всего, не сработает. В этом случае использование ближайшего к игроку объекта должно дать лучшие результаты.
Этот пример собирает все попадания всех лучей и сортирует их по расстоянию. Ближайшим попаданием считается желаемый результат:
using System.Linq;
private float distance = 0.5f;
private float yOffset = 0.5f;
private float playerRadius = 0.3f;
void Update()
{
List<RaycastHit> allHits = new List<RaycastHit>();
DetectGround(allHits, Vector3.zero);
const int rays = 10;
for (int i = 0; i < rays; ++i)
{
float angle = (360.0f / rays) * i;
Vector3 posOffset = Quaternion.AngleAxis(angle, Vector3.up) * (Vector3.forward * playerRadius);
DetectGround(allHits, posOffset);
}
if (allHits.Any())
{
RaycastHit closestHit = allHits.OrderBy(hit => hit.distance).First();
OnFound(closestHit.collider.tag);
}
}
void OnFound(string tag)
{
if(tag == "Ground")
{
Debug.Log ("Player is standing on the ground");
}
else if(tag == "Platform")
{
Debug.Log ("Player is standing on the platform");
}
}
void DetectGround(List<RaycastHit> hits, Vector3 posOffset)
{
Ray footstepRay = new Ray(transform.position + posOffset + (Vector3.up * yOffset), Vector3.down); // FIX: added an offset
Debug.DrawLine(footstepRay.origin, footstepRay.origin + (footstepRay.direction * (distance + yOffset)), Color.red);
hits.AddRange(Physics.RaycastAll(footstepRay, distance + yOffset, LayerMask.GetMask("Ground", "Platform")));
}
Примечание. В этом примере не требуется, чтобы LayerMask работал надежно. Может все же иметь смысл использовать слои для фильтрации рассматриваемых объектов по соображениям производительности, если в вашей игре много коллайдеров в непосредственной близости от raycast.