Unity C#基于网格的建筑系统无法正常工作 技术求助
修复类似Rust风格的网格建筑吸附系统
嘿,我看你在做生存游戏的网格建筑系统,目前的代码不仅有逻辑缺失,还有一些小问题,而且Raycast部分还截断了——先帮你梳理下现有代码的问题,再给你一套能正常运行的完整实现方案!
现有代码的明显问题
- 重复计算浪费性能:你在
Start()和Update()里都计算了gridSize = 1f / gridDistance,Update里每帧都算完全没必要,而且这个计算逻辑其实可以简化。 - 核心网格转换逻辑缺失:代码里定义了
gridPosition但没做任何转换计算,这是实现网格吸附的核心步骤。 - Raycast代码不完整:
Camera.ma...应该是要获取鼠标射线来检测场景中的放置平面,这部分是实现“跟着鼠标预览建筑位置”的关键,你没写完。 - 没有预览/放置逻辑:Rust风格的建筑系统需要实时显示预览模型,以及触发放置的输入处理,这些你的代码里都没有。
修复后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class SnapToGrid : MonoBehaviour { [Header("建筑设置")] public GameObject buildingPrefab; public float gridDistance = 2f; // 每个网格单元的大小(比如2米) public LayerMask placementLayer; // 允许放置建筑的层(比如地面层) private GameObject previewObject; private float gridHalfSize; // 辅助计算网格中心对齐 void Start() { // 提前计算网格半尺寸,用来让建筑中心对齐网格 gridHalfSize = gridDistance / 2f; // 初始化预览对象 CreatePreviewObject(); } void Update() { // 获取鼠标射线,检测场景中的放置平面 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, placementLayer)) { // 将检测到的世界坐标转换为网格对齐坐标 Vector3 gridAlignedPos = GetGridAlignedPosition(hit.point); // 更新预览对象的位置 UpdatePreviewPosition(gridAlignedPos); // 鼠标左键点击时放置建筑 if (Input.GetMouseButtonDown(0)) { PlaceBuilding(gridAlignedPos); } } else { // 没检测到放置平面时隐藏预览 if (previewObject != null) previewObject.SetActive(false); } } // 将世界坐标转换为网格对齐坐标 private Vector3 GetGridAlignedPosition(Vector3 worldPos) { // 按网格大小取整,然后加上半尺寸让建筑中心对齐网格 float x = Mathf.Round(worldPos.x / gridDistance) * gridDistance + gridHalfSize; float y = Mathf.Round(worldPos.y / gridDistance) * gridDistance + gridHalfSize; float z = Mathf.Round(worldPos.z / gridDistance) * gridDistance + gridHalfSize; return new Vector3(x, y, z); } // 创建预览对象(复制预制体,禁用碰撞和渲染以外的组件) private void CreatePreviewObject() { if (buildingPrefab == null) return; previewObject = Instantiate(buildingPrefab); // 禁用预览对象的碰撞和脚本,避免影响游戏逻辑 Collider[] colliders = previewObject.GetComponents<Collider>(); foreach (Collider col in colliders) col.enabled = false; // 给预览对象添加半透明材质,区分正式建筑 Renderer[] renderers = previewObject.GetComponentsInChildren<Renderer>(); foreach (Renderer rend in renderers) { Material previewMat = new Material(rend.material); previewMat.color = new Color(rend.material.color.r, rend.material.color.g, rend.material.color.b, 0.5f); rend.material = previewMat; } } // 更新预览对象的位置 private void UpdatePreviewPosition(Vector3 targetPos) { if (previewObject == null) return; previewObject.SetActive(true); previewObject.transform.position = targetPos; } // 放置正式建筑 private void PlaceBuilding(Vector3 targetPos) { if (buildingPrefab == null) return; Instantiate(buildingPrefab, targetPos, Quaternion.identity); } // 在编辑器里修改gridDistance时自动更新半尺寸 private void OnValidate() { gridHalfSize = gridDistance / 2f; } }
关键改进说明
- 优化网格计算:用
Mathf.Round()实现精准的网格对齐,同时加入gridHalfSize让建筑中心和网格中心对齐,更符合Rust的视觉效果。 - 预览系统:生成半透明的预览模型,实时跟随鼠标显示放置位置,玩家体验更直观。
- 分层检测:通过
LayerMask限制放置平面,避免建筑放到空中或者不该放的地方。 - 输入处理:加入鼠标左键放置逻辑,完成从预览到放置的完整流程。
- 编辑器友好:用
OnValidate()在修改参数时自动更新辅助变量,不用进入Play模式就能看到效果。
使用注意事项
- 给你的地面或者放置平面设置单独的Layer,然后在组件面板里把
placementLayer设为这个Layer。 - 确保
buildingPrefab有Collider组件,不然预览的半透明材质可能不会生效。 - 如果你的建筑是有旋转需求的,可以再加个右键旋转预览的逻辑,这个代码里没写,你可以自己扩展~
内容的提问来源于stack exchange,提问作者RugbugRedfern
相关产品推荐
相关产品推荐

