using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace JamTools { // 第一人称角色控制 public class FPSCharacterController : MonoBehaviour { [Flags] public enum CharacterModule { None, LookAround = 1 << 0, // 相机环绕 MoveAround = 1 << 1, // 水平地面移动 MoveInAir = 1 << 2, // 空中移动 Dodge = 1 << 3, // 冲刺 Rush = 1 << 4, // 加速 WalkOnSlope = 1 << 5 | 1 << 1, // 斜面移动 WalkOnStairs = 1 << 6 | 1 << 1, // 楼梯移动 WallRun = 1 << 7, // 飞檐走壁 Jump = 1 << 8, // 跳跃 WallJump = 1 << 9, // 忍者跳 Slide = 1 << 10, // 滑铲 Shot = 1 << 11, // 射击 Step = 1 << 12, // 脚步 PullTrick = 1 << 13, // ExtraGravity = 1 << 14, // 额外重力 Crouch = 1 << 15, } [SerializeField] private CharacterModule m_Modules; [SerializeField] private Transform m_Eye; [SerializeField] private PlayerBody m_Body; [SerializeField] private GroundChecker m_GroundChecker; [SerializeField] private WallChecker m_WallChecker; [SerializeField] private MainCameraFollow m_Camera; #region Modules [Header("Look Around")] [SerializeField] private float m_LookSensitive = 1000f; [Range(0.01f, 1)] [SerializeField] private float m_LookSmooth = 0.2f; private float m_CameraRotation; private float m_BodyRotation; [Header("Move Around")] [SerializeField] private float m_MoveSpeed = 100f; [Range(0.01f, 1)] [SerializeField] private float m_MoveSmooth = 0.2f; private Vector3 m_MoveDirection; private bool m_ReadyMoveAround = false; [Header("Move In Air")] [SerializeField] private float m_MoveSpeedInAir = 50f; [SerializeField] private float m_MoveInAirSmooth = 0.2f; private Vector3 m_MoveInAirDirection; [Header("Jump")] [SerializeField] private float m_JumpPower = 300f; private bool m_ReadyToJump = false; [Header("Shot")] [SerializeField] private Transform m_Muzzle; [SerializeField] private LayerMask m_HittableLayers; [SerializeField] private float m_ShotInfiniteDistance = 100f; [Header("WallJump")] [SerializeField] private float m_WallJumpForce = 1; [SerializeField] private float m_WallJumpPower = 1000; private bool m_ReadyWallJump = false; [Header("WallRun")] [SerializeField] private float m_WallRunSpeed = 1; [Header("ExtraGravity")] [SerializeField] private Vector3 m_ExtraGravity; #endregion public Func checkHit; public Action shootTarget; private Rigidbody m_Rigidbody; private bool m_LockCursor = false; public bool lockCursor { get { return m_LockCursor; } set { m_LockCursor = value; if (value) { Cursor.lockState = CursorLockMode.Locked; } else { Cursor.lockState = CursorLockMode.None; } } } bool IsModuleActive(CharacterModule module) { return (module & m_Modules) != 0; } void ActivateModule(CharacterModule module) { m_Modules |= module; } void LookAround() { if (!IsModuleActive(CharacterModule.LookAround)) return; float mouseX = Input.GetAxis("Mouse X"); float mouseY = Input.GetAxis("Mouse Y"); m_CameraRotation -= mouseY * Time.deltaTime * m_LookSensitive; m_CameraRotation = Mathf.Clamp(m_CameraRotation, -90, 90); Quaternion rot = Quaternion.Euler(m_CameraRotation, 0, 0); m_Eye.localRotation = Quaternion.Slerp(m_Eye.localRotation, rot, m_LookSmooth ); m_BodyRotation += mouseX * Time.deltaTime * m_LookSensitive; rot = Quaternion.Euler(0, m_BodyRotation, 0); transform.localRotation = Quaternion.Slerp(transform.localRotation, rot, m_LookSmooth); } void MoveAroundUpdate() { if (!IsModuleActive(CharacterModule.MoveAround)) return; if (!m_GroundChecker.isOnGround) return; float moveX = Input.GetAxisRaw("Horizontal"); float moveZ = Input.GetAxisRaw("Vertical"); Vector3 right = transform.right; Vector3 forward = transform.forward; m_MoveDirection = right * moveX + forward * moveZ; if (IsModuleActive(CharacterModule.WalkOnSlope)) { if (m_GroundChecker.isOnGround) { RaycastHit hitInfo; if (Physics.Raycast(m_GroundChecker.foot.position, Vector3.down, out hitInfo)) { Vector3 normal = hitInfo.normal; m_MoveDirection = Vector3.ProjectOnPlane(m_MoveDirection, normal); GizmosHandle.Instance.DoGizmos(() => { Gizmos.DrawLine(hitInfo.point + new Vector3(0, 0.1f, 0), hitInfo.point + m_MoveDirection); }); } } } m_MoveDirection = m_MoveDirection.normalized; } void MoveAroundFixedUpdate() { if (!IsModuleActive(CharacterModule.MoveAround)) return; if (!m_GroundChecker.isOnGround) return; float vy = m_Rigidbody.velocity.y; Vector3 velocity = new Vector3(m_MoveDirection.x * Time.deltaTime * m_MoveSpeed, vy, m_MoveDirection.z * Time.deltaTime * m_MoveSpeed); m_Rigidbody.velocity = Vector3.Lerp(m_Rigidbody.velocity, velocity, m_MoveSmooth); } void MoveInAirUpdate() { if (!IsModuleActive(CharacterModule.MoveInAir)) return; if (m_GroundChecker.isOnGround) return; float moveX = Input.GetAxisRaw("Horizontal"); float moveZ = Input.GetAxisRaw("Vertical"); m_MoveInAirDirection = Vector3.ClampMagnitude(transform.right * moveX + transform.forward * moveZ, 1); } void MoveInAirFixedUpdate() { if (!IsModuleActive(CharacterModule.MoveInAir)) return; if (m_GroundChecker.isOnGround) return; if (m_MoveInAirDirection.magnitude == 0f) return; float vy = m_Rigidbody.velocity.y; Vector3 velocity = new Vector3(m_MoveInAirDirection.x * Time.deltaTime * m_MoveSpeedInAir, vy, m_MoveInAirDirection.z * Time.deltaTime * m_MoveSpeedInAir); m_Rigidbody.velocity = Vector3.Lerp(m_Rigidbody.velocity, velocity, m_MoveInAirSmooth); } void Jump() { if (!IsModuleActive(CharacterModule.Jump)) return; if (!m_GroundChecker.isOnGround) return; if (Input.GetButtonDown("Jump")) { m_ReadyToJump = true; } } void JumpFixedUpdate() { if(m_ReadyToJump) { m_ReadyToJump = false; m_Rigidbody.AddForce(Vector3.up * m_JumpPower, ForceMode.Acceleration); } } void Dodge() { if (!IsModuleActive(CharacterModule.Dodge)) return; if (Input.GetKeyDown(KeyCode.LeftShift)) { } } void DodgeFixed() { } void Shot() { if (!IsModuleActive(CharacterModule.Shot)) return; if (Input.GetButtonDown("Fire1")) { Vector3 hitPoint = GetHitPoint(); if (shootTarget != null) shootTarget(hitPoint, m_Muzzle); } } Vector3 GetHitPoint() { RaycastHit[] hits = Physics.RaycastAll(m_Eye.position, m_Eye.forward, m_HittableLayers, (int)QueryTriggerInteraction.Ignore); if (hits.Length < 1) { return m_Eye.position + m_Eye.forward * m_ShotInfiniteDistance; } else { for (int i = 0; i < hits.Length; ++i) { if (checkHit != null && checkHit(hits[i])) { return hits[i].point; } } } return hits[0].point; } void WallJump() { if (!IsModuleActive(CharacterModule.WallJump)) return; if (!m_WallChecker.IsOnWall) return; if (m_GroundChecker.isOnGround) return; if (Input.GetButtonDown("Jump")) { m_ReadyWallJump = true; } } void WallJumpFixedUpdate() { if(m_ReadyWallJump) { m_ReadyWallJump = false; Vector3 poc; if (m_WallChecker.GetCollisionPoint(out poc)) { Vector3 dir = Vector3.ClampMagnitude(transform.position - poc, 1f); Vector3 wallJumpDirection = new Vector3(dir.x, 1, dir.z); m_Rigidbody.velocity = Vector3.zero; m_Rigidbody.AddForce(wallJumpDirection * m_WallJumpPower); } } } void WallRun() { if (!IsModuleActive(CharacterModule.WallRun)) return; if (!m_WallChecker.IsOnWall) return; } void PullTrick() { } void ExtraGravity() { if (!IsModuleActive(CharacterModule.ExtraGravity)) return; m_Rigidbody.AddForce(m_ExtraGravity, ForceMode.Acceleration); } void SetCamera () { m_Camera.SetCameraPositionAndRotation(m_Eye); } private void Awake() { m_Rigidbody = GetComponent(); } private void Start() { m_CameraRotation = 0; lockCursor = true; } private void Update() { LookAround(); MoveAroundUpdate(); MoveInAirUpdate(); Jump(); Dodge(); Shot(); WallJump(); PullTrick(); SetCamera(); } private void FixedUpdate() { MoveAroundFixedUpdate(); MoveInAirFixedUpdate(); DodgeFixed(); ExtraGravity(); JumpFixedUpdate(); WallJumpFixedUpdate(); } private void OnDrawGizmos() { } } }