Skip to content

Instantly share code, notes, and snippets.

@CoolOppo
Created November 11, 2018 08:27
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CoolOppo/d33f85d20a5d75ba958b45228b399bba to your computer and use it in GitHub Desktop.
Save CoolOppo/d33f85d20a5d75ba958b45228b399bba to your computer and use it in GitHub Desktop.
// A port of Source Engine / Quake Movement to Unity
// Made by CoolOppo
using System;
using UnityEditor.ShaderGraph;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
using UnityStandardAssets.Utility;
using Random = UnityEngine.Random;
namespace UnityStandardAssets.Characters.FirstPerson
{
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(AudioSource))]
public class FirstPersonController : MonoBehaviour
{
[SerializeField] float m_Friction = 4.0f;
[SerializeField] float m_MaxAccel = 10f;
[SerializeField] float m_MaxAirAccel = 300f;
[SerializeField] private bool m_IsWalking;
[SerializeField] private float m_WalkSpeed;
[SerializeField] private float m_RunSpeed;
[SerializeField] [Range(0f, 1f)] private float m_RunstepLenghten;
[SerializeField] private float m_JumpSpeed;
[SerializeField] private float m_StickToGroundForce;
[SerializeField] private float m_GravityMultiplier = 1.16147f;
[SerializeField] private MouseLook m_MouseLook;
[SerializeField] private bool m_UseFovKick;
[SerializeField] private FOVKick m_FovKick = new FOVKick();
[SerializeField] private bool m_UseHeadBob;
[SerializeField] private CurveControlledBob m_HeadBob = new CurveControlledBob();
[SerializeField] private LerpControlledBob m_JumpBob = new LerpControlledBob();
[SerializeField] private float m_StepInterval;
[SerializeField] private AudioClip[] m_FootstepSounds; // an array of footstep sounds that will be randomly selected from.
[SerializeField] private AudioClip m_JumpSound; // the sound played when character leaves the ground.
[SerializeField] private AudioClip m_LandSound; // the sound played when character touches back on ground.
private Camera m_Camera;
private bool m_Jump;
private float m_YRotation;
private Vector2 m_Input;
private Vector3 m_MoveDir = Vector3.zero;
private CharacterController m_CharacterController;
private CollisionFlags m_CollisionFlags;
private bool m_PreviouslyGrounded;
private Vector3 m_OriginalCameraPosition;
private float m_StepCycle;
private float m_NextStep;
private bool m_Jumping;
bool m_HasJumped = false; // False if on ground, true if not on ground and has jumped
private AudioSource m_AudioSource;
float m_MaxSpeed;
// Use this for initialization
private void Start()
{
m_CharacterController = GetComponent<CharacterController>();
m_Camera = Camera.main;
m_OriginalCameraPosition = m_Camera.transform.localPosition;
m_FovKick.Setup(m_Camera);
m_HeadBob.Setup(m_Camera, m_StepInterval);
m_StepCycle = 0f;
m_NextStep = m_StepCycle / 2f;
m_Jumping = false;
m_AudioSource = GetComponent<AudioSource>();
m_MouseLook.Init(transform, m_Camera.transform);
}
// Update is called once per frame
private void Update()
{
RotateView();
// the jump state needs to read here to make sure it is not missed
if (!m_Jump)
{
m_Jump = CrossPlatformInputManager.GetButtonDown("Jump");
}
}
private void PlayLandingSound()
{
m_AudioSource.clip = m_LandSound;
m_AudioSource.Play();
m_NextStep = m_StepCycle + .5f;
}
private void FixedUpdate()
{
Vector3 velocity = m_CharacterController.velocity;
GetInput(out m_MaxSpeed);
float speed = new Vector2(velocity.x, velocity.z).magnitude;
// Apply half-gravity (leapfrog integration; we'll add the rest below.):
velocity.y -= (15.24f * 0.5f * Time.fixedDeltaTime);
// Translate input vector to world-space based on camera:
Vector3 accelDir = transform.forward * m_Input.y + transform.right * m_Input.x;
accelDir = new Vector3(accelDir.x, 0.0f, accelDir.z).normalized;
// Calculate friction:
Friction(speed, ref velocity);
// Calculate acceleration:
var projectedSpeed = Vector3.Dot(velocity, accelDir);
bool onGround = m_CharacterController.isGrounded;
var addSpeed = (onGround ? m_MaxSpeed : (3.0 / 40.0) * m_MaxSpeed) - projectedSpeed;
if (addSpeed < 0)
{
addSpeed = 0;
}
var accelSpeed =
Mathf.Clamp(
(onGround ? m_MaxAccel : m_MaxAirAccel) * m_MaxSpeed * Time.fixedDeltaTime,
0.0f, (float)addSpeed);
// Apply acceleration to velocity:
velocity += accelDir * accelSpeed;
// Handle jumping
if (onGround && m_Jump && !m_HasJumped)
{
velocity.y += 5.1111f;
m_HasJumped = true;
}
// Apply other half of gravity:
velocity.y -= (15.24f * 0.5f * Time.fixedDeltaTime);
// Move based on velocity:
m_CharacterController.SimpleMove(velocity);
// Reset hasJumped if we haven't jumped:
if (onGround)
{
m_HasJumped = false;
}
ProgressStepCycle(m_MaxSpeed);
UpdateCameraPosition(m_MaxSpeed);
m_MouseLook.UpdateCursorLock();
}
void Friction(float speed, ref Vector3 velocity)
{
var drop = 0.0f;
if (speed < 0.00191)
return;
if (m_CharacterController.isGrounded && !m_Jump)
{
double stopSpeed = (5.0 / 16.0) * m_MaxSpeed;
double control = (speed < stopSpeed) ? stopSpeed : speed;
drop = (float)(control * m_Friction * Time.fixedDeltaTime);
}
float newSpeed = speed - drop;
if (newSpeed < 0)
newSpeed = 0;
newSpeed /= speed;
velocity *= newSpeed;
}
private void PlayJumpSound()
{
m_AudioSource.clip = m_JumpSound;
m_AudioSource.Play();
}
private void ProgressStepCycle(float speed)
{
if (m_CharacterController.velocity.sqrMagnitude > 0 && (m_Input.x != 0 || m_Input.y != 0))
{
m_StepCycle += (m_CharacterController.velocity.magnitude + (speed * (m_IsWalking ? 1f : m_RunstepLenghten))) *
Time.fixedDeltaTime;
}
if (!(m_StepCycle > m_NextStep))
{
return;
}
m_NextStep = m_StepCycle + m_StepInterval;
PlayFootStepAudio();
}
private void PlayFootStepAudio()
{
if (!m_CharacterController.isGrounded)
{
return;
}
// pick & play a random footstep sound from the array,
// excluding sound at index 0
int n = Random.Range(1, m_FootstepSounds.Length);
m_AudioSource.clip = m_FootstepSounds[n];
m_AudioSource.PlayOneShot(m_AudioSource.clip);
// move picked sound to index 0 so it's not picked next time
m_FootstepSounds[n] = m_FootstepSounds[0];
m_FootstepSounds[0] = m_AudioSource.clip;
}
private void UpdateCameraPosition(float speed)
{
Vector3 newCameraPosition;
if (!m_UseHeadBob)
{
return;
}
if (m_CharacterController.velocity.magnitude > 0 && m_CharacterController.isGrounded)
{
m_Camera.transform.localPosition =
m_HeadBob.DoHeadBob(m_CharacterController.velocity.magnitude +
(speed * (m_IsWalking ? 1f : m_RunstepLenghten)));
newCameraPosition = m_Camera.transform.localPosition;
newCameraPosition.y = m_Camera.transform.localPosition.y - m_JumpBob.Offset();
}
else
{
newCameraPosition = m_Camera.transform.localPosition;
newCameraPosition.y = m_OriginalCameraPosition.y - m_JumpBob.Offset();
}
m_Camera.transform.localPosition = newCameraPosition;
}
private void GetInput(out float speed)
{
// Read input
float horizontal = CrossPlatformInputManager.GetAxis("Horizontal");
float vertical = CrossPlatformInputManager.GetAxis("Vertical");
bool waswalking = m_IsWalking;
// set the desired speed to be walking or running
speed = m_IsWalking ? m_WalkSpeed : m_RunSpeed;
m_Input = new Vector2(horizontal, vertical);
// normalize input if it exceeds 1 in combined length:
if (m_Input.sqrMagnitude > 1)
{
m_Input.Normalize();
}
// handle speed change to give an fov kick
// only if the player is going to a run, is running and the fovkick is to be used
if (m_IsWalking != waswalking && m_UseFovKick && m_CharacterController.velocity.sqrMagnitude > 0)
{
StopAllCoroutines();
StartCoroutine(!m_IsWalking ? m_FovKick.FOVKickUp() : m_FovKick.FOVKickDown());
}
}
private void RotateView()
{
m_MouseLook.LookRotation(transform, m_Camera.transform);
}
private void OnControllerColliderHit(ControllerColliderHit hit)
{
Rigidbody body = hit.collider.attachedRigidbody;
//don't move the rigidbody if the character is on top of it
if (m_CollisionFlags == CollisionFlags.Below)
{
return;
}
if (body == null || body.isKinematic)
{
return;
}
body.AddForceAtPosition(m_CharacterController.velocity * 0.1f, hit.point, ForceMode.Impulse);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment