using System; using System.Collections.Generic; using DG.Tweening; using SickscoreGames.HUDNavigationSystem; using UniRx; using UnityEngine; using UnityEngine.UI; using SLSUtilities.General; using Unity.Cinemachine; using Ease = DG.Tweening.Ease; namespace Cielonos.MainGame.Characters { public partial class LockTargetSubmodule : SubmoduleBase { private Player player => owner.player; private PlayerViewSubcontroller viewSc => owner; private PlayerInputSubcontroller inputSc => player.inputSc; private HUDNavigationSystem navigationSystem => HUDNavigationSystem.Instance; private HUDNavigationCanvas navigationCanvas => HUDNavigationCanvas.Instance; /// /// 通常ACT类武器锁定目标时自动旋转摄像机,即使用LockTargetCamera /// TPS类远程武器不自动旋转,仅在目标上显示锁定标记,不切换摄像机 /// public bool isAutoRotate; /// /// 是否正在锁定目标 /// public bool isLocking; /// /// 是否正在使用锁定目标摄像机 /// public bool isUsingLockTargetCamera => isLocking && isAutoRotate; public bool isDuringCameraSwitch; private const float CameraSwitchCooldown = 0.25f; public CharacterBase lockTarget; private float lastTargetSwitchTime; private const float TargetSwitchCooldown = 0.25f; public Transform targetPoint; private Tweener iconTween; public LockTargetSubmodule(PlayerViewSubcontroller owner) : base(owner) { isLocking = false; isAutoRotate = false; isDuringCameraSwitch = false; lockTarget = null; targetPoint = null; } public void Update() { if (isUsingLockTargetCamera && !isDuringCameraSwitch) { if (targetPoint != null) { // 平滑跟随目标 Vector3 currentRotation = viewSc.cameraRoot.eulerAngles; Vector3 targetDirection = targetPoint.position - viewSc.cameraRoot.position; Quaternion targetRotation = Quaternion.LookRotation(targetDirection); viewSc.cameraRoot.rotation = Quaternion.Slerp( viewSc.cameraRoot.rotation, targetRotation, 1f - Mathf.Exp(-10f * Time.deltaTime) ); } } } } public partial class LockTargetSubmodule { public void SwitchLockState() { if (isLocking) { UnlockTarget(); } else { LockTarget(true); } } public void LockTarget(bool isAutoRotate) { if(isDuringCameraSwitch) return; CharacterBase target = CombatManager.EnemySm.GetNearestEnemy(50f); if (target != null) { this.isLocking = true; this.isAutoRotate = isAutoRotate; this.lockTarget = target; this.isDuringCameraSwitch = true; if (isAutoRotate) { targetPoint = target.bodyPartsSc.cameraLockingPoint ?? target.bodyPartsSc.staticCenterPoint; viewSc.currentCamera = viewSc.lockingTargetCamera; viewSc.lockingTargetCamera.LookAt = targetPoint; viewSc.stateDrivenCamera.GetComponent().SetBool("isLockTarget", true); viewSc.cameraRoot.DOLookAt(targetPoint.position, 0.5f) .SetEase(Ease.InOutSine) .OnComplete(() => { isDuringCameraSwitch = false; }) .Play(); } else { Observable.Timer(TimeSpan.FromSeconds(0.5f)).First().Subscribe(_ => { isDuringCameraSwitch = false; }); } lockTarget.navigationElement.showIndicator = true; Image icon = lockTarget.navigationElement.Indicator.OnscreenIcon; iconTween?.Kill(true); iconTween = icon.GetComponent().DOScale(1f, 0.5f).From(0f).SetEase(Ease.OutQuart).Play(); } } public void UnlockTarget() { if(isDuringCameraSwitch) return; Vector3 currentEuler = viewSc.playerCamera.transform.rotation.eulerAngles; var inputController = viewSc.freeLookCamera.GetComponent(); if (inputController == null) return; float newYaw = currentEuler.y; float newPitch = currentEuler.x; if (newPitch > 180f) newPitch -= 360f; float minPitch = -20f; float maxPitch = 70f; newPitch = Mathf.Clamp(newPitch, minPitch, maxPitch); CinemachineOrbitalFollow orbitalFollow = viewSc.freeLookCamera.GetComponent(); orbitalFollow.HorizontalAxis.Value = newYaw; orbitalFollow.VerticalAxis.Value = newPitch; if (lockTarget != null) { lockTarget.navigationElement.showIndicator = false; } this.isLocking = false; this.isAutoRotate = false; this.lockTarget = null; this.targetPoint = null; viewSc.stateDrivenCamera.GetComponent().SetBool("isLockTarget", false); } /// /// 切换锁定目标 /// public void SwitchTarget(float direction) { if (!isLocking || isDuringCameraSwitch) return; if (Time.time - lastTargetSwitchTime < TargetSwitchCooldown) return; List sortedEnemies = CombatManager.EnemySm.GetVisibleEnemiesSortedByScreenX(); if (sortedEnemies.Count <= 1) return; int currentIndex = sortedEnemies.IndexOf(lockTarget); if (currentIndex < 0) { currentIndex = 0; } int dir = direction > 0 ? -1 : 1; int newIndex = currentIndex + dir; // 边界检查(无循环) if (newIndex < 0 || newIndex >= sortedEnemies.Count) { return; } CharacterBase newTarget = sortedEnemies[newIndex]; // 目标相同检查 if (newTarget == lockTarget) { return; } lastTargetSwitchTime = Time.time; SetNewTarget(sortedEnemies[newIndex]); } private void SetNewTarget(CharacterBase newTarget) { if (lockTarget != null) { lockTarget.navigationElement.showIndicator = false; } lockTarget = newTarget; targetPoint = newTarget.bodyPartsSc.cameraLockingPoint ?? newTarget.bodyPartsSc.staticCenterPoint; if (isUsingLockTargetCamera) { viewSc.lockingTargetCamera.LookAt = targetPoint; } lockTarget.navigationElement.showIndicator = true; Image icon = lockTarget.navigationElement.Indicator.OnscreenIcon; iconTween?.Kill(true); iconTween = icon.GetComponent().DOScale(1f, 0.3f).From(0.5f).SetEase(Ease.OutQuart).Play(); } } }