Единство, получить "актуальную" текущую местность?

Единство имеет функцию Terrain.sampleHeight(point) что здорово, он сразу дает вам высоту ландшафта под ногами, а не бросает.

Тем не менее, любой нетривиальный проект имеет более одного ландшафта. (На самом деле любая физически большая сцена неизбежно показывает сшивание ландшафта, так или иначе.)

Единство имеет функцию Terrain.activeTerrain который - не выдумывая - дает вам "первый загруженный", следовательно, бесполезен.

Есть ли на самом деле быстрый способ получить Terrain "под себя" - чтобы потом использовать функцию fast .sampleHeight?

{Пожалуйста, обратите внимание, конечно, вы могли бы... бросить, чтобы найти Terrain под вами! Но тогда у вас будет высота, так что вам не о чем беспокоиться. .sampleHeight!}

Короче говоря, есть ли подходящая функция для использования с sampleHeight которая позволяет этой функции знать, какую Terrain использовать для данного xyz?

(Или действительно, это sampleHeight просто довольно бесполезная демонстрационная функция, используемая только в демонстрациях с одним Terrain?)

6 ответов

Terrain.GetPosition() = Terrain.transform.position = позиция в мировом
методе работы:

Terrain[] _terrains = Terrain.activeTerrains;

int GetClosestCurrentTerrain(Vector3 playerPos)
{
    //Get the closest one to the player
    var center = new Vector3(_terrains[0].transform.position.x + _terrains[0].terrainData.size.x / 2, playerPos.y, _terrains[0].transform.position.z + _terrains[0].terrainData.size.z / 2);
    float lowDist = (center - playerPos).sqrMagnitude;
    var terrainIndex = 0;

    for (int i = 0; i < _terrains.Length; i++)
    {
        center = new Vector3(_terrains[i].transform.position.x + _terrains[i].terrainData.size.x / 2, playerPos.y, _terrains[i].transform.position.z + _terrains[i].terrainData.size.z / 2);

        //Find the distance and check if it is lower than the last one then store it
        var dist = (center - playerPos).sqrMagnitude;
        if (dist < lowDist)
        {
            lowDist = dist;
            terrainIndex = i;
        }
    }
    return terrainIndex;
}

Есть ли на самом деле быстрый способ получить Terrain "под себя" - чтобы потом использовать быструю функцию.sampleHeight?

Да, это может быть сделано.

(Или действительно, sampleHeight - просто бесполезная демонстрационная функция, которую можно использовать только в демоверсиях с одним Terrain?)

нет


Есть Terrain.activeTerrain который возвращает основную местность в сцене. Существует также Terrain.activeTerrains (обратите внимание на "s" в конце), который возвращает активные ландшафты в сцене.

Получить местности с Terrain.activeTerrains который возвращается Terrain массив затем использовать Terrain.GetPosition функция для получения своей позиции. Получить текущую местность, найдя ближайшую местность с позиции игрока. Вы можете сделать это путем сортировки позиции местности, используя Vector3.Distance или же Vector3.sqrMagnitude (Быстрее).

Terrain GetClosestCurrentTerrain(Vector3 playerPos)
{
    //Get all terrain
    Terrain[] terrains = Terrain.activeTerrains;

    //Make sure that terrains length is ok
    if (terrains.Length == 0)
        return null;

    //If just one, return that one terrain
    if (terrains.Length == 1)
        return terrains[0];

    //Get the closest one to the player
    float lowDist = (terrains[0].GetPosition() - playerPos).sqrMagnitude;
    var terrainIndex = 0;

    for (int i = 1; i < terrains.Length; i++)
    {
        Terrain terrain = terrains[i];
        Vector3 terrainPos = terrain.GetPosition();

        //Find the distance and check if it is lower than the last one then store it
        var dist = (terrainPos - playerPos).sqrMagnitude;
        if (dist < lowDist)
        {
            lowDist = dist;
            terrainIndex = i;
        }
    }
    return terrains[terrainIndex];
}

ИСПОЛЬЗОВАНИЕ:

Предполагая, что позиция игрока transform.position:

//Get the current terrain
Terrain terrain = GetClosestCurrentTerrain(transform.position);
Vector3 point = new Vector3(0, 0, 0);
//Can now use SampleHeight
float yHeight = terrain.SampleHeight(point);

Хотя это возможно сделать с Terrain.SampleHeight, это может быть упрощено с помощью простого радиопередачи от позиции игрока до ландшафта.

Vector3 SampleHeightWithRaycast(Vector3 playerPos)
{
    float groundDistOffset = 2f;
    RaycastHit hit;
    //Raycast down to terrain
    if (Physics.Raycast(playerPos, -Vector3.up, out hit))
    {
        //Get y position
        playerPos.y = (hit.point + Vector3.up * groundDistOffset).y;
    }
    return playerPos;
}

Оказывается, ответ просто НЕТ, Unity не предоставляет такой функции.

Вы можете использовать эту функцию, чтобы получить ближайшую местность к вашей текущей позиции:

int GetClosestTerrain(Vector3 CheckPos)
{
    int terrainIndex = 0;
    float lowDist = float.MaxValue;

    for (int i = 0; i < _terrains.Length; i++)
    {
      var center = new Vector3(_terrains[i].transform.position.x + _terrains[i].terrainData.size.x / 2, CheckPos.y, _terrains[i].transform.position.z + _terrains[i].terrainData.size.z / 2);

      float dist = Vector3.Distance(center, CheckPos);

      if (dist < lowDist)
      {
        lowDist = dist;
        terrainIndex = i;
      }
    }
    return terrainIndex;
}

а затем вы можете использовать такую ​​функцию:

private Terrain[] _terrains;

void Start()
  {
    _terrains = Terrain.activeTerrains;

    Vector3 start_pos = Vector3.zero;

    start_pos.y = _terrains[GetClosestTerrain(start_pos)].SampleHeight(start_pos);

  }

Решение Raycast: (об этом не спрашивали, но для тех, кто ищет решение с использованием Raycast)

Raycast вниз от Player, игнорируйте все, что не имеет слоя «Terrain» (слой можно легко установить в инспекторе).

Код:

          void Update() {
    // Put this on Player! Raycast's down (raylength=10f), if we hit something, check if the Layers name is "Terrain", if yes, return its instanceID
    RaycastHit hit;
    if (Physics.Raycast (transform.localPosition, transform.TransformDirection (Vector3.down), out hit, 10f, 1 << LayerMask.NameToLayer("Terrain"))) {
        Debug.Log(hit.transform.gameObject.GetInstanceID());
    }
}

На данный момент у вас уже есть ссылка на Terrain через «hit.transform.gameObject».

В моем случае я хотел сослаться на этот ландшафт по его instanceID:

          // any other script
    public static UnityEngine.Object FindObjectFromInstanceID(int goID) {
    return (UnityEngine.Object)typeof(UnityEngine.Object)
            .GetMethod("FindObjectFromInstanceID", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static)
            .Invoke(null, new object[] { goID });
}

Но, как написано выше, если вам нужен сам Terrain (как объект Terrain), а не instanceID, то «hit.transform.gameObject» уже даст вам ссылку.

Входные данные и фрагменты кода взяты из этих ссылок:

      public static Terrain GetClosestCurrentTerrain(Vector3 position)
{
    return Terrain.activeTerrains.OrderBy(x => (x.GetPosition() - position).sqrMagnitude).First();
}
Другие вопросы по тегам