98 lines
3.5 KiB
C#
98 lines
3.5 KiB
C#
using UnityEngine;
|
|
|
|
/// <summary>
|
|
/// 标准的“点击地面移动角色”组件。
|
|
///
|
|
/// 用法:把本脚本挂到一个带 CharacterController 的对象上(本项目中是一个长方体 Cube)。
|
|
/// 运行时**按住**鼠标左键(拖拽),角色会持续朝鼠标当前指向的地面点移动并平滑转向,
|
|
/// 松开即停止;**按下瞬间**会在点击位置生成一个粒子预制体(可选),2 秒后自动销毁。
|
|
/// 角色仅在地表高度移动(保持 y 不变)。
|
|
/// </summary>
|
|
[RequireComponent(typeof(CharacterController))]
|
|
public class ClickToMove : MonoBehaviour
|
|
{
|
|
[Tooltip("角色移动速度(单位/秒)")]
|
|
[SerializeField] private float speed = 3f;
|
|
|
|
[Tooltip("点击地面时在点击位置动态生成的粒子预制体(需带 ParticleSystem 组件,实例化后 2 秒自动销毁)")]
|
|
[SerializeField] private GameObject clickEffect;
|
|
|
|
private CharacterController _controller;
|
|
private Vector3 _target;
|
|
private bool _moving;
|
|
|
|
// “Ground” 层索引,与 ProjectSettings/TagManager.asset 中的配置一致(第 6 层)。
|
|
private int _groundLayer = 6;
|
|
|
|
private void Start()
|
|
{
|
|
_controller = GetComponent<CharacterController>();
|
|
_target = transform.position;
|
|
|
|
// 优先使用项目里命名的 “Ground” 层;若编辑器尚未刷新层名则回退到默认索引 6。
|
|
int named = LayerMask.NameToLayer("Ground");
|
|
if (named >= 0)
|
|
_groundLayer = named;
|
|
else
|
|
Debug.LogWarning("[ClickToMove] 未找到 \"Ground\" 层,回退使用默认层索引 6。");
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
bool clicked = Input.GetMouseButtonDown(0);
|
|
bool holding = Input.GetMouseButton(0);
|
|
|
|
// 按下瞬间 或 按住拖拽时,都朝当前鼠标指向的地面点更新目标;
|
|
// 仅在“按下瞬间”生成一次点击粒子,避免按住过程中每帧重复生成。
|
|
if (clicked || holding)
|
|
{
|
|
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
|
if (Physics.Raycast(ray, out RaycastHit hit, 100f, 1 << _groundLayer))
|
|
{
|
|
_target = hit.point;
|
|
_moving = true;
|
|
if (clicked)
|
|
SpawnClickEffect(hit.point);
|
|
}
|
|
}
|
|
|
|
// 朝目标点移动并转向
|
|
if (!_moving) return;
|
|
|
|
Vector3 dir = _target - transform.position;
|
|
dir.y = 0f; // 只在地表水平方向移动
|
|
|
|
if (dir.sqrMagnitude > 0.05f)
|
|
{
|
|
_controller.Move(dir.normalized * (speed * Time.deltaTime));
|
|
|
|
Vector3 facing = new Vector3(dir.x, 0f, dir.z);
|
|
if (facing.sqrMagnitude > 0.001f)
|
|
transform.rotation = Quaternion.Slerp(
|
|
transform.rotation,
|
|
Quaternion.LookRotation(facing),
|
|
Time.deltaTime * 12f);
|
|
}
|
|
else
|
|
{
|
|
_moving = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>在点击位置动态生成粒子预制体,2 秒后自动销毁。</summary>
|
|
private void SpawnClickEffect(Vector3 position)
|
|
{
|
|
if (clickEffect == null) return;
|
|
|
|
// 在点击位置实例化预制体
|
|
GameObject effect = Instantiate(clickEffect, position, Quaternion.identity);
|
|
|
|
// 确保粒子播放(若预制体 playOnAwake 未勾选也能看到)
|
|
ParticleSystem ps = effect.GetComponent<ParticleSystem>();
|
|
if (ps != null) ps.Play();
|
|
|
|
// 延迟 2 秒后销毁
|
|
Destroy(effect, 2f);
|
|
}
|
|
}
|