194 lines
6.6 KiB
C#
194 lines
6.6 KiB
C#
using System;
|
||
using Core;
|
||
using Script.Gameplay.Input;
|
||
using UnityEngine;
|
||
|
||
namespace Script.Gameplay.Player
|
||
{
|
||
public class PlayerMoveController : MonoBehaviour
|
||
{
|
||
[Header("Movement Settings")] [Tooltip("移动速度(米/秒)")]
|
||
public float speed = 5f;
|
||
|
||
[Tooltip("跳跃高度(米)")] public float jumpHeight = 2f;
|
||
[Tooltip("重力加速度(米/秒²)")] public float gravity = -9.81f;
|
||
|
||
[Header("Ground Check")] [Tooltip("检测地面的位置(通常为角色脚下)")]
|
||
public Transform groundCheck;
|
||
|
||
[Tooltip("地面检测半径")] public float groundDistance = 0.4f;
|
||
[Tooltip("地面层(LayerMask)")] public LayerMask groundMask;
|
||
|
||
[Header("Sound Effects")]
|
||
[SerializeField] private AudioSource playerSound;
|
||
[SerializeField] private AudioClip jumpSound;
|
||
[SerializeField] private AudioClip walkSound;
|
||
private float initSpeed;
|
||
|
||
private CharacterController characterController;
|
||
private Vector3 velocity;
|
||
private bool isGrounded;
|
||
private InputManager inputManager;
|
||
|
||
private float lastGroundY;
|
||
private bool wasGroundedLastFrame;
|
||
|
||
[Tooltip("从高处落下时的伤害阈值(米)")] [SerializeField]
|
||
private float fallDamageThreshold = 3f; //伤害阈值
|
||
|
||
[Tooltip("推动物体的力量")] [SerializeField]
|
||
private float pushPower = 2f;
|
||
|
||
[Header("Footstep Settings")]
|
||
[Tooltip("判定为走路的最小输入强度")] [SerializeField]
|
||
private float walkThreshold = 0.1f;
|
||
|
||
private void Awake()
|
||
{
|
||
characterController = GetComponent<CharacterController>();
|
||
if (characterController == null)
|
||
Debug.LogError("PlayerMoveController 需要 CharacterController 组件!");
|
||
|
||
lastGroundY = transform.position.y;
|
||
initSpeed = speed;
|
||
playerSound = playerSound != null ? playerSound : GetComponent<AudioSource>();
|
||
if (playerSound == null)
|
||
Debug.LogWarning("PlayerMoveController: AudioSource 未设置,音效将不会播放。");
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
inputManager = InputManager.Instance;
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// 地面检测
|
||
bool isCurrentlyGrounded = groundCheck != null
|
||
? Physics.CheckSphere(groundCheck.position, groundDistance, groundMask)
|
||
: characterController.isGrounded;
|
||
|
||
// 记录离开地面时的高度
|
||
if (wasGroundedLastFrame && !isCurrentlyGrounded)
|
||
{
|
||
lastGroundY = transform.position.y;
|
||
}
|
||
|
||
// 落地检测
|
||
if (!wasGroundedLastFrame && isCurrentlyGrounded)
|
||
{
|
||
|
||
float fallHeight = lastGroundY - transform.position.y;
|
||
if (fallHeight > fallDamageThreshold) // 3米为伤害阈值,可调整
|
||
{
|
||
int damage = Mathf.RoundToInt((fallHeight - 3f) * 10); // 每超出1米伤害10点
|
||
var playerController = GetComponent<PlayerController>();
|
||
if (playerController != null)
|
||
playerController.TakeDamage(damage);
|
||
}
|
||
}
|
||
|
||
wasGroundedLastFrame = isCurrentlyGrounded;
|
||
|
||
// 地面检测
|
||
if (groundCheck != null)
|
||
isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask);
|
||
else
|
||
isGrounded = characterController.isGrounded;
|
||
|
||
if (isGrounded && velocity.y < 0)
|
||
velocity.y = -2f; // 保持贴地
|
||
|
||
Vector2 inputMove = inputManager.Move;
|
||
// 获取输入
|
||
float x = inputMove.x;
|
||
float z = inputMove.y;
|
||
|
||
// 按玩家朝向移动
|
||
Vector3 move = transform.right * x + transform.forward * z;
|
||
characterController.Move(move * speed * Time.deltaTime);
|
||
|
||
// 走路音效逻辑:在地面且移动输入较大时播放循环走路音效
|
||
HandleFootstepAudio(x, z);
|
||
|
||
// 跳跃
|
||
if (isGrounded && inputManager.JumpPressed)
|
||
{
|
||
// 跳跃时停止走路循环声并播放跳跃音效(一次性)
|
||
StopWalkAudioIfPlaying();
|
||
if (playerSound != null && jumpSound != null)
|
||
playerSound.PlayOneShot(jumpSound);
|
||
|
||
velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||
}
|
||
|
||
// 重力
|
||
velocity.y += gravity * Time.deltaTime;
|
||
characterController.Move(velocity * Time.deltaTime);
|
||
}
|
||
|
||
private void HandleFootstepAudio(float x, float z)
|
||
{
|
||
if (playerSound == null || walkSound == null) return;
|
||
|
||
float inputMag = new Vector2(x, z).magnitude;
|
||
bool shouldPlayWalk = isGrounded && inputMag > walkThreshold;
|
||
|
||
if (shouldPlayWalk)
|
||
{
|
||
if (playerSound.clip != walkSound || !playerSound.isPlaying)
|
||
{
|
||
playerSound.clip = walkSound;
|
||
playerSound.loop = true;
|
||
playerSound.Play();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果当前正在播放走路音效则停止
|
||
if (playerSound.clip == walkSound && playerSound.isPlaying)
|
||
{
|
||
playerSound.Stop();
|
||
playerSound.loop = false;
|
||
playerSound.clip = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void StopWalkAudioIfPlaying()
|
||
{
|
||
if (playerSound == null) return;
|
||
if (playerSound.clip == walkSound && playerSound.isPlaying)
|
||
{
|
||
playerSound.Stop();
|
||
playerSound.loop = false;
|
||
playerSound.clip = null;
|
||
}
|
||
}
|
||
|
||
private void OnControllerColliderHit(ControllerColliderHit hit)
|
||
{
|
||
Rigidbody body = hit.collider.attachedRigidbody;
|
||
// 只推动带有刚体且非静态的物体
|
||
if (body != null && !body.isKinematic)
|
||
{
|
||
// 忽略从下方碰撞
|
||
if (hit.moveDirection.y < -0.3f) return;
|
||
|
||
Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);
|
||
body.AddForce(pushDir * pushPower, ForceMode.Impulse);
|
||
}
|
||
}
|
||
|
||
public void SetSpeed(float newSpeed)
|
||
{
|
||
speed = newSpeed;
|
||
}
|
||
|
||
public void ResetSpeed()
|
||
{
|
||
speed = initSpeed;
|
||
}
|
||
}
|
||
}
|