using UnityEngine; /// /// 标准的“点击地面移动角色”组件。 /// /// 用法:把本脚本挂到一个带 CharacterController 的对象上(本项目中是一个长方体 Cube)。 /// 运行时**按住**鼠标左键(拖拽),角色会持续朝鼠标当前指向的地面点移动并平滑转向, /// 松开即停止;**按下瞬间**会在点击位置生成一个粒子预制体(可选),2 秒后自动销毁。 /// 角色仅在地表高度移动(保持 y 不变)。 /// [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(); _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; } } /// 在点击位置动态生成粒子预制体,2 秒后自动销毁。 private void SpawnClickEffect(Vector3 position) { if (clickEffect == null) return; // 在点击位置实例化预制体 GameObject effect = Instantiate(clickEffect, position, Quaternion.identity); // 确保粒子播放(若预制体 playOnAwake 未勾选也能看到) ParticleSystem ps = effect.GetComponent(); if (ps != null) ps.Play(); // 延迟 2 秒后销毁 Destroy(effect, 2f); } }