unity3d Unity,是否获得“实际”当前地形?

rkue9o1l  于 2022-11-25  发布在  其他
关注(0)|答案(6)|浏览(217)

Unity有一个功能Terrain.sampleHeight(point),这是伟大的,它立即给你脚下地形的高度,而不是必须投。
然而,任何重要的项目都有不止一个地形。(实际上,任何物理上大的场景都不可避免地以某种方式具有地形 * 拼接 *。)
Unity有一个函数Terrain.activeTerrain-我不是瞎编的-给你:“第一个加载”
显然那是完全没用的。
事实上,有没有一种快速的方法让地形“在你下面”?然后你可以使用快速函数.sampleHeight
“请注意,你当然可以......施法找一个地形!但你有你的高度,所以不用担心.sampleHeight!”
简而言之,是否有一个匹配的函数与sampleHeight一起使用,让该函数知道 * 哪个 * Terrain用于给定的xyz?
(Or确实,sampleHeight只是一个相当无用的演示函数,只在一个Terrain的演示中可用吗?)

qoefvg9y

qoefvg9y1#

事实上,是否有一种快速的方法可以让Terrain“位于您的下方”-以便随后使用快速函数.sampleHeight?
是的,这是可以做到的。
(Or确实,sampleHeight只是一个相当无用的演示函数,只在一个Terrain的演示中可用吗?)
没有
Terrain.activeTerrain返回场景中的地形,Terrain.activeTerrains(注意结尾的“s”)返回场景中的**活动地形。
使用Terrain.activeTerrains获取地形,它返回Terrain数组,然后使用Terrain.GetPosition函数获取其位置。通过查找离玩家位置最近的地形来获取当前地形。您可以使用Vector3.DistanceVector3.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;
}
kgsdhlau

kgsdhlau2#

Terrain.GetPosition()=地形.转换.位置=世界中的位置
工作方法:

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;
}
4si2a6ki

4si2a6ki3#

事实证明,答案是否定的,Unity不提供这样的功能。

9gm1akwq

9gm1akwq4#

您可以使用此功能获取与当前位置最近的地形:

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);

  }
a6b3iqyw

a6b3iqyw5#

public static Terrain GetClosestTerrain(Vector3 position)
{
    return Terrain.activeTerrains.OrderBy(x =>
    {
        var terrainPosition = x.transform.position;
        var terrainSize = x.terrainData.size * 0.5f;
        var terrainCenter = new Vector3(terrainPosition.x + terrainSize.x, position.y, terrainPosition.z + terrainSize.z);
        return Vector3.Distance(terrainCenter, position);
    }).First();
}
z2acfund

z2acfund6#

简单解决方案:(适用于2022年,已在Unity 2021.3.2f1中测试)

从播放器向下光线投射,忽略所有没有“地形”层的东西(可以在检查器中轻松设置层)。
编码:

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());
    }
}

此时,您已经通过“hit.transform.gameObject”引用了Terrain。
对于我的示例,我希望通过其instanceID引用该terrain:

// 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”已经给你提供了引用。
来自以下链接的输入和代码片段:

相关问题