1+ // original by Eric Haines (Eric5h5)
2+ // adapted by @torahhorse
3+ // http://wiki.unity3d.com/index.php/FPSWalkerEnhanced
4+
5+ using UnityEngine ;
6+ using System . Collections ;
7+
8+ [ RequireComponent ( typeof ( CharacterController ) ) ]
9+ public class FirstPersonDrifter : MonoBehaviour
10+ {
11+ public float walkSpeed = 6.0f ;
12+ public float runSpeed = 10.0f ;
13+
14+ // If true, diagonal speed (when strafing + moving forward or back) can't exceed normal move speed; otherwise it's about 1.4 times faster
15+ private bool limitDiagonalSpeed = true ;
16+
17+ public bool enableRunning = false ;
18+
19+ public float jumpSpeed = 4.0f ;
20+ public float gravity = 10.0f ;
21+
22+ // Units that player can fall before a falling damage function is run. To disable, type "infinity" in the inspector
23+ private float fallingDamageThreshold = 10.0f ;
24+
25+ // 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
26+ public bool slideWhenOverSlopeLimit = false ;
27+
28+ // If checked and the player is on an object tagged "Slide", he will slide down it regardless of the slope limit
29+ public bool slideOnTaggedObjects = false ;
30+
31+ public float slideSpeed = 5.0f ;
32+
33+ // If checked, then the player can change direction while in the air
34+ public bool airControl = true ;
35+
36+ // Small amounts of this results in bumping when walking down slopes, but large amounts results in falling too fast
37+ public float antiBumpFactor = .75f ;
38+
39+ // Player must be grounded for at least this many physics frames before being able to jump again; set to 0 to allow bunny hopping
40+ public int antiBunnyHopFactor = 1 ;
41+
42+ private Vector3 moveDirection = Vector3 . zero ;
43+ private bool grounded = false ;
44+ private CharacterController controller ;
45+ private Transform myTransform ;
46+ private float speed ;
47+ private RaycastHit hit ;
48+ private float fallStartLevel ;
49+ private bool falling ;
50+ private float slideLimit ;
51+ private float rayDistance ;
52+ private Vector3 contactPoint ;
53+ private bool playerControl = false ;
54+ private int jumpTimer ;
55+
56+ void Start ( )
57+ {
58+ controller = GetComponent < CharacterController > ( ) ;
59+ myTransform = transform ;
60+ speed = walkSpeed ;
61+ rayDistance = controller . height * .5f + controller . radius ;
62+ slideLimit = controller . slopeLimit - .1f ;
63+ jumpTimer = antiBunnyHopFactor ;
64+ }
65+
66+ void FixedUpdate ( ) {
67+ float inputX = Input . GetAxis ( "Horizontal" ) ;
68+ float inputY = Input . GetAxis ( "Vertical" ) ;
69+ // If both horizontal and vertical are used simultaneously, limit speed (if allowed), so the total doesn't exceed normal move speed
70+ float inputModifyFactor = ( inputX != 0.0f && inputY != 0.0f && limitDiagonalSpeed ) ? .7071f : 1.0f ;
71+
72+ if ( grounded ) {
73+ bool sliding = false ;
74+ // See if surface immediately below should be slid down. We use this normally rather than a ControllerColliderHit point,
75+ // because that interferes with step climbing amongst other annoyances
76+ if ( Physics . Raycast ( myTransform . position , - Vector3 . up , out hit , rayDistance ) ) {
77+ if ( Vector3 . Angle ( hit . normal , Vector3 . up ) > slideLimit )
78+ sliding = true ;
79+ }
80+ // However, just raycasting straight down from the center can fail when on steep slopes
81+ // So if the above raycast didn't catch anything, raycast down from the stored ControllerColliderHit point instead
82+ else {
83+ Physics . Raycast ( contactPoint + Vector3 . up , - Vector3 . up , out hit ) ;
84+ if ( Vector3 . Angle ( hit . normal , Vector3 . up ) > slideLimit )
85+ sliding = true ;
86+ }
87+
88+ // If we were falling, and we fell a vertical distance greater than the threshold, run a falling damage routine
89+ if ( falling ) {
90+ falling = false ;
91+ if ( myTransform . position . y < fallStartLevel - fallingDamageThreshold )
92+ FallingDamageAlert ( fallStartLevel - myTransform . position . y ) ;
93+ }
94+
95+ if ( enableRunning )
96+ {
97+ speed = Input . GetButton ( "Run" ) ? runSpeed : walkSpeed ;
98+ }
99+
100+ // 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
101+ if ( ( sliding && slideWhenOverSlopeLimit ) || ( slideOnTaggedObjects && hit . collider . tag == "Slide" ) ) {
102+ Vector3 hitNormal = hit . normal ;
103+ moveDirection = new Vector3 ( hitNormal . x , - hitNormal . y , hitNormal . z ) ;
104+ Vector3 . OrthoNormalize ( ref hitNormal , ref moveDirection ) ;
105+ moveDirection *= slideSpeed ;
106+ playerControl = false ;
107+ }
108+ // Otherwise recalculate moveDirection directly from axes, adding a bit of -y to avoid bumping down inclines
109+ else {
110+ moveDirection = new Vector3 ( inputX * inputModifyFactor , - antiBumpFactor , inputY * inputModifyFactor ) ;
111+ moveDirection = myTransform . TransformDirection ( moveDirection ) * speed ;
112+ playerControl = true ;
113+ }
114+
115+ // Jump! But only if the jump button has been released and player has been grounded for a given number of frames
116+ if ( ! Input . GetButton ( "Jump" ) )
117+ jumpTimer ++ ;
118+ else if ( jumpTimer >= antiBunnyHopFactor ) {
119+ moveDirection . y = jumpSpeed ;
120+ jumpTimer = 0 ;
121+ }
122+ }
123+ else {
124+ // If we stepped over a cliff or something, set the height at which we started falling
125+ if ( ! falling ) {
126+ falling = true ;
127+ fallStartLevel = myTransform . position . y ;
128+ }
129+
130+ // If air control is allowed, check movement but don't touch the y component
131+ if ( airControl && playerControl ) {
132+ moveDirection . x = inputX * speed * inputModifyFactor ;
133+ moveDirection . z = inputY * speed * inputModifyFactor ;
134+ moveDirection = myTransform . TransformDirection ( moveDirection ) ;
135+ }
136+ }
137+
138+ // Apply gravity
139+ moveDirection . y -= gravity * Time . deltaTime ;
140+
141+ // Move the controller, and set grounded true or false depending on whether we're standing on something
142+ grounded = ( controller . Move ( moveDirection * Time . deltaTime ) & CollisionFlags . Below ) != 0 ;
143+ }
144+
145+ // Store point that we're in contact with for use in FixedUpdate if needed
146+ void OnControllerColliderHit ( ControllerColliderHit hit ) {
147+ contactPoint = hit . point ;
148+ }
149+
150+ // If falling damage occured, this is the place to do something about it. You can make the player
151+ // have hitpoints and remove some of them based on the distance fallen, add sound effects, etc.
152+ void FallingDamageAlert ( float fallDistance )
153+ {
154+ //print ("Ouch! Fell " + fallDistance + " units!");
155+ }
156+ }
0 commit comments