using UnityEngine; using UnityEngine.InputSystem; using Cinemachine; using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch; using BITKit.IO; using Newtonsoft.Json; using System.IO; using Cysharp.Threading.Tasks; using UnityEngine.InputSystem.Controls; namespace BITKit { public class SimpleCameraLook : MonoBehaviour { private string fileName => Path.Combine(Utility.Path.persistentDataPath, gameObject.scene.name, $"{gameObject.GetInstanceID()}.bit"); public enum State { Mouse, Touchscreen, } [Header(Constant.Header.Gameobjects)] public Transform cameraRoot; public CinemachineVirtualCamera virtualCamera; [Header(Constant.Header.Settings)] public TransformMode touchMode; [Header(Constant.Header.Settings)] public InputActionReference viewAction; public InputActionReference movementAction; public InputActionReference adsAction; public InputActionReference entryAction; public InputActionReference entryMoveAction; public InputActionGroup inputGroup = new(); [Header(Constant.Header.Settings)] public float minEntrySqrMagnitude = 1; public LayerMask layerMask; [Header(Constant.Header.Settings)] public AnimationCurve scollCurve = new(); public AnimationCurve moveCurve = new(); public AnimationCurve touchMoveCurve = new(); public Vector2 cameraDistanceLimit = new(1, 64); [Header(Constant.Header.Providers)] [SerializeField,SerializeReference,SubclassSelector] private ICondition allowInput; [Header(Constant.Header.Debug)] public int touchesCount; public bool isParallelVector; [Header(Constant.Header.InternalVariables)] public Vector3 lookInput; public Vector3 moveVector; public ValidHandle freeLooking = new(); public ValidHandle moving = new(); private Cinemachine3rdPersonFollow tpv; private Location startLocation; private Location savedLocation; private float savedDistance; private float startDistance; private CinemachineBrain _brain; public Vector3 Position { get=>cameraRoot.position; set=>cameraRoot.position = value; } public void Reset() { lookInput = MathV.TransientRotationAxis(startLocation.position); cameraRoot.position = startLocation; tpv.CameraDistance = startDistance; } public void DisableInput(bool disable) { inputGroup.allowInput.SetDisableElements(nameof(DisableInput), disable); } public void SetTouchMode(int index) { SetTouchMode((TransformMode)index); } public void SetTouchMode(TransformMode mode) { touchMode = mode; } public void SetViewScale(float value) { tpv.CameraDistance = value; } public float GetViewScale() => tpv.CameraDistance; public void Align(Transform target) { if (target.TryGetComponent(out var x)) { cameraRoot.position = x.cameraRoot.position; //lookInput = x.lookInput; lookInput = MathV.TransientRotationAxis(x.cameraRoot.rotation.eulerAngles); if (x.tpv) tpv.CameraDistance = x.tpv.CameraDistance; } else if (target.TryGetComponent(out var vCam)) { cameraRoot.position = vCam.Follow.position; lookInput = MathV.TransientRotationAxis(vCam.LookAt.rotation.eulerAngles); var e = vCam.GetCinemachineComponent(); if (e) { tpv.CameraDistance = e.CameraDistance; } } else { cameraRoot.position = target.position; lookInput = MathV.TransientRotationAxis(target.eulerAngles); } } public void Align(Vector3 position)=>cameraRoot.position= position; public void SaveLocation() { savedLocation = new(cameraRoot); savedDistance = tpv.CameraDistance; BITAssets.Build( PathHelper.GetFilePath(fileName), new IAsset[] { new BITAsset(nameof(savedLocation),JsonUtility.ToJson(savedLocation)), new BITAsset(nameof(savedDistance),JsonConvert.SerializeObject(savedDistance)), } ); } public void LoadLocation() { lookInput = MathV.TransientRotationAxis(savedLocation.rotation.eulerAngles); cameraRoot.position = savedLocation; tpv.CameraDistance = savedDistance; } private void Awake() { freeLooking.AddListener(OnFreeLook); tpv = virtualCamera.GetCinemachineComponent(); savedLocation = startLocation = new(cameraRoot); savedDistance = startDistance = tpv.CameraDistance; inputGroup.RegisterCallback(viewAction, OnView); inputGroup.RegisterCallback(movementAction, OnMovement); inputGroup.RegisterCallback(adsAction, OnAds); inputGroup.RegisterCallback(entryAction, OnEntry); inputGroup.RegisterCallback(entryMoveAction, OnEntryMoveAction); if (Camera.main != null) _brain = Camera.main.GetComponent(); } private async void Start() { var fileName = this.fileName; await UniTask.SwitchToThreadPool(); if (File.Exists(fileName)) { savedLocation = BITAssets.Read(fileName, nameof(savedLocation)); savedDistance = BITAssets.Read(fileName, nameof(savedDistance)); } } private void OnEnable() { SetEnabled(true); lookInput = MathV.TransientRotationAxis(cameraRoot.eulerAngles); freeLooking.RemoveDisableElements(this); if (virtualCamera is not null) virtualCamera.enabled = true; } private void OnDisable() { SetEnabled(false); freeLooking.AddDisableElements(this); if (virtualCamera is not null) virtualCamera.enabled = false; } private void OnDestroy() { inputGroup.Dispose(); } private void SetEnabled(bool enabled) { //BIT4Log.Log($"正在设置输入状态:{enabled}"); BITAppForUnity.AllowTouchSupport.SetElements(this, enabled); inputGroup.allowInput.SetElements(this, enabled); } private void Update() { //var debuger = DI.Get(); var playerConfig = PlayerConfig.singleton; var sensitivity = playerConfig.touchSensitivity; //debuger.Log(nameof(Touch.activeTouches), Touch.activeTouches.Count.ToString()); touchesCount = Touch.activeTouches.Count; if (Mouse.current is not null && Mouse.current.leftButton.wasPressedThisFrame) { var ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue()); if (Physics.Raycast(ray, out var raycast, 256, layerMask)) { cameraRoot.position = raycast.point; } } if (_brain) { inputGroup.allowInput.SetDisableElements(nameof(CinemachineBrain),virtualCamera != (CinemachineVirtualCamera)_brain.ActiveVirtualCamera); } } private void LateUpdate() { lookInput.x = Mathf.Clamp(lookInput.x, -80, 80); var newRotation = Quaternion.Euler(lookInput); cameraRoot.rotation = Quaternion.Lerp(cameraRoot.rotation, newRotation, 48 * Time.deltaTime); var offset = cameraRoot.localRotation * moveVector; cameraRoot.localPosition -= offset * Time.deltaTime; } private void OnView(InputAction.CallbackContext context) { if (allowInput.OnCheck() is false) return; var playerConfig = PlayerConfig.singleton; var sensitivity = playerConfig.sensitivity * playerConfig.m_yaw; var delta = context.ReadValue(); switch (context.control.device) { case Mouse mouse: if (freeLooking && !moving) { lookInput.x -= delta.y * sensitivity; lookInput.y += delta.x * sensitivity; } break; case Touchscreen touchscreen: switch (touchMode) { case TransformMode.Move: break; case TransformMode.Rotate: lookInput.x -= delta.y * sensitivity; lookInput.y += delta.x * sensitivity; moveVector = default; break; case TransformMode.Scale: break; } if (touchMode is TransformMode.Scale) { OnAds(context); } break; } } private void OnAds(InputAction.CallbackContext context) { try { BITAppForUnity.ThrowIfWindowNotFocus(); switch (context.control.device) { case Mouse mouse: OnAds(context.ReadValue().y); break; case Touchscreen touchscreen: /* lookInput.x -= delta.y * sensitivity; lookInput.y += delta.x * sensitivity; */ switch (touchMode) { case TransformMode.Move: break; case TransformMode.Rotate: moveVector = default; break; case TransformMode.Scale: OnAds(context.ReadValue().y); break; } break; } } catch (MouseNotOverGameViewException) { } } private void OnAds(float delta) { var newDistance = tpv.CameraDistance - delta * Time.deltaTime * scollCurve.Evaluate(tpv.CameraDistance); tpv.CameraDistance = Mathf.Clamp(newDistance, cameraDistanceLimit.x, cameraDistanceLimit.y); } private void OnFreeLook(bool active) { BITAppForUnity.AllowCursor.SetDisableElements(this, active); } private void OnEntry(InputAction.CallbackContext context) { if (context.started) { var delta = viewAction.action.ReadValue(); if (delta.sqrMagnitude > minEntrySqrMagnitude) freeLooking.AddElement(this); } else if (context.canceled) { freeLooking.RemoveElement(this); } } private void OnMovement(InputAction.CallbackContext context) { switch (context.control.device) { case Mouse mouse: if (moving && freeLooking) { moveVector = context.ReadValue() * moveCurve.Evaluate(tpv.CameraDistance); } break; case Touchscreen touchscreen: /* lookInput.x -= delta.y * sensitivity; lookInput.y += delta.x * sensitivity; */ switch (touchMode) { case TransformMode.Move: moveVector = context.ReadValue() * moveCurve.Evaluate(tpv.CameraDistance); break; case TransformMode.Rotate: break; case TransformMode.Scale: break; } break; } if (context.canceled) { moveVector = default; } } private void OnEntryMoveAction(InputAction.CallbackContext context) { if (context.started) { moving.AddElement(this); } else if (context.canceled) { moving.RemoveElement(this); } } } }