PlayerMotor.js
#pragma strict
#pragma implicit
#pragma downcast
@script RequireComponent (CharacterController)
@script AddComponentMenu ("Character/Player Motor")
private var m_Controller : CharacterController;
// motor
var motorAcceleration = 0.18f;
var motorDamping = 0.1f;
var motorJumpForce = 0.25f;
var motorAirSpeed = 0.7f;
var motorSlopeSpeedUp = 1.0f;
var motorSlopeSpeedDown = 1.0f;
@HideInInspector
var m_MoveFactor = 1.0f;
@HideInInspector
var m_MotorThrottle = Vector3.zero;
@HideInInspector
var m_MoveForward = false;
@HideInInspector
var m_MoveLeft = false;
@HideInInspector
var m_MoveRight = false;
@HideInInspector
var m_MoveBack = false;
// physics
var PhysicsForceDamping = 0.05f; // damping of external forces
var PhysicsPushForce = 5.0f; // mass for pushing around rigidbodies
var PhysicsGravityModifier = 0.2f; // affects fall speed
var PhysicsWallBounce = 0.0f; // how much to bounce off walls
var m_ExternalForce = Vector3.zero; // velocity from external forces (explosion knockback, jump pads, rocket packs)
// fall impact variables
var m_WasGroundedLastFrame = true;
var m_FallSpeed = 0.0f;
var m_LastFallSpeed = 0.0f;
var m_HighestFallSpeed = 0.0f;
var m_FallImpact = 0.0f;
// height
@HideInInspector
var m_Compact = false; // if true, the character controller will be forced to half of its initial height
@HideInInspector
var m_NormalHeight = 0.0f; // height of the player controller when not crouching (stored from the character controller in Start)
@HideInInspector
var m_NormalCenter = Vector3.zero; // forced to half of the controller height (for crouching logic)
@HideInInspector
var m_CompactHeight = 0.0f; // height of the player controller when crouching (calculated in Start)
@HideInInspector
var m_CompactCenter = Vector3.zero; // will be half of the compact height
//////////////////////////////////////////////////////////
// in 'Awake' we do things that need to be run once at the
// very beginning. NOTE: as of Unity 4, gameobject hierarchy
// can not be altered in 'Awake'
//////////////////////////////////////////////////////////
function Awake()
{
m_Controller = GetComponent(CharacterController);
// store the initial CharacterController height for crouching logic.
// NOTE: we force the 'Center' parameter of the CharacterController
// to its 'Height' / 2, (putting its pivot point at the bottom)
// or the player may fall through the ground after crouching
m_NormalHeight = m_Controller.height;
m_NormalCenter = new Vector3(0, m_NormalHeight * 0.5f, 0);
m_CompactHeight = m_NormalHeight * 0.5f;
m_CompactCenter = m_NormalCenter * 0.5f;
}
function Update()
{
if (m_Controller.isGrounded) { Debug.Log("We are grounded"); }
UpdateMoves();
var moveDirection = Vector3.zero;
// --- dampen forces ---
// dampen motor force, but only apply vertical damping
// if going up, or jumping will be damped too
var motorDamp : float = (1 + (motorDamping * (Time.deltaTime * 60)));
m_MotorThrottle.x /= motorDamp;
if (m_MotorThrottle.y == (m_MotorThrottle.y > 0.0f))
{
m_MotorThrottle.y = (m_MotorThrottle.y / motorDamp);
}
m_MotorThrottle.z /= motorDamp;
// snap super small values to zero (to avoid floating point bugs)
if (m_MotorThrottle.x == (Mathf.Abs(m_MotorThrottle.x) < 0.0001f))
{
m_MotorThrottle.x = 0.0f;
}
if (m_MotorThrottle.y == (Mathf.Abs(m_MotorThrottle.y) < 0.0001f))
{
m_MotorThrottle.y = 0.0f;
}
if (m_MotorThrottle.z == (Mathf.Abs(m_MotorThrottle.z) < 0.0001f))
{
m_MotorThrottle.z = 0.0f;
}
// --- apply gravity ---
if (!m_Controller.isGrounded && m_FallSpeed <= 0)
{
m_FallSpeed += ((Physics.gravity.y * (PhysicsGravityModifier * 0.002f)) * (Time.deltaTime * 60)); // flying
}
else
{
m_FallSpeed = ((Physics.gravity.y * (PhysicsGravityModifier * 0.002f))); // grounded
}
var antiBumpOffset = 0.0f;
if (m_Controller.isGrounded && m_MotorThrottle.y <= 0.001f)
{
antiBumpOffset = Mathf.Max(m_Controller.stepOffset,
new Vector3(moveDirection.x, 0, moveDirection.z).magnitude);
moveDirection -= antiBumpOffset * Vector3.up;
}
// safeguard against non numerical value error. this may occur if
// the character controller is pushed around by other objects (?)
moveDirection.x = double.IsNaN(moveDirection.x) ? 0.0f : moveDirection.x;
moveDirection.y = double.IsNaN(moveDirection.y) ? 0.0f : moveDirection.y;
moveDirection.z = double.IsNaN(moveDirection.z) ? 0.0f : moveDirection.z;
m_FallImpact = 0.0f;
if (m_FallSpeed < m_LastFallSpeed)
{
m_HighestFallSpeed = m_FallSpeed;
}
if (m_Controller.isGrounded && !m_WasGroundedLastFrame)
{
m_FallImpact = -m_HighestFallSpeed;
m_HighestFallSpeed = 0.0f;
}
m_WasGroundedLastFrame = m_Controller.isGrounded;
m_LastFallSpeed = m_FallSpeed;
// --- move controller ---
moveDirection += m_ExternalForce * (Time.deltaTime * 60);
moveDirection += m_MotorThrottle * (Time.deltaTime * 60);
moveDirection.y += m_FallSpeed * (Time.deltaTime * 60);
// move the charactercontroller
m_Controller.Move(moveDirection);
// do some prediction in order to absorb external forces upon collision
var predictedXZ = Vector3.Scale((m_Controller.transform.localPosition + moveDirection), new Vector3(1, 0, 1));
var predictedY = m_Controller.transform.localPosition.y + moveDirection.y;
}
///////////////////////////////////////////////////////////
// transforms user input data into motion on the controller
///////////////////////////////////////////////////////////
function UpdateMoves()
{
// if we're moving diagonally, slow down using square root of 2
if ((m_MoveForward && m_MoveLeft) || (m_MoveForward && m_MoveRight) ||
(m_MoveBack && m_MoveLeft) || (m_MoveBack && m_MoveRight))
{
m_MoveFactor = 0.70710678f;
}
else
{
m_MoveFactor = 1.0f;
}
// if on the ground, modify movement depending on ground slope
if (m_Controller.isGrounded)
{
//m_MoveFactor *= GetSlopeMultiplier();
}
// if in the air, apply air damping
else
{
m_MoveFactor *= motorAirSpeed;
}
// keep framerate independent
m_MoveFactor *= (Time.deltaTime * 60);
// apply speeds to motor
if (m_MoveForward)
{
m_MotorThrottle += transform.TransformDirection(Vector3.forward * (motorAcceleration * 0.1f) * m_MoveFactor);
}
if (m_MoveBack)
{
m_MotorThrottle += transform.TransformDirection(Vector3.back * (motorAcceleration * 0.1f) * m_MoveFactor);
}
if (m_MoveLeft)
{
m_MotorThrottle += transform.TransformDirection(Vector3.left * (motorAcceleration * 0.1f) * m_MoveFactor);
}
if (m_MoveRight)
{
m_MotorThrottle += transform.TransformDirection(Vector3.right * (motorAcceleration * 0.1f) * m_MoveFactor);
}
// reset input for next frame
m_MoveForward = false;
m_MoveLeft = false;
m_MoveRight = false;
m_MoveBack = false;
}
function Jump() : boolean
{
if (!m_Controller.isGrounded) { return false; }
else
{
m_MotorThrottle += Vector3(0, motorJumpForce, 0);
return true;
}
}
///////////////////////////////////////////////////////////
// move methods for external use
///////////////////////////////////////////////////////////
function MoveForward() { m_MoveForward = true; }
function MoveBack() { m_MoveBack = true; }
function MoveLeft() { m_MoveLeft = true; }
function MoveRight() { m_MoveRight = true; }
///////////////////////////////////////////////////////////
// sets the position of the character controller
///////////////////////////////////////////////////////////
function SetPosition( position : Vector3 )
{
transform.position = position;
}
PlayerInputController.Js
private var motor : PlayerMotor;
var m_LockCursor = true;
// Use this for initialization
function Awake () {
motor = GetComponent(PlayerMotor);
}
function Update()
{
// if the 'LockCursor' property is set on the player, hide
// mouse cursor and center it every frame
Screen.lockCursor = m_LockCursor;
// TIP: edit the 'Input' methods to customize how your game
// deals with input
// handle input for moving
InputWalk();
//InputRun();
InputJump();
//InputCrouch();
// handle input for weapons
//InputFire();
//InputZoom();
//InputReload();
//InputCycleWeapon();
//InputSetWeapon();
// TIP: uncomment either of these lines to debug print the
// speed of the character controller
//Debug.Log(Controller.Velocity.magnitude); // speed in meters per second
//Debug.Log(Controller.Velocity.sqrMagnitude); // speed as used by the camera bob
}
function InputWalk()
{
// classic 'WASD' first person controls
if (Input.GetKey(KeyCode.W)) { motor.MoveForward(); }
if (Input.GetKey(KeyCode.A)) { motor.MoveLeft(); }
if (Input.GetKey(KeyCode.S)) { motor.MoveBack(); }
if (Input.GetKey(KeyCode.D)) { motor.MoveRight(); }
// you may replace the above code with the following to use
// input axes instead, though it might feel a bit sluggish
// and require some tweaking
//if (Input.GetAxis("Vertical") > 0.01f) { Controller.MoveForward(); }
//if (Input.GetAxis("Horizontal") < -0.01f) { Controller.MoveLeft(); }
//if (Input.GetAxis("Vertical") < -0.01f) { Controller.MoveBack(); }
//if (Input.GetAxis("Horizontal") > 0.01f) { Controller.MoveRight(); }
}
function InputJump()
{
if (Input.GetKeyDown(KeyCode.Space))
{ motor.Jump();}
}
If you save both of the scripts and apply them to a standard capsule gameobject you will end up with a basic character motor and input manager, the only problem is if you jump (space key) the character will jump up, fall down, and then jump back up endlessly.
[P.S. This code is a attempted js conversion of VisionPunk’s FPS controller C# code]