虚拟场景对象交互需求:实现触摸选中任意对象进行移动缩放旋转
Hey there! I totally get the frustration of only being able to interact with the last placed object—let's fix this so you can select, move, scale, and rotate any virtual object at will. Here's a structured approach that works for most AR/3D interactive setups:
1. Add Interactive Components to Each Virtual Object
First, every object you spawn needs a script that handles selection detection and manipulation logic. Let's use a common Unity example (adjust for your framework if needed):
Create a script like InteractiveObject.cs and attach it to every virtual object you instantiate:
using UnityEngine; using UnityEngine.EventSystems; public class InteractiveObject : MonoBehaviour, IPointerClickHandler { // Visual feedback when selected (e.g., outline material) public Material highlightMaterial; private Material originalMaterial; private Renderer objRenderer; private void Awake() { objRenderer = GetComponent<Renderer>(); originalMaterial = objRenderer.material; } // Triggered when the object is touched/clicked public void OnPointerClick(PointerEventData eventData) { // Notify the selection manager this object is now active ObjectSelectionManager.Instance.SetSelectedObject(this); } public void Highlight(bool isSelected) { objRenderer.material = isSelected ? highlightMaterial : originalMaterial; } // Add your manipulation methods here public void Move(Vector3 delta) { transform.position += delta; } public void Scale(float scaleFactor) { transform.localScale *= scaleFactor; // Optional: Clamp scale to prevent too small/large objects transform.localScale = Vector3.Max(transform.localScale, new Vector3(0.1f, 0.1f, 0.1f)); } public void Rotate(Vector3 rotationDelta) { transform.Rotate(rotationDelta); } }
2. Create a Global Selection Manager
You need a singleton manager to track the currently selected object—this ensures you always know which object to apply touch inputs to:
using UnityEngine; public class ObjectSelectionManager : MonoBehaviour { public static ObjectSelectionManager Instance { get; private set; } private InteractiveObject currentSelectedObject; private void Awake() { // Enforce singleton pattern if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; DontDestroyOnLoad(gameObject); } } public void SetSelectedObject(InteractiveObject newSelected) { // Deselect the previous object if there was one if (currentSelectedObject != null) { currentSelectedObject.Highlight(false); } // Set and highlight the new selected object currentSelectedObject = newSelected; currentSelectedObject.Highlight(true); } public InteractiveObject GetCurrentSelectedObject() { return currentSelectedObject; } }
3. Handle Touch Inputs for Manipulation
Create a touch input handler script that reads touch gestures and applies them to the currently selected object:
using UnityEngine; public class TouchInputHandler : MonoBehaviour { private float initialDistance; private Vector3 initialRotation; private void Update() { var selectedObj = ObjectSelectionManager.Instance.GetCurrentSelectedObject(); if (selectedObj == null) return; // Single touch for movement if (Input.touchCount == 1) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Moved) { // Convert touch delta to world space movement (adjust based on your camera setup) Vector3 moveDelta = new Vector3(touch.deltaPosition.x * 0.01f, touch.deltaPosition.y * 0.01f, 0); selectedObj.Move(moveDelta); } } // Two touches for scaling and rotation else if (Input.touchCount == 2) { Touch touch1 = Input.GetTouch(0); Touch touch2 = Input.GetTouch(1); if (touch1.phase == TouchPhase.Began || touch2.phase == TouchPhase.Began) { initialDistance = Vector2.Distance(touch1.position, touch2.position); initialRotation = selectedObj.transform.eulerAngles; } else if (touch1.phase == TouchPhase.Moved && touch2.phase == TouchPhase.Moved) { // Handle scaling float currentDistance = Vector2.Distance(touch1.position, touch2.position); float scaleFactor = currentDistance / initialDistance; selectedObj.Scale(scaleFactor); initialDistance = currentDistance; // Handle rotation Vector2 touch1PrevPos = touch1.position - touch1.deltaPosition; Vector2 touch2PrevPos = touch2.position - touch2.deltaPosition; Vector2 prevDir = touch2PrevPos - touch1PrevPos; Vector2 currentDir = touch2.position - touch1.position; float rotationDelta = Vector2.SignedAngle(prevDir, currentDir); selectedObj.Rotate(new Vector3(0, rotationDelta, 0)); } } } }
4. Key Notes to Make This Work
- Raycast Setup: Ensure your camera has a
PhysicsRaycaster(for Unity UI/3D raycasting) so touch clicks can detect objects. - Layer Masking: If you have non-interactive objects, assign interactive ones to a specific layer and filter raycasts to only that layer.
- Spawning Objects: When you instantiate new virtual objects, make sure the
InteractiveObjectscript is attached (either prefab it or add it via code on spawn). - Visual Feedback: The highlight material helps users clearly see which object is selected—this is crucial for UX.
This setup decouples object selection from spawn order, so every object (old or new) can be selected and manipulated as long as it has the interactive component attached.
内容的提问来源于stack exchange,提问作者Ancuta Cozma

