- Home /
Approach for forcing player movement
Hi there,
I'm a newbie dev trying to brush up to become better, so pardon if I seem dumbo. (I probably will, bear with me)
I'm trying to create an fps movement similiar to quake/half-life. I'm using the standard asset fps controller, controlling some of it's parameters via a separate script. Whether or not that is a good idea or not, doesn't matter for now I think. Primarily I'm trying to ape up a similiar feeling as to what air acceleration does in half-life. Essentially just cause cannon-ball effect from bunnyhopping
Now my actual question/problem is:
How do I force player forward movement with fps/input axis movement?
The movement is being handled in the fps controller script which Unity's Standard Assets have provided. I'm not 100% sure but I have a general idea that it handles movement via the input manager somehow (I don't understand much of the code in the controller).
I have managed to craft a rudimentary function which accelerates the speed of the player while jumping around.
I'm hoping to add into that functionality a feature which forces the player to maintain movement in the direction of the aim for few seconds, if the player reaches the "top speed" which I've just capped at an emulated value of 15f.
My initial approach to this would be to just check if the top speed has been reached, if so: Force player movement until the speed value has dropped down back to normal speed, upon which stop forced movement. However it seems that a simulated input is unpractical or undoable...(?)
I would assume one could do this via just using rigidbody-based movement, but I feeling results with this approach as the current feeling of accelerated bunnyhopping is pretty great imo.
Here's my player extension script, I have not yet implemented anything for the problem I'm facing, tried some stuff but it just doesn't work so there's no remark for it, I'm mainly just trying to find a good angle for of approach.
I'll just post it here for clarity, I also included the standard fps script for quick reference. Pardon beforehand for longpost and thanks in advance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.Characters.FirstPerson;
public class PlayerExtension : MonoBehaviour {
FirstPersonController FPSCon;
//Walking variables
private float initialWalkSpeed;
private float optimalWalkSpeed = 7.5f;
//Air Acceleration variables
private float accelerationForce = 2.5f;
private float dampening = 2f;
private float maximumPlayerSpeed = 15f;
private bool moving;
public bool forceMove;
void Start(){
//Find player fps controller
FPSCon = GameObject.FindObjectOfType<FirstPersonController> ();
initialWalkSpeed = FPSCon.m_WalkSpeed;
}
void FixedUpdate (){
AirAcceleration ();
//If player is moving in any way, movement is true
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.D)) {
moving = true;
} else {
moving = false;
}
}
public void AirAcceleration(){
//Accelerate while in air
if (FPSCon.m_Jumping == true) {
FPSCon.m_WalkSpeed = FPSCon.m_WalkSpeed + (Time.deltaTime * accelerationForce);
} else {
FPSCon.m_WalkSpeed = FPSCon.m_WalkSpeed - (Time.deltaTime * dampening);
}
//Restrain
if (FPSCon.m_Jumping == false && FPSCon.m_WalkSpeed <= initialWalkSpeed) {
FPSCon.m_WalkSpeed = initialWalkSpeed;
}
//Speedcap
if (FPSCon.m_WalkSpeed >= maximumPlayerSpeed) {
FPSCon.m_WalkSpeed = maximumPlayerSpeed;
}
//Halt speed if input is no longer given
if (moving == false && FPSCon.m_Jumping == false && FPSCon.m_WalkSpeed >= initialWalkSpeed) {
FPSCon.m_WalkSpeed = FPSCon.m_WalkSpeed - (Time.deltaTime * dampening);
//Debug.Log ("No input, slowing");
}
}
}
Here's the fpscontroller that's standard for unity, you can obviously also just ref it in Unity:
using System;
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
{
public float m_WalkSpeed;
[SerializeField] private float m_RunSpeed;
public float m_JumpSpeed;
public float m_GravityMultiplier;
public bool m_Jumping;
[SerializeField] private bool m_IsWalking;
[SerializeField] [Range(0f, 1f)] private float m_RunstepLenghten;
[SerializeField] private float m_StickToGroundForce;
[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 AudioSource m_AudioSource;
// 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");
}
if (!m_PreviouslyGrounded && m_CharacterController.isGrounded)
{
StartCoroutine(m_JumpBob.DoBobCycle());
PlayLandingSound();
m_MoveDir.y = 0f;
m_Jumping = false;
}
if (!m_CharacterController.isGrounded && !m_Jumping && m_PreviouslyGrounded)
{
m_MoveDir.y = 0f;
}
m_PreviouslyGrounded = m_CharacterController.isGrounded;
}
private void PlayLandingSound()
{
m_AudioSource.clip = m_LandSound;
m_AudioSource.Play();
m_NextStep = m_StepCycle + .5f;
}
private void FixedUpdate()
{
float speed;
GetInput(out speed);
// always move along the camera forward as it is the direction that it being aimed at
Vector3 desiredMove = transform.forward*m_Input.y + transform.right*m_Input.x;
// get a normal for the surface that is being touched to move along it
RaycastHit hitInfo;
Physics.SphereCast(transform.position, m_CharacterController.radius, Vector3.down, out hitInfo,
m_CharacterController.height/2f, Physics.AllLayers, QueryTriggerInteraction.Ignore);
desiredMove = Vector3.ProjectOnPlane(desiredMove, hitInfo.normal).normalized;
m_MoveDir.x = desiredMove.x*speed;
m_MoveDir.z = desiredMove.z*speed;
if (m_CharacterController.isGrounded)
{
m_MoveDir.y = -m_StickToGroundForce;
if (m_Jump)
{
m_MoveDir.y = m_JumpSpeed;
PlayJumpSound();
m_Jump = false;
m_Jumping = true;
}
}
else
{
m_MoveDir += Physics.gravity*m_GravityMultiplier*Time.fixedDeltaTime;
}
m_CollisionFlags = m_CharacterController.Move(m_MoveDir*Time.fixedDeltaTime);
ProgressStepCycle(speed);
UpdateCameraPosition(speed);
m_MouseLook.UpdateCursorLock();
}
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;
#if !MOBILE_INPUT
// On standalone builds, walk/run speed is modified by a key press.
// keep track of whether or not the character is walking or running
m_IsWalking = !Input.GetKey(KeyCode.LeftShift);
#endif
// 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;
//dont 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);
}
}
}