Advertisement
Guest User

FPS Controller

a guest
Nov 13th, 2019
532
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 15.32 KB | None | 0 0
  1. using System;
  2. using System.Linq;
  3. using UnityEngine;
  4. using Photon.Pun;
  5.  
  6. namespace FPSControllerLPFP
  7. {
  8.     /// Manages a first person character
  9.     [RequireComponent(typeof(Rigidbody))]
  10.     [RequireComponent(typeof(CapsuleCollider))]
  11.     [RequireComponent(typeof(AudioSource))]
  12.     public class FpsControllerLPFP : MonoBehaviourPunCallbacks
  13.     {
  14. #pragma warning disable 649
  15.         [Header("Arms")]
  16.         [Tooltip("The transform component that holds the gun camera."), SerializeField]
  17.         private Transform arms;
  18.  
  19.         [Tooltip("The position of the arms and gun camera relative to the fps controller GameObject."), SerializeField]
  20.         private Vector3 armPosition;
  21.  
  22.         [Header("Audio Clips")]
  23.         [Tooltip("The audio clip that is played while walking."), SerializeField]
  24.         private AudioClip walkingSound;
  25.  
  26.         [Tooltip("The audio clip that is played while running."), SerializeField]
  27.         private AudioClip runningSound;
  28.  
  29.         [Header("Movement Settings")]
  30.         [Tooltip("How fast the player moves while walking and strafing."), SerializeField]
  31.         private float walkingSpeed = 5f;
  32.  
  33.         [Tooltip("How fast the player moves while running."), SerializeField]
  34.         private float runningSpeed = 9f;
  35.  
  36.         [Tooltip("Approximately the amount of time it will take for the player to reach maximum running or walking speed."), SerializeField]
  37.         private float movementSmoothness = 0.125f;
  38.  
  39.         [Tooltip("Amount of force applied to the player when jumping."), SerializeField]
  40.         private float jumpForce = 35f;
  41.  
  42.         [Header("Look Settings")]
  43.         [Tooltip("Rotation speed of the fps controller."), SerializeField]
  44.         private float mouseSensitivity = 7f;
  45.  
  46.         [Tooltip("Approximately the amount of time it will take for the fps controller to reach maximum rotation speed."), SerializeField]
  47.         private float rotationSmoothness = 0.05f;
  48.  
  49.         [Tooltip("Minimum rotation of the arms and camera on the x axis."),
  50.          SerializeField]
  51.         private float minVerticalAngle = -90f;
  52.  
  53.         [Tooltip("Maximum rotation of the arms and camera on the axis."),
  54.          SerializeField]
  55.         private float maxVerticalAngle = 90f;
  56.  
  57.         [Tooltip("The names of the axes and buttons for Unity's Input Manager."), SerializeField]
  58.         private FpsInput input;
  59. #pragma warning restore 649
  60.  
  61.         private Rigidbody _rigidbody;
  62.         private CapsuleCollider _collider;
  63.         private AudioSource _audioSource;
  64.         private SmoothRotation _rotationX;
  65.         private SmoothRotation _rotationY;
  66.         private SmoothVelocity _velocityX;
  67.         private SmoothVelocity _velocityZ;
  68.         private bool _isGrounded;
  69.         public GameObject cameraParent;
  70.         public GameObject gunCamera;
  71.         public GameObject gunArm;
  72.  
  73.         private readonly RaycastHit[] _groundCastResults = new RaycastHit[8];
  74.         private readonly RaycastHit[] _wallCastResults = new RaycastHit[8];
  75.  
  76.         /// Initializes the FpsController on start.
  77.         private void Start()
  78.         {
  79.  
  80.                 cameraParent.SetActive(photonView.IsMine);
  81.                 gunCamera.SetActive(photonView.IsMine);
  82.                 gunArm.GetComponent<HandgunScriptLPFP>().enabled = photonView.IsMine;
  83.                 _rigidbody = GetComponent<Rigidbody>();
  84.                 _rigidbody.constraints = RigidbodyConstraints.FreezeRotation;
  85.                 _collider = GetComponent<CapsuleCollider>();
  86.                 _audioSource = GetComponent<AudioSource>();
  87.                 arms = AssignCharactersCamera();
  88.                 _audioSource.clip = walkingSound;
  89.                 _audioSource.loop = true;
  90.                 _rotationX = new SmoothRotation(RotationXRaw);
  91.                 _rotationY = new SmoothRotation(RotationYRaw);
  92.                 _velocityX = new SmoothVelocity();
  93.                 _velocityZ = new SmoothVelocity();
  94.                 Cursor.lockState = CursorLockMode.Locked;
  95.                 ValidateRotationRestriction();
  96.            
  97.         }
  98.            
  99.         private Transform AssignCharactersCamera()
  100.         {
  101.             var t = transform;
  102.             arms.SetPositionAndRotation(t.position, t.rotation);
  103.             return arms;
  104.         }
  105.        
  106.         /// Clamps <see cref="minVerticalAngle"/> and <see cref="maxVerticalAngle"/> to valid values and
  107.         /// ensures that <see cref="minVerticalAngle"/> is less than <see cref="maxVerticalAngle"/>.
  108.         private void ValidateRotationRestriction()
  109.         {
  110.             minVerticalAngle = ClampRotationRestriction(minVerticalAngle, -90, 90);
  111.             maxVerticalAngle = ClampRotationRestriction(maxVerticalAngle, -90, 90);
  112.             if (maxVerticalAngle >= minVerticalAngle) return;
  113.             Debug.LogWarning("maxVerticalAngle should be greater than minVerticalAngle.");
  114.             var min = minVerticalAngle;
  115.             minVerticalAngle = maxVerticalAngle;
  116.             maxVerticalAngle = min;
  117.         }
  118.  
  119.         private static float ClampRotationRestriction(float rotationRestriction, float min, float max)
  120.         {
  121.             if (rotationRestriction >= min && rotationRestriction <= max) return rotationRestriction;
  122.             var message = string.Format("Rotation restrictions should be between {0} and {1} degrees.", min, max);
  123.             Debug.LogWarning(message);
  124.             return Mathf.Clamp(rotationRestriction, min, max);
  125.         }
  126.            
  127.         /// Checks if the character is on the ground.
  128.         private void OnCollisionStay()
  129.         {
  130.             var bounds = _collider.bounds;
  131.             var extents = bounds.extents;
  132.             var radius = extents.x - 0.01f;
  133.             Physics.SphereCastNonAlloc(bounds.center, radius, Vector3.down,
  134.                 _groundCastResults, extents.y - radius * 0.5f, ~0, QueryTriggerInteraction.Ignore);
  135.             if (!_groundCastResults.Any(hit => hit.collider != null && hit.collider != _collider)) return;
  136.             for (var i = 0; i < _groundCastResults.Length; i++)
  137.             {
  138.                 _groundCastResults[i] = new RaycastHit();
  139.             }
  140.  
  141.             _isGrounded = true;
  142.         }
  143.            
  144.         /// Processes the character movement and the camera rotation every fixed framerate frame.
  145.         private void FixedUpdate()
  146.         {
  147.             if (!photonView.IsMine) return;
  148.  
  149.             // FixedUpdate is used instead of Update because this code is dealing with physics and smoothing.
  150.             RotateCameraAndCharacter();
  151.             MoveCharacter();
  152.             _isGrounded = false;
  153.         }
  154.            
  155.         /// Moves the camera to the character, processes jumping and plays sounds every frame.
  156.         private void Update()
  157.         {
  158.             if (!photonView.IsMine) return;
  159.  
  160.             arms.position = transform.position + transform.TransformVector(armPosition);
  161.             Jump();
  162.             PlayFootstepSounds();
  163.         }
  164.  
  165.         private void RotateCameraAndCharacter()
  166.         {
  167.             var rotationX = _rotationX.Update(RotationXRaw, rotationSmoothness);
  168.             var rotationY = _rotationY.Update(RotationYRaw, rotationSmoothness);
  169.             var clampedY = RestrictVerticalRotation(rotationY);
  170.             _rotationY.Current = clampedY;
  171.             var worldUp = arms.InverseTransformDirection(Vector3.up);
  172.             var rotation = arms.rotation *
  173.                            Quaternion.AngleAxis(rotationX, worldUp) *
  174.                            Quaternion.AngleAxis(clampedY, Vector3.left);
  175.             transform.eulerAngles = new Vector3(0f, rotation.eulerAngles.y, 0f);
  176.             arms.rotation = rotation;
  177.         }
  178.            
  179.         /// Returns the target rotation of the camera around the y axis with no smoothing.
  180.         private float RotationXRaw
  181.         {
  182.             get { return input.RotateX * mouseSensitivity; }
  183.         }
  184.            
  185.         /// Returns the target rotation of the camera around the x axis with no smoothing.
  186.         private float RotationYRaw
  187.         {
  188.             get { return input.RotateY * mouseSensitivity; }
  189.         }
  190.            
  191.         /// Clamps the rotation of the camera around the x axis
  192.         /// between the <see cref="minVerticalAngle"/> and <see cref="maxVerticalAngle"/> values.
  193.         private float RestrictVerticalRotation(float mouseY)
  194.         {
  195.             var currentAngle = NormalizeAngle(arms.eulerAngles.x);
  196.             var minY = minVerticalAngle + currentAngle;
  197.             var maxY = maxVerticalAngle + currentAngle;
  198.             return Mathf.Clamp(mouseY, minY + 0.01f, maxY - 0.01f);
  199.         }
  200.            
  201.         /// Normalize an angle between -180 and 180 degrees.
  202.         /// <param name="angleDegrees">angle to normalize</param>
  203.         /// <returns>normalized angle</returns>
  204.         private static float NormalizeAngle(float angleDegrees)
  205.         {
  206.             while (angleDegrees > 180f)
  207.             {
  208.                 angleDegrees -= 360f;
  209.             }
  210.  
  211.             while (angleDegrees <= -180f)
  212.             {
  213.                 angleDegrees += 360f;
  214.             }
  215.  
  216.             return angleDegrees;
  217.         }
  218.  
  219.         private void MoveCharacter()
  220.         {
  221.             var direction = new Vector3(input.Move, 0f, input.Strafe).normalized;
  222.             var worldDirection = transform.TransformDirection(direction);
  223.             var velocity = worldDirection * (input.Run ? runningSpeed : walkingSpeed);
  224.             //Checks for collisions so that the character does not stuck when jumping against walls.
  225.             var intersectsWall = CheckCollisionsWithWalls(velocity);
  226.             if (intersectsWall)
  227.             {
  228.                 _velocityX.Current = _velocityZ.Current = 0f;
  229.                 return;
  230.             }
  231.  
  232.             var smoothX = _velocityX.Update(velocity.x, movementSmoothness);
  233.             var smoothZ = _velocityZ.Update(velocity.z, movementSmoothness);
  234.             var rigidbodyVelocity = _rigidbody.velocity;
  235.             var force = new Vector3(smoothX - rigidbodyVelocity.x, 0f, smoothZ - rigidbodyVelocity.z);
  236.             _rigidbody.AddForce(force, ForceMode.VelocityChange);
  237.         }
  238.  
  239.         private bool CheckCollisionsWithWalls(Vector3 velocity)
  240.         {
  241.             if (_isGrounded) return false;
  242.             var bounds = _collider.bounds;
  243.             var radius = _collider.radius;
  244.             var halfHeight = _collider.height * 0.5f - radius * 1.0f;
  245.             var point1 = bounds.center;
  246.             point1.y += halfHeight;
  247.             var point2 = bounds.center;
  248.             point2.y -= halfHeight;
  249.             Physics.CapsuleCastNonAlloc(point1, point2, radius, velocity.normalized, _wallCastResults,
  250.                 radius * 0.04f, ~0, QueryTriggerInteraction.Ignore);
  251.             var collides = _wallCastResults.Any(hit => hit.collider != null && hit.collider != _collider);
  252.             if (!collides) return false;
  253.             for (var i = 0; i < _wallCastResults.Length; i++)
  254.             {
  255.                 _wallCastResults[i] = new RaycastHit();
  256.             }
  257.  
  258.             return true;
  259.         }
  260.  
  261.         private void Jump()
  262.         {
  263.             if (!_isGrounded || !input.Jump) return;
  264.             _isGrounded = false;
  265.             _rigidbody.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
  266.         }
  267.  
  268.         private void PlayFootstepSounds()
  269.         {
  270.             if (_isGrounded && _rigidbody.velocity.sqrMagnitude > 0.1f)
  271.             {
  272.                 _audioSource.clip = input.Run ? runningSound : walkingSound;
  273.                 if (!_audioSource.isPlaying)
  274.                 {
  275.                     _audioSource.Play();
  276.                 }
  277.             }
  278.             else
  279.             {
  280.                 if (_audioSource.isPlaying)
  281.                 {
  282.                     _audioSource.Pause();
  283.                 }
  284.             }
  285.         }
  286.            
  287.         /// A helper for assistance with smoothing the camera rotation.
  288.         private class SmoothRotation
  289.         {
  290.             private float _current;
  291.             private float _currentVelocity;
  292.  
  293.             public SmoothRotation(float startAngle)
  294.             {
  295.                 _current = startAngle;
  296.             }
  297.                
  298.             /// Returns the smoothed rotation.
  299.             public float Update(float target, float smoothTime)
  300.             {
  301.                 return _current = Mathf.SmoothDampAngle(_current, target, ref _currentVelocity, smoothTime);
  302.             }
  303.  
  304.             public float Current
  305.             {
  306.                 set { _current = value; }
  307.             }
  308.         }
  309.            
  310.         /// A helper for assistance with smoothing the movement.
  311.         private class SmoothVelocity
  312.         {
  313.             private float _current;
  314.             private float _currentVelocity;
  315.  
  316.             /// Returns the smoothed velocity.
  317.             public float Update(float target, float smoothTime)
  318.             {
  319.                 return _current = Mathf.SmoothDamp(_current, target, ref _currentVelocity, smoothTime);
  320.             }
  321.  
  322.             public float Current
  323.             {
  324.                 set { _current = value; }
  325.             }
  326.         }
  327.            
  328.         /// Input mappings
  329.         [Serializable]
  330.         private class FpsInput
  331.         {
  332.             [Tooltip("The name of the virtual axis mapped to rotate the camera around the y axis."),
  333.              SerializeField]
  334.             private string rotateX = "Mouse X";
  335.  
  336.             [Tooltip("The name of the virtual axis mapped to rotate the camera around the x axis."),
  337.              SerializeField]
  338.             private string rotateY = "Mouse Y";
  339.  
  340.             [Tooltip("The name of the virtual axis mapped to move the character back and forth."),
  341.              SerializeField]
  342.             private string move = "Horizontal";
  343.  
  344.             [Tooltip("The name of the virtual axis mapped to move the character left and right."),
  345.              SerializeField]
  346.             private string strafe = "Vertical";
  347.  
  348.             [Tooltip("The name of the virtual button mapped to run."),
  349.              SerializeField]
  350.             private string run = "Fire3";
  351.  
  352.             [Tooltip("The name of the virtual button mapped to jump."),
  353.              SerializeField]
  354.             private string jump = "Jump";
  355.  
  356.             /// Returns the value of the virtual axis mapped to rotate the camera around the y axis.
  357.             public float RotateX
  358.             {
  359.                 get { return Input.GetAxisRaw(rotateX); }
  360.             }
  361.                          
  362.             /// Returns the value of the virtual axis mapped to rotate the camera around the x axis.        
  363.             public float RotateY
  364.             {
  365.                 get { return Input.GetAxisRaw(rotateY); }
  366.             }
  367.                        
  368.             /// Returns the value of the virtual axis mapped to move the character back and forth.        
  369.             public float Move
  370.             {
  371.                 get { return Input.GetAxisRaw(move); }
  372.             }
  373.                        
  374.             /// Returns the value of the virtual axis mapped to move the character left and right.        
  375.             public float Strafe
  376.             {
  377.                 get { return Input.GetAxisRaw(strafe); }
  378.             }
  379.                    
  380.             /// Returns true while the virtual button mapped to run is held down.          
  381.             public bool Run
  382.             {
  383.                 get { return Input.GetButton(run); }
  384.             }
  385.                      
  386.             /// Returns true during the frame the user pressed down the virtual button mapped to jump.          
  387.             public bool Jump
  388.             {
  389.                 get { return Input.GetButtonDown(jump); }
  390.             }
  391.         }
  392.     }
  393. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement