Как проверить, что объект не скрыт другим объектом? - PullRequest
0 голосов
/ 22 сентября 2019

Я делаю игру с изометрической проекцией.Когда игрок входит в дом, его крыша скрывается, и игрок может взаимодействовать с неигровыми персонажами, предметами и т. Д. Но теперь он может взаимодействовать с ним, даже когда крыша видна.Как обнаружить, что предмет спрятан за крышей дома, или за стеной, или за другим предметом?

void Update() {
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);

    if (Input.GetMouseButtonDown(0)) {
        foreach (RaycastHit hit in hits) {
            if (hit.collider.tag != "NPC") {
                continue;
            }
          //Interact ...
        }
    }
}

Ответы [ 2 ]

2 голосов
/ 22 сентября 2019

Вы можете просто проверить расстояние между ударной стеной / крышей и NPC от источника луча (камера).Вот так:

private Camera cameraRef;

private void Awake() {
    // P.S: Cache the 'Camera.main', calls to it can be expensive.
    cameraRef = Camera.main;
}

void Update() {
    if (Input.GetMouseButtonDown(0)) {
        Ray ray = cameraRef.ScreenPointToRay(Input.mousePosition);
        RaycastHit[] hits = Physics.RaycastAll(ray, Mathf.Infinity);

        foreach (RaycastHit hit in hits) {
            if (hit.collider.tag != "NPC") {
                continue;
            } else if (RaycastHitRoofOrWallFirst(hits, hit.collider.gameObject)) {
                // This NPC is hidden behind a roof/wall.
                continue;
            }
            // Interaction...
        }
    }
}

/// <summary>
/// Check if a target object is being hidden behind a roof/wall.
/// </summary>
/// <param name="hits">The hits that the raycast gotten.</param>
/// <param name="targetObject">The gameobject to check against.</param>
/// <returns>Return true if the target object is hidden, false if not.</returns>
private bool RaycastHitRoofOrWallFirst(RaycastHit[] hits, GameObject targetObject) {
    foreach (RaycastHit hit in hits) {
        if (hit.collider.CompareTag("roof") || hit.collider.CompareTag("wall")) {

            float distanceFromCameraToObstacle = Vector3.Distance(cameraRef.transform.position, hit.collider.transform.position);
            float distanceFromCameraToNPC = Vector3.Distance(cameraRef.transform.position, targetObject.transform.position);

            // Check if the NPC is closer to the camera (raycast origin)
            // compared to the roof or wall.
            if (distanceFromCameraToObstacle < distanceFromCameraToNPC) {
                // The roof/wall is closer to the camera (raycast origin)
                // compared to the NPC, hence the NPC is blocked by the roof/wall
                return true;
            }
        }
    }

    return false;
}

Вот небольшая визуальная диаграмма того, что он должен проверять:

Visual Diagram of the code

Или просто используйте простойraycast ...

Если возможно в зависимости от контекста, вместо использования Physics.RaycastAll, вы можете использовать Physics.Raycast.

Itвозвращает первый объект, попавший под действие луча.

1 голос
/ 22 сентября 2019

Добавление к этого ответа альтернатива может также использовать OnBecameVisible

OnBecameVisible вызывается, когда объект становится видимым для любогоCamera.

Это сообщение отправляется всем сценариям, прикрепленным к Renderer.

и OnBecameInvisible

OnBecameInvisible вызывается, когда Renderer больше не виден любому Camera.

Это сообщение отправляется всем сценариям, прикрепленным к Renderer.

OnBecameVisible и OnBecameInvisible, полезны во избежание вычислений, которые необходимы только тогда, когда объект виден.

Для активации и деактивации соответствующих коллайдеров NPC, так что Raycast в любом случае будет работать только на видимых объектах.

Как и на NPC, есть скрипт

public class InteractableController : MonoBehaviour
{
    // you can also reference them via the Inspector
    public Collider[] colliders;

    private void Awake()
    {
        // pass in true to also get inactive components
        if(colliders.Length = 0) colliders = GetComponentsInChildren<Collider>(true);
    }

    private void OnBecameInvisible()
    {
        foreach(var collider in colliders)
        {
            collider.enabled = false;
        }
    }

    private void OnBecameVisible()
    {
        foreach(var collider in colliders)
        {
            collider.enabled = true;
        }
    }
}

Однако

Обратите внимание, что объект считается видимым, когда его необходимо визуализировать в сцене.Он может быть невидим для любой камеры, но все равно его нужно рендерить, например, для теней. Кроме того, при работе в редакторе камеры вида сцены также будут вызывать эту функцию .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...