Files
Cielonos/Assets/Scripts/MainGame/Characters/Player/View/LockTargetSubmodule.cs

222 lines
7.9 KiB
C#
Raw Normal View History

2025-12-23 19:47:06 -05:00
using System;
2026-04-28 15:46:32 -04:00
using System.Collections.Generic;
2025-12-23 19:47:06 -05:00
using DG.Tweening;
using SickscoreGames.HUDNavigationSystem;
using UniRx;
using UnityEngine;
using UnityEngine.UI;
2026-02-13 09:22:11 -05:00
using SLSUtilities.General;
2026-01-03 18:19:39 -05:00
using Unity.Cinemachine;
2025-12-23 19:47:06 -05:00
using Ease = DG.Tweening.Ease;
namespace Cielonos.MainGame.Characters
{
public partial class LockTargetSubmodule : SubmoduleBase<PlayerViewSubcontroller>
{
private Player player => owner.player;
private PlayerViewSubcontroller viewSc => owner;
private PlayerInputSubcontroller inputSc => player.inputSc;
private HUDNavigationSystem navigationSystem => HUDNavigationSystem.Instance;
private HUDNavigationCanvas navigationCanvas => HUDNavigationCanvas.Instance;
/// <summary>
/// 通常ACT类武器锁定目标时自动旋转摄像机即使用LockTargetCamera
/// TPS类远程武器不自动旋转仅在目标上显示锁定标记不切换摄像机
/// </summary>
public bool isAutoRotate;
/// <summary>
/// 是否正在锁定目标
/// </summary>
public bool isLocking;
/// <summary>
/// 是否正在使用锁定目标摄像机
/// </summary>
public bool isUsingLockTargetCamera => isLocking && isAutoRotate;
2026-04-28 15:46:32 -04:00
public bool isDuringCameraSwitch;
private const float CameraSwitchCooldown = 0.25f;
2025-12-23 19:47:06 -05:00
public CharacterBase lockTarget;
2026-04-28 15:46:32 -04:00
private float lastTargetSwitchTime;
private const float TargetSwitchCooldown = 0.25f;
2025-12-23 19:47:06 -05:00
public Transform targetPoint;
private Tweener iconTween;
public LockTargetSubmodule(PlayerViewSubcontroller owner) : base(owner)
{
isLocking = false;
isAutoRotate = false;
2026-04-28 15:46:32 -04:00
isDuringCameraSwitch = false;
2025-12-23 19:47:06 -05:00
lockTarget = null;
targetPoint = null;
}
public void Update()
{
2026-04-28 15:46:32 -04:00
if (isUsingLockTargetCamera && !isDuringCameraSwitch)
2025-12-23 19:47:06 -05:00
{
2026-04-28 15:46:32 -04:00
if (targetPoint != null)
2025-12-23 19:47:06 -05:00
{
2026-04-28 15:46:32 -04:00
// 平滑跟随目标
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)
);
2025-12-23 19:47:06 -05:00
}
}
}
}
public partial class LockTargetSubmodule
{
public void SwitchLockState()
{
if (isLocking)
{
UnlockTarget();
}
else
{
LockTarget(true);
}
}
public void LockTarget(bool isAutoRotate)
{
2026-04-28 15:46:32 -04:00
if(isDuringCameraSwitch) return;
2025-12-23 19:47:06 -05:00
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(50f);
if (target != null)
{
this.isLocking = true;
this.isAutoRotate = isAutoRotate;
this.lockTarget = target;
2026-04-28 15:46:32 -04:00
this.isDuringCameraSwitch = true;
2025-12-23 19:47:06 -05:00
if (isAutoRotate)
{
targetPoint = target.bodyPartsSc.cameraLockingPoint ?? target.bodyPartsSc.staticCenterPoint;
viewSc.currentCamera = viewSc.lockingTargetCamera;
viewSc.lockingTargetCamera.LookAt = targetPoint;
viewSc.stateDrivenCamera.GetComponent<Animator>().SetBool("isLockTarget", true);
viewSc.cameraRoot.DOLookAt(targetPoint.position, 0.5f)
.SetEase(Ease.InOutSine)
2026-04-28 15:46:32 -04:00
.OnComplete(() => { isDuringCameraSwitch = false; })
2025-12-23 19:47:06 -05:00
.Play();
}
else
{
Observable.Timer(TimeSpan.FromSeconds(0.5f)).First().Subscribe(_ =>
{
2026-04-28 15:46:32 -04:00
isDuringCameraSwitch = false;
2025-12-23 19:47:06 -05:00
});
}
lockTarget.navigationElement.showIndicator = true;
Image icon = lockTarget.navigationElement.Indicator.OnscreenIcon;
iconTween?.Kill(true);
iconTween = icon.GetComponent<RectTransform>().DOScale(1f, 0.5f).From(0f).SetEase(Ease.OutQuart).Play();
}
}
public void UnlockTarget()
{
2026-04-28 15:46:32 -04:00
if(isDuringCameraSwitch) return;
2025-12-23 19:47:06 -05:00
2026-01-03 18:19:39 -05:00
Vector3 currentEuler = viewSc.playerCamera.transform.rotation.eulerAngles;
var inputController = viewSc.freeLookCamera.GetComponent<CinemachineInputAxisController>();
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<CinemachineOrbitalFollow>();
orbitalFollow.HorizontalAxis.Value = newYaw;
orbitalFollow.VerticalAxis.Value = newPitch;
2026-04-28 15:46:32 -04:00
if (lockTarget != null)
{
lockTarget.navigationElement.showIndicator = false;
}
2025-12-23 19:47:06 -05:00
this.isLocking = false;
this.isAutoRotate = false;
2026-01-03 18:19:39 -05:00
this.lockTarget = null;
this.targetPoint = null;
viewSc.stateDrivenCamera.GetComponent<Animator>().SetBool("isLockTarget", false);
2025-12-23 19:47:06 -05:00
}
2026-04-28 15:46:32 -04:00
/// <summary>
/// 切换锁定目标
/// </summary>
public void SwitchTarget(float direction)
{
if (!isLocking || isDuringCameraSwitch) return;
if (Time.time - lastTargetSwitchTime < TargetSwitchCooldown) return;
List<CharacterBase> sortedEnemies = BattleManager.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<RectTransform>().DOScale(1f, 0.3f).From(0.5f).SetEase(Ease.OutQuart).Play();
}
2025-12-23 19:47:06 -05:00
}
}