You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中如何实现拖拽操作生成地形而非普通游戏对象?

地形生成与普通游戏对象生成的核心差异
  • 底层逻辑不同:普通游戏对象生成是实例化独立的GameObject资源,自带完整的组件、变换属性。Unity内置Terrain是基于TerrainData资源驱动的组件,修改地形本质是修改TerrainData存储的高度图、纹理混合、细节植被等数组数据,而非频繁实例化新的Terrain对象。
  • 性能逻辑不同:单块Terrain会自动做网格合批、LOD优化,若按普通Prefab的思路每拖拽一次生成一块新Terrain,会快速升高DrawCall导致卡顿。大部分拖拽生成地形的需求都基于修改现有Terrain的高度数据实现,仅大世界流式加载场景才会分块实例化Terrain。
  • 坐标映射逻辑不同:普通Prefab实例化可直接传入世界坐标赋值位置。修改地形需要先将鼠标的屏幕坐标转世界坐标,再换算为Terrain局部空间的相对坐标,最终映射到高度图的像素索引才能做修改。
拖拽修改地形的实现方案

不需要将代码挂载到Terrain对象上,你可以将脚本挂载到任意全局常驻对象上,也可以直接挂载到Terrain对象上,参考代码如下:

using UnityEngine;

public class DragModifyTerrain : MonoBehaviour
{
    public Terrain targetTerrain; // Inspector中拖入要修改的地形对象
    public int brushSize = 5; // 笔刷大小,单位是高度图像素
    public float heightDelta = 0.01f; // 每次拖拽的抬升幅度
    private TerrainData _terrainData;
    private int _heightmapResolution;

    void Start()
    {
        if (targetTerrain == null)
        {
            targetTerrain = GetComponent<Terrain>();
        }
        _terrainData = targetTerrain.terrainData;
        _heightmapResolution = _terrainData.heightmapResolution;
    }

    void Update()
    {
        // 按住鼠标左键拖拽时触发地形修改
        if (Input.GetMouseButton(0))
        {
            // 射线检测获取鼠标指向的地形世界坐标
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                // 将世界坐标转换为地形局部的相对坐标
                Vector3 terrainLocalPos = hit.point - targetTerrain.transform.position;
                // 换算为高度图对应的UV坐标
                float normalizedX = terrainLocalPos.x / _terrainData.size.x;
                float normalizedZ = terrainLocalPos.z / _terrainData.size.z;
                // 换算高度图的像素索引
                int heightmapX = Mathf.RoundToInt(normalizedX * _heightmapResolution);
                int heightmapZ = Mathf.RoundToInt(normalizedZ * _heightmapResolution);

                // 避免索引越界
                int startX = Mathf.Clamp(heightmapX - brushSize / 2, 0, _heightmapResolution - 1);
                int endX = Mathf.Clamp(heightmapX + brushSize / 2, 0, _heightmapResolution - 1);
                int startZ = Mathf.Clamp(heightmapZ - brushSize / 2, 0, _heightmapResolution - 1);
                int endZ = Mathf.Clamp(heightmapZ + brushSize / 2, 0, _heightmapResolution - 1);

                // 获取对应区域的高度数组,修改后写回
                float[,] heights = _terrainData.GetHeights(startX, startZ, endX - startX, endZ - startZ);
                for (int x = 0; x < endX - startX; x++)
                {
                    for (int z = 0; z < endZ - startZ; z++)
                    {
                        heights[z, x] += heightDelta;
                    }
                }
                _terrainData.SetHeights(startX, startZ, heights);
            }
        }
    }
}

配置说明

  1. 若脚本挂载在Terrain对象上,targetTerrain会自动读取,无需手动赋值;若挂载在其他对象上,需要在Inspector中将目标地形拖入对应字段。
  2. 调整brushSize控制笔刷范围,调整heightDelta控制每次拖拽地形抬升的速度,若填负数则会实现降低地形的效果。
拓展说明

如果你的需求是拖拽生成独立的地形地块而非修改现有地形高度,可以直接将带Terrain组件的地形块做成Prefab,复用你现有的实例化逻辑即可,额外需要注意相邻地形块的高度图接缝对齐,避免出现高度断层。

内容的提问来源于stack exchange,提问作者user11749375

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 18:51:00