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;
|
2026-06-30 01:48:58 -04:00
|
|
|
|
using Cielonos.MainGame.Effects.Feedback;
|
2025-12-23 19:47:06 -05:00
|
|
|
|
|
|
|
|
|
|
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;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
private const float CameraSwitchCooldown = 0.5f;
|
2026-04-28 15:46:32 -04:00
|
|
|
|
|
2026-05-26 00:21:27 -04:00
|
|
|
|
public Enemy lockTarget;
|
2026-04-28 15:46:32 -04:00
|
|
|
|
private float lastTargetSwitchTime;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
private const float TargetSwitchCooldown = 0.5f;
|
2025-12-23 19:47:06 -05:00
|
|
|
|
public Transform targetPoint;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
|
|
|
|
|
|
// 用于存储独立的摄像机平滑旋转状态,防止跟随 Transform 的跳变
|
|
|
|
|
|
private float currentYaw;
|
|
|
|
|
|
private float currentPitch;
|
2026-06-30 01:48:58 -04:00
|
|
|
|
private bool wasShakingLastFrame;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
|
2025-12-23 19:47:06 -05:00
|
|
|
|
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-06-27 12:52:03 -04:00
|
|
|
|
if (isUsingLockTargetCamera)
|
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-06-27 12:52:03 -04:00
|
|
|
|
Vector3 actualTargetPos = targetPoint.position;
|
|
|
|
|
|
Vector3 playerPos = viewSc.cameraRoot.position;
|
|
|
|
|
|
var camLockData = viewSc.currentCameraLockData;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 根据距离动态计算怪物权重
|
|
|
|
|
|
float horizontalDistance = Vector2.Distance(new Vector2(playerPos.x, playerPos.z), new Vector2(actualTargetPos.x, actualTargetPos.z));
|
|
|
|
|
|
float closeRangeThreshold = camLockData.weightDistanceRange.x;
|
|
|
|
|
|
float farRangeThreshold = camLockData.weightDistanceRange.y;
|
|
|
|
|
|
float dynamicWeight = camLockData.weightCurve.Evaluate((horizontalDistance - closeRangeThreshold) / (farRangeThreshold - closeRangeThreshold));
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 高度差硬性限制 (解决 Boss 跳跃问题)
|
|
|
|
|
|
float heightDiff = actualTargetPos.y - playerPos.y;
|
|
|
|
|
|
float maxHeightDifference = 2f;
|
|
|
|
|
|
if (heightDiff > maxHeightDifference)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 确保 TargetGroup 中心点的 Y 坐标最高不超过 player.y + maxHeightDifference
|
|
|
|
|
|
float heightWeightLimit = maxHeightDifference / (heightDiff - maxHeightDifference);
|
|
|
|
|
|
dynamicWeight = Mathf.Min(dynamicWeight, heightWeightLimit);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 将动态权重应用到 TargetGroup 中的目标
|
|
|
|
|
|
List<CinemachineTargetGroup.Target> targets = viewSc.targetGroup.Targets;
|
|
|
|
|
|
targets[0].Weight = 1 - dynamicWeight; //玩家中心点权重
|
|
|
|
|
|
for (int i = 1; i < targets.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (targets[i].Object == targetPoint)
|
|
|
|
|
|
{
|
|
|
|
|
|
var t = targets[i];
|
|
|
|
|
|
t.Weight = dynamicWeight;
|
|
|
|
|
|
targets[i] = t;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 重新计算虚拟中心点,用于驱动 OrbitalFollow 的轨道 Pitch
|
|
|
|
|
|
// 注意:Yaw 依旧严格瞄准敌人的真实 XZ 位置,以免水平方向跑偏
|
|
|
|
|
|
Vector3 horizontalDirection = actualTargetPos - playerPos;
|
|
|
|
|
|
float targetYaw = Mathf.Atan2(horizontalDirection.x, horizontalDirection.z) * Mathf.Rad2Deg;
|
|
|
|
|
|
|
|
|
|
|
|
float groupCenterY = (playerPos.y * 1f + actualTargetPos.y * dynamicWeight) / (1f + dynamicWeight);
|
|
|
|
|
|
float virtualYDiff = groupCenterY - playerPos.y;
|
|
|
|
|
|
float targetPitch = Mathf.Atan2(-virtualYDiff, horizontalDistance) * Mathf.Rad2Deg;
|
|
|
|
|
|
|
|
|
|
|
|
// 独立平滑 Yaw (保证在水平面上环绕旋转) 和 Pitch,使用内部状态而非容易受动画影响的 transform
|
|
|
|
|
|
float smoothFactor = 1f - Mathf.Exp(-10f * Time.deltaTime);
|
|
|
|
|
|
currentYaw = Mathf.LerpAngle(currentYaw, targetYaw, smoothFactor);
|
|
|
|
|
|
currentPitch = Mathf.LerpAngle(currentPitch, targetPitch, smoothFactor);
|
|
|
|
|
|
|
|
|
|
|
|
// 限制硬锁定时的最大俯仰角,防止在极近距离或穿模时摄像机垂直朝地/朝天
|
|
|
|
|
|
currentPitch = Mathf.Clamp(currentPitch, -20f, 60f);
|
|
|
|
|
|
|
|
|
|
|
|
viewSc.cameraRoot.rotation = Quaternion.Euler(currentPitch, currentYaw, 0f);
|
|
|
|
|
|
|
|
|
|
|
|
// 【2】接管并强制控制 OrbitalFollow 的轨道位置
|
|
|
|
|
|
var orbitalFollow = viewSc.lockingTargetCamera.GetComponent<CinemachineOrbitalFollow>();
|
2026-06-30 01:48:58 -04:00
|
|
|
|
|
|
|
|
|
|
float horizontalOffset = 0f;
|
|
|
|
|
|
float verticalOffset = 0f;
|
|
|
|
|
|
var orbitShaker = viewSc.lockingTargetCamera.GetComponent<CameraOrbitShaker>();
|
|
|
|
|
|
bool isOrbitActive = orbitShaker != null && orbitShaker.enabled && orbitShaker.HasActiveShakes;
|
|
|
|
|
|
|
|
|
|
|
|
if (isOrbitActive)
|
|
|
|
|
|
{
|
|
|
|
|
|
horizontalOffset = orbitShaker.CurrentHorizontalOffset;
|
|
|
|
|
|
verticalOffset = orbitShaker.CurrentVerticalOffset;
|
|
|
|
|
|
wasShakingLastFrame = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (wasShakingLastFrame)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 刚结束大招回旋的帧:将相机的最终物理轴值写入内部状态,防止数值突变
|
|
|
|
|
|
currentYaw = orbitalFollow.HorizontalAxis.Value;
|
|
|
|
|
|
currentPitch = orbitalFollow.VerticalAxis.Value;
|
|
|
|
|
|
wasShakingLastFrame = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 直接强制摄像机在水平和垂直轨道上移动(包含大招/反馈的 Orbit 回旋偏差量)
|
|
|
|
|
|
orbitalFollow.HorizontalAxis.Value = currentYaw + horizontalOffset;
|
|
|
|
|
|
orbitalFollow.VerticalAxis.Value = currentPitch + verticalOffset;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
|
|
|
|
|
|
float OF_Fade = camLockData.orbitalFollowFadeCurve.Evaluate(Mathf.Clamp01((horizontalDistance - camLockData.orbitalFollowFadeDistanceRange.x) /
|
|
|
|
|
|
(camLockData.orbitalFollowFadeDistanceRange.y - camLockData.orbitalFollowFadeDistanceRange.x)));
|
|
|
|
|
|
orbitalFollow.TargetOffset.y = MathExtensions.Lerp(camLockData.orbitalFollowTargetYRange, OF_Fade);
|
|
|
|
|
|
|
|
|
|
|
|
var rotationComposer = viewSc.lockingTargetCamera.GetComponent<CinemachineRotationComposer>();
|
|
|
|
|
|
float RC_Fade = camLockData.rotationComposerFadeCurve.Evaluate(Mathf.Clamp01((horizontalDistance - camLockData.rotationComposerFadeDistanceRange.x) /
|
|
|
|
|
|
(camLockData.rotationComposerFadeDistanceRange.y - camLockData.rotationComposerFadeDistanceRange.x)));
|
|
|
|
|
|
rotationComposer.TargetOffset.y = MathExtensions.Lerp(camLockData.rotationComposerTargetYRange, RC_Fade);
|
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
|
|
|
|
|
2026-05-26 00:21:27 -04:00
|
|
|
|
Enemy target = CombatManager.EnemySm.GetNearestEnemy(50f);
|
2025-12-23 19:47:06 -05:00
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
2026-06-27 12:52:03 -04:00
|
|
|
|
// 初始化内部旋转状态,防止切入时画面突变
|
|
|
|
|
|
Vector3 camEuler = viewSc.playerCamera.transform.eulerAngles;
|
|
|
|
|
|
currentYaw = camEuler.y;
|
|
|
|
|
|
currentPitch = camEuler.x;
|
|
|
|
|
|
if (currentPitch > 180f) currentPitch -= 360f;
|
|
|
|
|
|
|
2025-12-23 19:47:06 -05:00
|
|
|
|
if (isAutoRotate)
|
|
|
|
|
|
{
|
|
|
|
|
|
targetPoint = target.bodyPartsSc.cameraLockingPoint ?? target.bodyPartsSc.staticCenterPoint;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
// 给怪物赋予基础权重 1,以及 1.5 的包围盒半径,防止 Group size is zero
|
|
|
|
|
|
viewSc.targetGroup.AddMember(targetPoint, 1f, 1.5f);
|
2025-12-23 19:47:06 -05:00
|
|
|
|
viewSc.currentCamera = viewSc.lockingTargetCamera;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
viewSc.lockingTargetCamera.LookAt = viewSc.targetGroup.transform;
|
2025-12-23 19:47:06 -05:00
|
|
|
|
viewSc.stateDrivenCamera.GetComponent<Animator>().SetBool("isLockTarget", true);
|
2026-06-27 12:52:03 -04:00
|
|
|
|
|
|
|
|
|
|
Observable.Timer(TimeSpan.FromSeconds(CameraSwitchCooldown)).First().Subscribe(_ =>
|
|
|
|
|
|
{
|
|
|
|
|
|
isDuringCameraSwitch = false;
|
|
|
|
|
|
});
|
2025-12-23 19:47:06 -05:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-06-27 12:52:03 -04:00
|
|
|
|
Observable.Timer(TimeSpan.FromSeconds(CameraSwitchCooldown)).First().Subscribe(_ =>
|
2025-12-23 19:47:06 -05:00
|
|
|
|
{
|
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-06-27 12:52:03 -04:00
|
|
|
|
viewSc.stateDrivenCamera.GetComponent<Animator>().SetBool("isLockTarget", false);
|
2026-06-30 01:48:58 -04:00
|
|
|
|
viewSc.currentCamera = viewSc.freeLookCamera; // 核心修复:更新当前相机引用为自由相机
|
2026-06-27 12:52:03 -04:00
|
|
|
|
|
|
|
|
|
|
Transform oldTargetPoint = targetPoint;
|
2026-01-03 18:19:39 -05:00
|
|
|
|
this.lockTarget = null;
|
|
|
|
|
|
this.targetPoint = null;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
this.isDuringCameraSwitch = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 延迟移除目标组的成员,直到摄像机混合完成(通常为 0.5s)。
|
|
|
|
|
|
// 否则退出的相机在混合的第一帧就会因为失去目标而瞬间位移,导致画面抖动。
|
|
|
|
|
|
Observable.Timer(TimeSpan.FromSeconds(CameraSwitchCooldown)).First().Subscribe(_ =>
|
|
|
|
|
|
{
|
|
|
|
|
|
if (oldTargetPoint != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
viewSc.targetGroup.RemoveMember(oldTargetPoint);
|
|
|
|
|
|
}
|
|
|
|
|
|
isDuringCameraSwitch = 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;
|
|
|
|
|
|
|
2026-05-26 00:21:27 -04:00
|
|
|
|
List<Enemy> sortedEnemies = CombatManager.EnemySm.GetVisibleEnemiesSortedByScreenX();
|
2026-04-28 15:46:32 -04:00
|
|
|
|
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]);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-26 00:21:27 -04:00
|
|
|
|
private void SetNewTarget(Enemy newTarget)
|
2026-04-28 15:46:32 -04:00
|
|
|
|
{
|
|
|
|
|
|
if (lockTarget != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
lockTarget.navigationElement.showIndicator = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
lockTarget = newTarget;
|
2026-06-27 12:52:03 -04:00
|
|
|
|
Transform oldTargetPoint = targetPoint;
|
2026-04-28 15:46:32 -04:00
|
|
|
|
targetPoint = newTarget.bodyPartsSc.cameraLockingPoint ?? newTarget.bodyPartsSc.staticCenterPoint;
|
|
|
|
|
|
|
|
|
|
|
|
if (isUsingLockTargetCamera)
|
|
|
|
|
|
{
|
2026-06-27 12:52:03 -04:00
|
|
|
|
viewSc.lockingTargetCamera.LookAt = viewSc.targetGroup.transform;
|
|
|
|
|
|
|
|
|
|
|
|
// 同步更新 TargetGroup,替换新旧目标点
|
|
|
|
|
|
if (oldTargetPoint != null && oldTargetPoint != targetPoint)
|
|
|
|
|
|
{
|
|
|
|
|
|
viewSc.targetGroup.RemoveMember(oldTargetPoint);
|
|
|
|
|
|
}
|
|
|
|
|
|
viewSc.targetGroup.AddMember(targetPoint, 1f, 1.5f);
|
2026-04-28 15:46:32 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|