跳至主要内容
版本: 2.0.0

简单位置控制教程

本教程以《快速入门指南》中介绍的概念为基础,演示如何在 Unity 场景中使用动态游戏对象控制 Inverse3 设备的光标位置。

导言

Inverse3 设备支持两种控制模式: 武力控制位置控制. 前者根据光标的位置调整力,后者则直接操纵光标的位置。 本教程将利用 动态场景中的力反馈教程该项目侧重于采用 CursorSetPositionFixedUpdate 方法,使设备的光标能够跟随移动的游戏对象。

场景设置

中描述的场景配置开始。 动态场景中的力反馈教程其中包括 触觉装置 和一个 移动球 控制的游戏对象 MovableObject 组件。

实现 SpherePositionControl 组件

更换 球体力反馈 组件上的 移动球 游戏对象的新 C# 脚本,该脚本名为 SpherePositionControl.cs. 在 SpherePositionControl 类:

public Inverse3 inverse3;
public float minSyncDistance = 0.05f;
private bool _isCursorSynchronized;
  • inverse3:Inverse3 设备组件的引用,通过检查器设置。
  • minSyncDistance(最小同步距离):启动光标同步的最小距离阈值。
  • _isCursorSynchronized(光标同步):指示光标的移动是否与移动球同步的标志。

同步逻辑

根据与移动球的距离实现启动和停止光标同步的方法:

private void StartSynchronizeCursor()
{
var cursorPosition = inverse3.Cursor.transform.position;
GetComponent<MovableObject>().SetTargetPosition(cursorPosition, teleport: true);
_isCursorSynchronized = true;
}

private void StopSynchronizeCursor()
{
_isCursorSynchronized = !inverse3.TryResetForce();
}

在这里,我们使用 MovableObject.SetTargetPosition(position, teleport:true)移动球 到光标位置。

更新和固定更新方法

Update 方法,根据光标与 移动球:

private void Update()
{
var distance = Vector3.Distance(inverse3.Position, transform.position);
if (!_isCursorSynchronized && distance <= minSyncDistance)
{
StartSynchronizeCursor();
}
else if (_isCursorSynchronized && distance > minSyncDistance)
{
StopSynchronizeCursor();
}
}

FixedUpdate,更新光标的位置,使其与 移动球 如果同步处于激活状态:

private void FixedUpdate()
{
if (_isCursorSynchronized)
{
inverse3.CursorSetPosition(transform.position);
}
}

使用 FixedUpdate 方法可确保位置控制以稳定的帧速率运行,不受图形渲染性能的影响。

初始化

Awake()确保 移动球 从反向 3 设备可触及的位置开始,无论其手持方向如何:

private void Awake()
{
inverse3.DeviceOpened.AddListener(device =>
{
GetComponent<MovableObject>().SetTargetPosition(((Inverse3)device).WorkspaceCenter, teleport: true);
});
}

游戏体验

固定好 Inverse3 设备,确保有足够的空间。 启动 "游戏模式 "并用光标接近移动球,Inverse3 将跟随移动球移动。 可以使用键盘输入来移动移动,展示光标跟踪移动位置的能力。

简单位置控制

源文件

本教程的完整场景和相关文件可在 教程 Unity 软件包管理器中的样本。 其中包括 MovableObject 脚本,该脚本在多个示例中用于通过键盘输入控制游戏对象的移动。

SpherePositionControl.cs

/*
* Copyright 2024 Haply Robotics Inc. All rights reserved.
*/

using Haply.Inverse.Unity;
using Haply.Samples.Tutorials.Utils;
using UnityEngine;

namespace Haply.Samples.Tutorials._5_SimplePositionControl
{
/// <summary>
/// Controls the Inverse3 cursor position based on the current position of this GameObject.
/// When the GameObject is within a specified distance from the cursor, it initiates synchronized control,
/// allowing the cursor to follow the GameObject's movements.
/// </summary>
[RequireComponent(typeof(MovableObject))]
public class SpherePositionControl : MonoBehaviour
{
public Inverse3 inverse3;

[Tooltip("Minimum distance required to initiate synchronized control between this GameObject and the Inverse3 cursor.")]
[Range(0, 1)]
public float minSyncDistance = 0.05f;

private bool _isCursorSynchronized;

private void Awake()
{
// Ensure inverse3 is set, finding it in the scene if necessary.
if (inverse3 == null)
{
inverse3 = FindObjectOfType<Inverse3>();
}

// When inverse3 is ready, so the handedness is defined
inverse3.DeviceOpened.AddListener(device =>
{
var inverse3 = (Inverse3)device;
// Teleport the sphere to its workspace center to ensure it can be reached,
// regardless of whether the device is left or right-handed. This ensures the GameObject starts in a
// position that is accessible by the Inverse3 device.
GetComponent<MovableObject>().SetTargetPosition(((Inverse3)device).WorkspaceCenter, teleport:true);
});
}

private void OnDisable()
{
// Ensure movement synchronization is disabled when the component is disabled.
StopSynchronizeCursor();
}

private void Update()
{
// Calculate the distance between the Inverse3 position and this object's position.
var distance = Vector3.Distance(inverse3.Position, transform.position);

// Enable synchronized movement if within the minimum sync distance and not already synced.
if (!_isCursorSynchronized && distance <= minSyncDistance)
{
StartSynchronizeCursor();
}
// Disable synchronized movement if outside the minimum sync distance and currently synced.
else if (_isCursorSynchronized && distance > minSyncDistance)
{
StopSynchronizeCursor();
}
}

private void FixedUpdate()
{
if (_isCursorSynchronized)
{
// If in sync, set the Inverse3 cursor position to this object's position.
inverse3.CursorSetPosition(transform.position);
}
}

private void StartSynchronizeCursor()
{
// Get the current cursor position.
var cursorPosition = inverse3.Cursor.transform.position;

// Teleport this object to the cursor position to avoid a sudden jump when position control starts.
GetComponent<MovableObject>().SetTargetPosition(cursorPosition, teleport:true);

// Start synchronizing the movement of this object with the cursor.
_isCursorSynchronized = true;
}

private void StopSynchronizeCursor()
{
// Stop synchronizing the movement.
_isCursorSynchronized = !inverse3.TryResetForce();
}
}
}