我将首先尝试解释此示例中发生的情况。从 UI.cs 中的调用开始到 Query_OnFloor_AwayFromMe()...
- 这会调用 LevelSolver.cs。在这个方法中,它创建了一个新的 PlacementQuery 对象并调用 PlaceObjectAsync。
- PlaceObjectAsync 通过调用 System.Threading.Tasks.Task.Run 创建一个新线程,调用空间理解的实际工作已经完成。这项工作必须在单独的线程中完成,因为它需要很长时间,因此需要跨多个帧执行,否则您的应用程序将在执行期间锁定。
- 在调用新线程 PlaceObject 时,这会实际调用空间理解 (SpatialUnderstandingDllObjectPlacement.Solver_PlaceObject),这是对 C++ HoloToolkit 的调用,这是需要很长时间的调用。当这完成时,结果被添加到基本上是一个工作队列的placementResults。
- 在 LevelSolver.cs 中有一个 Update 方法,每次统一绘制新框架时都会调用该方法。这里有一个对 Draw_PlacementResults 的调用。
- Draw_PlacementResults 循环遍历放置结果工作队列中的每个结果,并调用 LineDrawer.cs 中的 Draw_AnimatedBox
- 这就是我们在上一个问题中讨论的对 Draw_Box 的调用的方式。
所以另一个问题是我如何修改这个例子来放置你自己的模型。我建议在 LevelSolver.cs 中进行修改。尝试将 Update() 更改为如下内容:
private void Update()
{
// Can't do any of this till we're done with the scanning phase
if (SpatialUnderstanding.Instance.ScanState != SpatialUnderstanding.ScanStates.Done)
{
return;
}
// Make sure the solver has been initialized
if (!IsSolverInitialized &&
SpatialUnderstanding.Instance.AllowSpatialUnderstanding)
{
InitializeSolver();
}
// Constraint queries
if (SpatialUnderstanding.Instance.ScanState == SpatialUnderstanding.ScanStates.Done)
{
Update_Queries();
}
// Handle async query results
ProcessPlacementResults();
MyProcessPlacementResults();
}
这将导致它停止绘制框,而是调用“MyProcessPlacementResults”来处理对结果进行的处理。我会在 LevelSolver.cs 中添加类似这样的方法。(在我借用的代码中,我正在创建一棵树)
private void MyProcessPlacementResults()
{
if (placementResults.Count > 0)
{
var toPlace = placementResults.Dequeue();
var rotation = Quaternion.LookRotation(toPlace.Result.Forward, Vector3.up);
CreateTree(toPlace.Result.Position, rotation);
}
}
这是我用来在正确位置实际实例化树的代码:
private GameObject ThisIsYourCustomModelInMineItWasATree;
private Vector3 TheSizeIPassedToSpatialUnderstanding = new Vector3(1, 1, 1);
public void CreateHologram(Vector3 positionCenter, Quaternion rotation)
{
ThisIsYourCustomModelInMineItWasATree = GameObject.CreatePrimitive(PrimitiveType.Sphere);
GameObject newObject = Instantiate(ThisIsYourCustomModelInMineItWasATree, positionCenter, rotation) as GameObject;
if (newObject != null)
{
// Set the parent of the new object the GameObject it was placed on
newObject.transform.parent = gameObject.transform;
newObject.transform.localScale = StretchToFit(ThisIsYourCustomModelInMineItWasATree, TheSizeIPassedToSpatialUnderstanding);
newObject.AddComponent<MeshCollider>();
}
}
为了完整起见,这里是我用来将其缩放到所需大小的代码:
private Vector3 StretchToFit(GameObject obj, Vector3 desiredSize)
{
var curBounds = GetBoundsForAllChildren(obj).size;
return new Vector3(desiredSize.x / curBounds.x / 2, desiredSize.y, desiredSize.z / curBounds.z / 2);
}
private Bounds GetBoundsForAllChildren(GameObject findMyBounds)
{
Bounds result = new Bounds(Vector3.zero, Vector3.zero);
foreach (var renderer in findMyBounds.GetComponentsInChildren<Renderer>())
{
if (result.extents == Vector3.zero)
{
result = renderer.bounds;
}
else
{
result.Encapsulate(renderer.bounds);
}
}
return result;
}
我还将私有变量placementResults 从列表更改为队列。我这样做是因为 List 用于在每一帧中绘制一个框。我们想要一个队列,因为我们将实例化一个新对象一次并让统一引擎管理它。找到这一行:
private List<PlacementResult> placementResults = new List<PlacementResult>();
并将其更改为:
private Queue<PlacementResult> placementResults = new Queue<PlacementResult>();
进行更改后,您将需要修复几个地方。删除方法 Draw_PlacementResults,反正它不再使用了。将调用placementResults.Add 的两个地方更改为placementResults.Enqueue。