diff --git a/FPSController.cs b/FPSController.cs deleted file mode 100644 index 57c8c7d52e7865a3b96cce210d56d3a790443373..0000000000000000000000000000000000000000 --- a/FPSController.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -// Generic First-Person-Shooter-like Controller -[RequireComponent(typeof(CharacterController))] -public class FPSController : MonoBehaviour -{ - [Tooltip("How fast the player moves when walking (default move speed).")] - [SerializeField] - private float movementWalkSpeed = 6.0f; - - [Tooltip("How fast the player moves when running.")] - [SerializeField] - private float movementRunSpeed = 11.0f; - - [Tooltip("If true, diagonal speed (when strafing + moving forward or back) can't exceed normal move speed; otherwise it's about 1.4 times faster.")] - [SerializeField] - public bool movementLimitDiagonalSpeed = true; - - [Tooltip("If checked, the run key toggles between running and walking. Otherwise player runs if the key is held down.")] - [SerializeField] - private bool movementToggleRun = false; - - [Tooltip("How high the player jumps when hitting the jump button.")] - [SerializeField] - private float movementJumpSpeed = 8.0f; - - [Tooltip("How fast the player falls when not standing on anything.")] - [SerializeField] - private float movementGravity = 20.0f; - - [Tooltip("Units that player can fall before a falling function is run. To disable, type \"infinity\" in the inspector.")] - [SerializeField] - private float movementFallingThreshold = 10.0f; - - [Tooltip("If the player ends up on a slope which is at least the Slope Limit as set on the character controller, then he will slide down.")] - [SerializeField] - private bool movementSlideWhenOverSlopeLimit = false; - - [Tooltip("If checked and the player is on an object tagged \"Slide\", he will slide down it regardless of the slope limit.")] - [SerializeField] - private bool movementSlideOnTaggedObjects = false; - - [Tooltip("How fast the player slides when on slopes as defined above.")] - [SerializeField] - private float movementSlideSpeed = 12.0f; - - [Tooltip("If checked, then the player can change direction while in the air.")] - [SerializeField] - private bool movementAirControl = false; - - [Tooltip("Small amounts of this results in bumping when walking down slopes, but large amounts results in falling too fast.")] - [SerializeField] - private float movementAntiBumpFactor = .75f; - - [Tooltip("Player must be grounded for at least this many physics frames before being able to jump again; set to 0 to allow bunny hopping.")] - [SerializeField] - private int movementAntiBunnyHopFactor = 1; - - private Vector3 movementMoveDirection = Vector3.zero; - private bool movementGrounded = false; - private CharacterController movementController; - private Transform movementTransform; - private float movementSpeed; - private RaycastHit movementHit; - private float movementFallStartLevel; - private bool movementFalling; - private float movementSlideLimit; - private float movementRayDistance; - private Vector3 movementContactPoint; - private bool movementPlayerControl = false; - private int movementJumpTimer; - - - private void Start() - { - // Saving component references to improve performance. - movementTransform = GetComponent<Transform>(); - movementController = GetComponent<CharacterController>(); - - // Setting initial values. - movementSpeed = movementWalkSpeed; - movementRayDistance = movementController.height * .5f + movementController.radius; - movementSlideLimit = movementController.slopeLimit - .1f; - movementJumpTimer = movementAntiBunnyHopFactor; - } - - - private void Update() - { - // If the run button is set to toggle, then switch between walk/run speed. (We use Update for this... - // FixedUpdate is a poor place to use GetButtonDown, since it doesn't necessarily run every frame and can miss the event) - if (movementToggleRun && movementGrounded && Input.GetButtonDown("Run")) - { - movementSpeed = (movementSpeed == movementWalkSpeed ? movementRunSpeed : movementWalkSpeed); - } - } - - - private void FixedUpdate() - { - float inputX = Input.GetAxis("Horizontal"); - float inputY = Input.GetAxis("Vertical"); - - // If both horizontal and vertical are used simultaneously, limit speed (if allowed), so the total doesn't exceed normal move speed - float inputModifyFactor = (inputX != 0.0f && inputY != 0.0f && movementLimitDiagonalSpeed) ? .7071f : 1.0f; - - if (movementGrounded) - { - bool sliding = false; - // See if surface immediately below should be slid down. We use this normally rather than a ControllerColliderHit point, - // because that interferes with step climbing amongst other annoyances - if (Physics.Raycast(movementTransform.position, -Vector3.up, out movementHit, movementRayDistance)) - { - if (Vector3.Angle(movementHit.normal, Vector3.up) > movementSlideLimit) - { - sliding = true; - } - } - // However, just raycasting straight down from the center can fail when on steep slopes - // So if the above raycast didn't catch anything, raycast down from the stored ControllerColliderHit point instead - else - { - Physics.Raycast(movementContactPoint + Vector3.up, -Vector3.up, out movementHit); - if (Vector3.Angle(movementHit.normal, Vector3.up) > movementSlideLimit) - { - sliding = true; - } - } - - // If we were falling, and we fell a vertical distance greater than the threshold, run a falling damage routine - if (movementFalling) - { - movementFalling = false; - if (movementTransform.position.y < movementFallStartLevel - movementFallingThreshold) - { - OnFell(movementFallStartLevel - movementTransform.position.y); - } - } - - // If running isn't on a toggle, then use the appropriate speed depending on whether the run button is down - if (!movementToggleRun) - { - movementSpeed = Input.GetKey(KeyCode.LeftShift) ? movementRunSpeed : movementWalkSpeed; - } - - // If sliding (and it's allowed), or if we're on an object tagged "Slide", get a vector pointing down the slope we're on - if ((sliding && movementSlideWhenOverSlopeLimit) || (movementSlideOnTaggedObjects && movementHit.collider.tag == "Slide")) - { - Vector3 hitNormal = movementHit.normal; - movementMoveDirection = new Vector3(hitNormal.x, -hitNormal.y, hitNormal.z); - Vector3.OrthoNormalize(ref hitNormal, ref movementMoveDirection); - movementMoveDirection *= movementSlideSpeed; - movementPlayerControl = false; - } - // Otherwise recalculate moveDirection directly from axes, adding a bit of -y to avoid bumping down inclines - else - { - movementMoveDirection = new Vector3(inputX * inputModifyFactor, -movementAntiBumpFactor, inputY * inputModifyFactor); - movementMoveDirection = movementTransform.TransformDirection(movementMoveDirection) * movementSpeed; - movementPlayerControl = true; - } - - // Jump! But only if the jump button has been released and player has been grounded for a given number of frames - if (!Input.GetButton("Jump")) - { - movementJumpTimer++; - } - else if (movementJumpTimer >= movementAntiBunnyHopFactor) - { - movementMoveDirection.y = movementJumpSpeed; - movementJumpTimer = 0; - } - } - else - { - // If we stepped over a cliff or something, set the height at which we started falling - if (!movementFalling) - { - movementFalling = true; - movementFallStartLevel = movementTransform.position.y; - } - - // If air control is allowed, check movement but don't touch the y component - if (movementAirControl && movementPlayerControl) - { - movementMoveDirection.x = inputX * movementSpeed * inputModifyFactor; - movementMoveDirection.z = inputY * movementSpeed * inputModifyFactor; - movementMoveDirection = movementTransform.TransformDirection(movementMoveDirection); - } - } - - // Apply gravity - movementMoveDirection.y -= movementGravity * Time.deltaTime; - - // Move the controller, and set grounded true or false depending on whether we're standing on something - movementGrounded = (movementController.Move(movementMoveDirection * Time.deltaTime) & CollisionFlags.Below) != 0; - } - - - // Store point that we're in contact with for use in FixedUpdate if needed - private void OnControllerColliderHit(ControllerColliderHit hit) - { - movementContactPoint = hit.point; - } - - - // This is the place to apply things like fall damage. You can give the player hitpoints and remove some - // of them based on the distance fallen, play sound effects, etc. - private void OnFell(float fallDistance) - { - print("Ouch! Fell " + fallDistance + " units!"); - } -} \ No newline at end of file diff --git a/Movement.cs b/Movement.cs new file mode 100644 index 0000000000000000000000000000000000000000..a9d415976af0d37f73de2495edc7bc03c6214010 --- /dev/null +++ b/Movement.cs @@ -0,0 +1,84 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +[RequireComponent(typeof(CharacterController))] +public class Movement : MonoBehaviour +{ + // The Speed at which the Player can move in units per second + [Tooltip("How fast the character moves when walking (units per second).")] + [SerializeField] float moveSpeed = 4f; + + // Allow Jumping + [Tooltip("Should Jumping be allowed?")] + [SerializeField] bool allowJump = true; + + // The height of a jump, in units + [Tooltip("How high should the character jump (in units).")] + [SerializeField] float jumpHeight = 1.5f; + + // The rate at which our vertical speed will be reduced in units per second + [Tooltip("Gravity in units per second.")] + [SerializeField] float gravity = 30f; + + // The degree to which movement can be controlled midair + [Tooltip("How much maneuverability should remain in the air.")] + [Range(0,10), SerializeField] float airControl = 5; + + // Movement Direction, can be only controlled on the ground + Vector3 moveDirection = Vector3.zero; + + // Reference to the Character Controller (cached) + CharacterController controller; + + // Start is called before the first frame update + void Start() + { + controller = GetComponent<CharacterController>(); + } + + // FixedUpdate must be used if the Player should interact with Physics Objects + void FixedUpdate() + { + // This vector describes the desired plane of movement. If we are in the + // air we will interpolate the current movement with this vector to + // simulate a form of momentum + var input = new Vector3( + Input.GetAxis("Horizontal"), + 0, + Input.GetAxis("Vertical") + ); + + // Multiply the movement with the movement speed + input *= moveSpeed; + + // Convert the rotation to world-space coordinates (as needed by + // the CharacterController Type) + input = transform.TransformDirection(input); + + // Is the Controllers bottom-most point touching the Ground? + if (controller.isGrounded) { + moveDirection = input; + // If jumping is allowed and the Jump Button is pressed + if (allowJump && Input.GetButton("Jump")) { + moveDirection.y = Mathf.Sqrt(2 * gravity * jumpHeight); + }else{ + // Player is on the Ground, cancel Y-Axis Movement + moveDirection.y = 0; + } + }else{ + // We are falling, so Ignore Y-Axis-Input, but allow user to steer + // the fall on the X/Z-Axis + input.y = moveDirection.y; + moveDirection = Vector3.Lerp(moveDirection, input, airControl * Time.deltaTime); + } + // Subtract the gravity from any Y-Axis movement + moveDirection.y -= gravity * Time.deltaTime; + + // Finally: Move the controller into the desired direction. + // Note: if this would hit a collider or the controller would refuse + // to move further – however – if the other collider would move + // into this object it won't budge.. + controller.Move(moveDirection * Time.deltaTime); + } +} diff --git a/README.md b/README.md index e01b4ce97f89619d964ad9a2198bbe116f49f8f6..5b36e0d3383376a21c47cfc4b7e7b56418544667 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ These two Scripts allow you to implement a simple First-Person Control for Unity. -### Script 1: FPSController +### Script 1: Movement This enables the player to change position by walking, running and jumping by pressing the following keys: @@ -10,8 +10,7 @@ This enables the player to change position by walking, running and jumping by pr - <kbd>A</kbd> or <kbd>←</kbd> Moves sidways to the left - <kbd>S</kbd> or <kbd>↓</kbd> Moves backwards - <kbd>D</kbd> or <kbd>→</kbd> Moves sidways to the right -- <kbd>Shift</kbd> "Run" (move faster) -- <kbd>Space</kbd> Jump +- <kbd>Space</kbd> Jump (if enabled) ### Script 2: SmoothMouseLook @@ -27,7 +26,7 @@ This allows users to use the mouse for looking around, with seperate sensitivite Now things should look like in the picture above (highlighted in blue). Next we can add the Scripts. -1. Drag the `FPSController.cs`-script from the Assets onto the `Player`-Empty +1. Drag the `Movement.cs`-script from the Assets onto the `Player`-Empty 2. Drag the `SmoothMouseLook.cs`-script from the Assets onto the `Player`-Empty. ## Tweaking