using UnityEngine; using UnityEngine.InputSystem; namespace Agame { /// /// 第一人称视角控制:鼠标横移带动 body 偏航,纵移带动相机俯仰。 /// /// 网络侧前提(重要):本组件挂在 PlayerCamera 上,而 PlayerCamera 只会被 /// 为「本地拥有者」启用(见其 ApplyOwnership)。 /// 非拥有者客户端的相机是 disabled 的,因此这里不会执行,天然不需要再判所有权。 /// /// 反过来说:如果日后把 PlayerCamera 改成常驻启用,必须补上所有权判断, /// 否则每个客户端都会用自己的鼠标去转动别人玩家的视角。 /// public class MouseLook : MonoBehaviour { [Header("Sensitivity")] [Tooltip("Degrees of rotation per pixel of mouse movement.")] [SerializeField] private float mouseSensitivity = 0.12f; [Header("Vertical Clamp")] [SerializeField] private float minLookAngle = -80f; [SerializeField] private float maxLookAngle = 80f; [Header("References")] [Tooltip("被偏航(Horizontal)的躯体 Transform,通常是玩家根节点。")] [SerializeField] private Transform characterBody; private float _xRotation; private bool _warnedMissingBody; private void OnEnable() { // 跟随本对象的启用状态锁定光标:只有本地拥有者的相机会被启用, // 所以光标锁定/释放自然就只在拥有者这一侧发生。 LockCursor(true); } private void OnDisable() { // 对象被禁用(非拥有者,或玩家已下线)时务必释放光标,否则会锁死在别人的机器上。 LockCursor(false); } private void Update() { Mouse mouse = Mouse.current; if (mouse == null) return; // Esc 释放光标,左键重新抓取(方便编辑器里切出去)。 Keyboard kb = Keyboard.current; if (kb != null && kb.escapeKey.wasPressedThisFrame) LockCursor(false); if (mouse.leftButton.wasPressedThisFrame && Cursor.lockState != CursorLockMode.Locked) LockCursor(true); if (Cursor.lockState != CursorLockMode.Locked) return; // 新输入系统下 delta 的单位是像素,不是轴值。 Vector2 delta = mouse.delta.ReadValue(); // 横移:转动躯体 if (characterBody != null) { characterBody.Rotate(Vector3.up * (delta.x * mouseSensitivity)); } else if (!_warnedMissingBody) { _warnedMissingBody = true; Debug.LogWarning( $"[{nameof(MouseLook)}] 未指定 Character Body,水平视角不会生效。请指向玩家根节点的 Transform。", this); } // 纵移:俯仰相机,并夹在上下限之间防止翻转 _xRotation = Mathf.Clamp(_xRotation - (delta.y * mouseSensitivity), minLookAngle, maxLookAngle); transform.localRotation = Quaternion.Euler(_xRotation, 0f, 0f); } private void LockCursor(bool locked) { Cursor.lockState = locked ? CursorLockMode.Locked : CursorLockMode.None; Cursor.visible = !locked; } } }