using UnityEngine; #if ENABLE_INPUT_SYSTEM using UnityEngine.InputSystem; #endif // Basic touch/mouse movement controller suitable for top-down 2D or 3D overworld // Attach to the player GameObject. Works with or without Rigidbody/Rigidbody2D. public class PlayerTouchController : MonoBehaviour { public float moveSpeed = 5f; public float stopDistance = 0.1f; // If true and a Rigidbody/Rigidbody2D is present, movement will use physics MovePosition. public bool useRigidbody = true; Vector3 targetPosition; bool hasTarget = false; Rigidbody rb3d; Rigidbody2D rb2d; void Awake() { rb3d = GetComponent(); rb2d = GetComponent(); } void Start() { // Initialize target to current position so object doesn't snap targetPosition = transform.position; hasTarget = false; } void Update() { HandleInput(); if (hasTarget) { MoveTowardsTarget(); } } void HandleInput() { #if ENABLE_INPUT_SYSTEM // Using the new Unity Input System (InputSystem package) only. // Touch input (mobile) if (Touchscreen.current != null && Touchscreen.current.touches.Count > 0) { var touch = Touchscreen.current.touches[0]; // press indicates the touch is down; use position even while moved/held if (touch.press != null && touch.press.isPressed) { Vector2 pos = touch.position.ReadValue(); SetTargetFromScreenPoint(new Vector3(pos.x, pos.y, 0f)); return; } } // Mouse support (Editor/PC) if (Mouse.current != null && Mouse.current.leftButton != null && Mouse.current.leftButton.isPressed) { Vector2 mpos = Mouse.current.position.ReadValue(); SetTargetFromScreenPoint(new Vector3(mpos.x, mpos.y, 0f)); } #else // Input System package not yet enabled/installed. Provide a helpful runtime message. // This branch ensures the script compiles until the package is installed and Player Settings are set. if (Application.isPlaying) { Debug.LogError("PlayerTouchController: New Input System is not enabled. Install 'com.unity.inputsystem' and set Active Input Handling to 'Input System Package' (or Both) in Player Settings, then restart the Editor."); } #endif } void SetTargetFromScreenPoint(Vector3 screenPoint) { Camera cam = Camera.main; if (cam == null) { Debug.LogWarning("PlayerTouchController: No Camera.main found."); return; } // Convert screen point to world point at the player's depth Vector3 worldPoint = cam.ScreenToWorldPoint(new Vector3(screenPoint.x, screenPoint.y, cam.WorldToScreenPoint(transform.position).z)); // For 2D top-down games using orthographic camera, z will be player's z already. targetPosition = worldPoint; hasTarget = true; } void MoveTowardsTarget() { // Keep original y (or z for 2D) depending on scene setup. We assume the player moves on the plane defined by its current transform. Vector3 current = transform.position; // Create a target that preserves the movement plane (so we don't change vertical axis unexpectedly) Vector3 planarTarget = targetPosition; // If the camera is orthographic top-down (y is up), preserve y // Heuristic: if camera forward is (0,-1,0) then y is up and we should preserve y Camera cam = Camera.main; if (cam != null && Vector3.Dot(cam.transform.forward, Vector3.down) > 0.5f) { planarTarget.y = current.y; } else { // Otherwise assume z is depth for 2D setups and preserve z planarTarget.z = current.z; } float step = moveSpeed * Time.deltaTime; Vector3 next = Vector3.MoveTowards(current, planarTarget, step); // If using Rigidbody, move via physics when present if (useRigidbody && rb2d != null) { rb2d.MovePosition(new Vector2(next.x, next.y)); transform.position = new Vector3(next.x, next.y, transform.position.z); // ensure transform matches } else if (useRigidbody && rb3d != null) { rb3d.MovePosition(next); } else { transform.position = next; } if (Vector3.Distance(transform.position, planarTarget) <= stopDistance) { hasTarget = false; } } }