ARCore:扫描足够空间后通过C#脚本随机生成预制体
如何在ARCore扫描到指定面积地板后随机生成预制体
嘿,这个需求其实很好实现,核心就是先检测扫描到的地板总面积是否达标,再在合格的地板平面范围内随机生成预制体。下面是我整理的分步方案和代码,直接就能用:
1. 核心逻辑拆解
首先得明确ARCore的平面检测机制:ARCore会自动识别平面并分类(比如地板、墙面),我们可以通过ARPlaneManager获取这些平面数据。我们要做的就是:
- 筛选出所有标记为「地板」的平面
- 计算它们的总面积,判断是否达到你设定的阈值
- 一旦达标,就在这些地板的范围内随机生成预制体
2. 完整C#代码实现
第一步:配置基础变量和引用
先创建一个脚本,绑定到场景中的AR Session Origin对象上,然后引用ARCore的核心组件:
using UnityEngine; using UnityEngine.XR.ARFoundation; using UnityEngine.XR.ARSubsystems; public class RandomFloorSpawner : MonoBehaviour { [Header("AR组件引用")] public ARPlaneManager planeManager; // 拖拽场景中的AR Plane Manager进来 [Header("生成参数")] public GameObject prefabToSpawn; // 要生成的预制体 public float requiredFloorArea = 2.5f; // 你需要的最小地板面积(单位:平方米) public int spawnAmount = 6; // 生成的预制体数量 private bool hasCompletedSpawn = false; // 防止重复生成的标记 void OnEnable() { // 订阅平面更新事件,每次ARCore更新平面数据时触发检查 planeManager.planeUpdated += CheckAndSpawn; } void OnDisable() { planeManager.planeUpdated -= CheckAndSpawn; } }
第二步:检测地板面积是否达标
添加方法来计算所有地板平面的总面积,判断是否满足要求:
private bool IsFloorAreaEnough() { float totalFloorArea = 0f; foreach (var plane in planeManager.trackables) { // 只统计分类为地板的平面 if (plane.classification == PlaneClassification.Floor) { // ARPlane的size属性是平面在X和Z方向的长度,相乘就是面积 totalFloorArea += plane.size.x * plane.size.z; } } // 打印当前总面积,方便调试(可以删掉) Debug.Log($"当前扫描到的地板总面积:{totalFloorArea:F2} 平方米"); return totalFloorArea >= requiredFloorArea; }
第三步:在地板上随机生成预制体
当面积达标后,随机选择地板平面,并在平面范围内生成预制体:
private void SpawnRandomPrefabs() { if (hasCompletedSpawn) return; // 收集所有地板平面 var floorPlanes = new System.Collections.Generic.List<ARPlane>(); foreach (var plane in planeManager.trackables) { if (plane.classification == PlaneClassification.Floor) { floorPlanes.Add(plane); } } if (floorPlanes.Count == 0) return; for (int i = 0; i < spawnAmount; i++) { // 随机选一个地板平面 ARPlane targetPlane = floorPlanes[Random.Range(0, floorPlanes.Count)]; // 计算平面内的随机局部坐标:X和Z方向在平面的一半范围内随机 float randomLocalX = Random.Range(-targetPlane.size.x / 2f, targetPlane.size.x / 2f); float randomLocalZ = Random.Range(-targetPlane.size.z / 2f, targetPlane.size.z / 2f); // 转换为世界空间坐标 Vector3 spawnPos = targetPlane.transform.TransformPoint(new Vector3(randomLocalX, 0f, randomLocalZ)); // 让预制体和平面保持同一旋转(可选,根据你的需求调整) Quaternion spawnRot = targetPlane.transform.rotation; // 生成预制体 Instantiate(prefabToSpawn, spawnPos, spawnRot); } hasCompletedSpawn = true; // 可选:关闭平面检测,避免后续平面更新干扰 planeManager.enabled = false; // 可选:隐藏所有已检测的平面 foreach (var plane in planeManager.trackables) { plane.gameObject.SetActive(false); } }
第四步:关联事件触发逻辑
在之前订阅的CheckAndSpawn方法里,把检测和生成逻辑串起来:
private void CheckAndSpawn(ARPlane plane) { if (IsFloorAreaEnough()) { SpawnRandomPrefabs(); // 取消订阅事件,避免重复触发 planeManager.planeUpdated -= CheckAndSpawn; } }
3. 实用优化建议
- 单平面判断:如果你只需要单个地板平面达到面积(而不是累加),修改
IsFloorAreaEnough方法,遍历的时候判断单个平面的面积是否大于等于阈值即可。 - 位置去重:如果不想让预制体重叠,可以记录已生成的位置,每次生成前判断距离是否足够。
- 对象池优化:如果生成大量预制体,建议用对象池代替
Instantiate,减少GC开销。 - 调试UI:可以在场景中加一个Text组件,实时显示当前地板总面积,方便调整阈值。
内容的提问来源于stack exchange,提问作者JDorman
相关产品推荐
相关产品推荐

