Files
Cielonos/Assets/Scripts/MainGame/Characters/Player/View/LockTargetSubmodule.cs
SoulliesOfficial 0902ca8a9e Bezi回来了
2026-04-28 15:46:32 -04:00

222 lines
7.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<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;
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 = BattleManager.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<Animator>().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<RectTransform>().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<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;
if (lockTarget != null)
{
lockTarget.navigationElement.showIndicator = false;
}
this.isLocking = false;
this.isAutoRotate = false;
this.lockTarget = null;
this.targetPoint = null;
viewSc.stateDrivenCamera.GetComponent<Animator>().SetBool("isLockTarget", false);
}
/// <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();
}
}
}