using UnityEngine; using System.Collections; [RequireComponent (typeof(CharacterController))] public class RelativeMovement : MonoBehaviour { [SerializeField] private Transform target; public float moveSpeed = 6.0f; public float rotSpeed = 15.0f; public float jumpSpeed = 15.0f; public float gravity = -9.8f; public float terminalVelocity = -20.0f; public float minFall = -1.5f; public float pushForce = 3.0f; // Amount of force to apply. private float vertSpeed; private ControllerColliderHit contact; private CharacterController charController; private Animator animator; private void Awake() { } // Use this for initialization void Start () { vertSpeed = minFall; charController = GetComponent(); animator = GetComponentInChildren(); } // Update is called once per frame void Update () { // start with zero and add movement components progressively Vector3 movement = Vector3.zero; // x z movement transformed relative to target float horInput = Input.GetAxis("Horizontal"); float vertInput = Input.GetAxis("Vertical"); // Only handle movement if the arrow keys are pressed. if (horInput != 0 || vertInput != 0) { animator.SetBool("Idling", false); // Get the keyboard input for movement and apply movement speed movement.x = horInput * moveSpeed; movement.z = vertInput * moveSpeed; // limit the diagonal movement to the same speed as movement along an axis movement = Vector3.ClampMagnitude(movement, moveSpeed); Quaternion tmp = target.rotation; // read the current rotation of the camera to restore target.eulerAngles = new Vector3(0, target.eulerAngles.y, 0); // apply the rotation around y (turning) movement = target.TransformDirection(movement); target.rotation = tmp; // face movement direction //transform.rotation = Quaternion.LookRotation(movement); // store rotated direction in variable Quaternion direction = Quaternion.LookRotation(movement); // interpolate (transition) the change gradually transform.rotation = Quaternion.Lerp(transform.rotation, direction, rotSpeed * Time.deltaTime); } else { animator.SetBool("Idling", true); } // raycast down to address steep slopes and dropoff edge bool hitGround = false; RaycastHit hit; // Check if the player is falling if (vertSpeed < 0 && Physics.Raycast(transform.position, Vector3.down, out hit)) { // The distance to check against (extend slightly beyond the bottom of the capsule // (The height of the character + radius of capsule) divided by 2 (as ray is being cast from the centre) // Make it 1.9 to check for a little beyond the character to account for inaccuracies in the raycasting float check = (charController.height + charController.radius) / 1.9f; hitGround = hit.distance <= check; // to be sure check slightly beyond bottom of capsule } // y movement: possibly jump impulse up, always accel down // could charController.isGrounded instead, but then cannot workaround dropoff edge if (hitGround) { if (Input.GetButtonDown("Jump")) { vertSpeed = jumpSpeed; } else { // minFall set to negative value to implement some downward force vertSpeed = minFall; } } else { // apply gravity until terminal velocity is reached vertSpeed += gravity * 5 * Time.deltaTime; if (vertSpeed < terminalVelocity) { vertSpeed = terminalVelocity; } // Step 4 if (contact != null) { // not right at level start //animator.SetBool("Jumping", true); } //// workaround for standing on dropoff edge // Raycasting didn't detect the ground, but the capsule is touching the ground if (charController.isGrounded) { // Respond differently depending on direction character is facing // Is character facing the contact point/not? // The collision data contains a normal vector of the surface hit // Dot product returns 1 if they point in exactly the same direction, -1 if they point in completely opposite directions and zero if the vectors are perpendicular. if (Vector3.Dot(movement, contact.normal) < 0) { // replace the movement movement = contact.normal * moveSpeed; } else { // continue the movment - add to previous movement to keep forward momentum movement += contact.normal * moveSpeed; } } } // Step 2 movement.y = vertSpeed; movement *= Time.deltaTime; charController.Move(movement); } // called when the controller hits a collider while performing a Move. // can be used to push objects when they collide with the character. void OnControllerColliderHit(ControllerColliderHit hit) { // Store the collision data in the callback when a collision is detected contact = hit; // Get the rigid body attached to the game object collided with // Check that it is NOT kinematic as kinematic bodies will not be affected by physics (forces, collisions, etc.) Rigidbody body = hit.collider.attachedRigidbody; if (body != null && !body.isKinematic) { // Applies the force to the rigid body, adding an instant velocity change to the body. // do not modify the velocity directly as this can result in unrealistic behaviour //body.velocity = hit.moveDirection * pushForce; body.AddForce((hit.moveDirection * pushForce), ForceMode.VelocityChange); } } }