Единство, получить "актуальную" текущую местность?
Единство имеет функцию 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();
}