自动锁定系统
自动锁定系统文档
概述
百度 习近平引用的这句古语,化用了中国古代“慎微”和“节欲”两种思想,意在告诫人们不要被蝇头小利诱惑,因此失去操守,坏了大事,忘了大义。允许玩家通过鼠标中键快速锁定和切换敌人目标,实现精确的第三人称战斗体验。
参数配置
自动锁定参数
[Header("自动锁定参数")]
public float lockDetectionRadius = 30f; // 锁定检测半径
public LayerMask enemyLayerMask = -1; // 敌人层级遮罩
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
lockDetectionRadius |
float | 30f | 检测敌人目标的最大半径(米) |
enemyLayerMask |
LayerMask | -1 | 指定检测的层级,-1表示所有层级 |
锁定转向参数
[Header("锁定转向参数")]
public float lockRotationSpeed = 10f; // 锁定时的旋转速度
public float minLockDistance = 1f; // 最小锁定距离
public float maxLockDistance = 20f; // 最大锁定距离
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
lockRotationSpeed |
float | 10f | 锁定模式下角色转向目标的速度 |
minLockDistance |
float | 1f | 最小锁定距离,防止锁定过近目标 |
maxLockDistance |
float | 20f | 最大锁定距离,超出范围自动取消锁定 |
目标要求
- 命名规范: 目标对象名称必须包含"Enemy"(不区分大小写)
- 距离限制: 目标必须在
minLockDistance
和maxLockDistance
之间 - 后面记得改为敌人设置专门的层级以提高性能,用名字区分太蠢了
思路
1. 目标检测
开始检测 → 获取检测半径内的碰撞体 → 筛选包含"Enemy"的对象 → 验证距离范围 → 按距离排序 → 更新可用目标列表
2. 锁定切流程
按中键 → 检查当前状态 → 开始锁定/切换目标/退出锁定 → 更新锁定状态 → 验证目标有效性
3. 实时验证
每帧检查 → 验证目标存在性 → 验证距离有效性 → 无效时自动退出锁定
性能优化以及后续更新
1. 层级设置
// 为敌人设置专门的层级
enemyLayerMask = LayerMask.GetMask("Enemy");
2. 检测半径优化
- 根据游戏场景大小调整
lockDetectionRadius
- 避免设置过大的检测半径影响性能
3. 目标命名规范
- 统一使用包含"Enemy"的命名规范
- 避免在非敌人对象中使用"Enemy"关键词
4. 添加锁定UI指示器
// 在锁定目标时显示UI指示器
public void ShowLockIndicator(Transform target)
{
// 显示锁定UI逻辑
}
效果
后面记得录GIF
完整的第三人称移动代码:
using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections.Generic;
public class ThirdPersonMove : MonoBehaviour
{
//锁定目标
public Transform LockTarget;
[Header("输入参数")]
public float dashSpeed = 1.5f;
public bool inputEnabled = true;
// 混合树参数
[Header("混合树参数")]
public float blendTreeTransitionTime = 0.2f; // 混合树切换时间
// 状态机控制参数
[Header("状态机控制参数")]
public string moveStateParameter = "IsLockMode"; // 控制移动状态混合树的参数名
// 锁定转向参数
[Header("锁定转向参数")]
public float lockRotationSpeed = 10f; // 锁定时的旋转速度
public float minLockDistance = 1f; // 最小锁定距离
public float maxLockDistance = 20f; // 最大锁定距离
// 自动锁定参数
[Header("自动锁定参数")]
public float lockDetectionRadius = 30f; // 锁定检测半径
public LayerMask enemyLayerMask = -1; // 敌人层级遮罩
GameObject mainCamera;
Animator animator;
CharacterController characterController;
// 状态机相关变量
private bool isLockMode = false;
// 自动锁定相关变量
private List<Transform> availableTargets = new List<Transform>();
private int currentTargetIndex = -1;
private bool isLocking = false;
void Start()
{
//获取当前场景的主相机
if (mainCamera == null)
{
mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
}
}
void Awake()
{
animator = GetComponent<Animator>();
characterController = GetComponent<CharacterController>();
//速度控制
animator.speed /= animator.humanScale;
}
// 目标旋转角度
float targetRotation = 0.0f;
// 平滑旋转时间
public float RotationSmoothTime = 0.1f;
// 平滑旋转速度
float rotationVelocity;
void Update()
{
if (!inputEnabled) return;
// 检测鼠标中键输入
if (Input.GetMouseButtonDown(2)) // 鼠标中键
{
ToggleAutoLock();
}
// 验证当前锁定目标是否仍然有效
if (isLocking)
{
ValidateCurrentTarget();
}
if (Input.GetKey(KeyCode.LeftShift))
{
//animator.speed = moveSpeed * 1.5f / animator.humanScale;
//只改变走路的速度,在混合树上乘这个SprintSpeed参数
animator.SetFloat("SprintSpeed", dashSpeed / animator.humanScale);
}
else
{
animator.SetFloat("SprintSpeed", 1 / animator.humanScale);
}
// 检查是否进入或退出锁定模式
bool shouldBeLockMode = LockTarget != null;
if (shouldBeLockMode != isLockMode)
{
isLockMode = shouldBeLockMode;
}
// 更新混合树权重
UpdateBlendTreeWeight();
#region 水平方向
if (LockTarget == null)
FreeMove();
else
LockMove();
#endregion
}
/// <summary>
/// 更新混合树权重
/// </summary>
private void UpdateBlendTreeWeight()
{
// 状态机参数控制
animator.SetBool(moveStateParameter, isLockMode);
}
/// <summary>
/// 新版Input输入系统————移动
/// </summary>
Vector2 moveAmount;
void OnMove(InputValue value)
{
moveAmount = value.Get<Vector2>();
}
/// <summary>
/// 自由移动,方向
/// </summary>
private void FreeMove()
{
if (moveAmount != Vector2.zero)
{
// 计算水平方向输入方向,将其转换为世界空间坐标系——x和z是水平坐标轴
Vector3 inputDir = new Vector3(moveAmount.x, 0.0f, moveAmount.y).normalized;
// 计算目标朝向
targetRotation = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg +
mainCamera.transform.eulerAngles.y;
// 平稳地旋转玩家,使其面向他们移动的方向。
float rotation = Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref rotationVelocity, RotationSmoothTime);
// 旋转玩家
transform.rotation = Quaternion.Euler(0.0f, rotation, 0.0f);
}
//Y轴输入值——只影响前进的速度控制,朝向由上面的if中的语句决定
var axisY = animator.GetFloat("AxisY");
//平滑过渡到Y轴目标值
axisY = Mathf.MoveTowards(axisY, moveAmount.magnitude, Time.deltaTime * 5f);
animator.SetFloat("AxisY", axisY);
}
/// <summary>
/// 锁定目标时的移动——八向移动
/// </summary>
private void LockMove()
{
if (LockTarget == null) return;
//角色位置到目标的向量
var dir = LockTarget.position - transform.position;
float distance = dir.magnitude;
// 检查距离是否在有效范围内
if (distance < minLockDistance || distance > maxLockDistance)
{
// 距离太近或太远时,不进行锁定转向
return;
}
//去除y轴的影响
dir.y = 0;
// 确保方向向量不为零
if (dir.magnitude > 0.1f)
{
//目标朝向
var targetRotation = Quaternion.LookRotation(dir);
//从 当前朝向到目标朝向 平滑旋转
// 使用lockRotationSpeed作为旋转速度
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, lockRotationSpeed * Time.deltaTime);
}
// 锁定移动混合树参数
var AxisX = animator.GetFloat("AxisX");
var AxisY = animator.GetFloat("AxisY");
//平滑改变轴向
AxisX = Mathf.MoveTowards(AxisX, moveAmount.x, Time.deltaTime * 5f);
AxisY = Mathf.MoveTowards(AxisY, moveAmount.y, Time.deltaTime * 5f);
//更新锁定移动动画参数
animator.SetFloat("AxisX", AxisX);
animator.SetFloat("AxisY", AxisY);
}
/// <summary>
/// 切换自动锁定状态
/// </summary>
private void ToggleAutoLock()
{
if (!isLocking)
{
// 开始锁定模式
FindAvailableTargets();
if (availableTargets.Count > 0)
{
currentTargetIndex = 0;
LockTarget = availableTargets[currentTargetIndex];
isLocking = true;
Debug.Log($"锁定目标: {LockTarget.name}");
}
}
else
{
// 切换到下一个目标
if (availableTargets.Count > 1)
{
currentTargetIndex = (currentTargetIndex + 1) % availableTargets.Count;
LockTarget = availableTargets[currentTargetIndex];
Debug.Log($"切换到目标: {LockTarget.name}");
}
else
{
// 只有一个目标或没有目标,退出锁定模式
ExitLockMode();
}
}
}
/// <summary>
/// 查找可用的锁定目标
/// </summary>
private void FindAvailableTargets()
{
availableTargets.Clear();
// 获取检测半径内的所有碰撞体
Collider[] colliders = Physics.OverlapSphere(transform.position, lockDetectionRadius, enemyLayerMask);
foreach (Collider col in colliders)
{
// 检查对象名称是否包含"Enemy"
if (col.name.ToLower().Contains("enemy"))
{
// 检查距离是否在有效范围内
float distance = Vector3.Distance(transform.position, col.transform.position);
if (distance >= minLockDistance && distance <= maxLockDistance)
{
availableTargets.Add(col.transform);
}
}
}
// 按距离排序,最近的优先
availableTargets.Sort((a, b) =>
Vector3.Distance(transform.position, a.position).CompareTo(
Vector3.Distance(transform.position, b.position)));
}
/// <summary>
/// 退出锁定模式
/// </summary>
private void ExitLockMode()
{
LockTarget = null;
isLocking = false;
currentTargetIndex = -1;
availableTargets.Clear();
Debug.Log("退出锁定模式");
}
/// <summary>
/// 检查当前锁定目标是否仍然有效
/// </summary>
private void ValidateCurrentTarget()
{
if (LockTarget == null)
{
ExitLockMode();
return;
}
// 检查目标是否仍然存在
if (LockTarget.gameObject == null)
{
ExitLockMode();
return;
}
// 检查距离是否仍然有效
float distance = Vector3.Distance(transform.position, LockTarget.position);
if (distance < minLockDistance || distance > maxLockDistance)
{
ExitLockMode();
return;
}
}
/// <summary>
/// 手动设置锁定目标
/// </summary>
/// <param name="target">要锁定的目标</param>
public void SetLockTarget(Transform target)
{
if (target != null)
{
LockTarget = target;
isLocking = true;
currentTargetIndex = -1; // 手动设置时不使用索引
Debug.Log($"手动锁定目标: {target.name}");
}
}
/// <summary>
/// 手动清除锁定目标
/// </summary>
public void ClearLockTarget()
{
ExitLockMode();
}
/// <summary>
/// 获取当前是否处于锁定模式
/// </summary>
/// <returns>是否锁定中</returns>
public bool IsLocking()
{
return isLocking;
}
/// <summary>
/// 获取当前可用目标数量
/// </summary>
/// <returns>可用目标数量</returns>
public int GetAvailableTargetCount()
{
return availableTargets.Count;
}
}