├── .gitignore
├── Assets
├── Resources.meta
├── Resources
│ ├── Crosshair.png
│ ├── Crosshair.png.meta
│ ├── Prefabs.meta
│ ├── Prefabs
│ │ ├── Player.prefab
│ │ └── Player.prefab.meta
│ ├── SFX.meta
│ └── SFX
│ │ ├── Footstep1.wav
│ │ ├── Footstep1.wav.meta
│ │ ├── Footstep3.wav
│ │ ├── Footstep3.wav.meta
│ │ ├── Footstep4.wav
│ │ ├── Footstep4.wav.meta
│ │ ├── Jump.wav
│ │ ├── Jump.wav.meta
│ │ ├── Land.wav
│ │ └── Land.wav.meta
├── Scenes.meta
├── Scenes
│ ├── TestScene.unity
│ └── TestScene.unity.meta
├── Scripts.meta
└── Scripts
│ ├── FpsController.cs
│ ├── FpsController.cs.meta
│ ├── GuiDraw.cs
│ └── GuiDraw.cs.meta
├── FpsController.unitypackage
├── LICENSE
├── Packages
└── manifest.json
├── ProjectSettings
├── AudioManager.asset
├── ClusterInputManager.asset
├── DynamicsManager.asset
├── EditorBuildSettings.asset
├── EditorSettings.asset
├── GraphicsSettings.asset
├── InputManager.asset
├── MemorySettings.asset
├── NavMeshAreas.asset
├── NetworkManager.asset
├── PackageManagerSettings.asset
├── Physics2DSettings.asset
├── PresetManager.asset
├── ProjectSettings.asset
├── ProjectVersion.txt
├── QualitySettings.asset
├── SceneTemplateSettings.json
├── TagManager.asset
├── TimeManager.asset
├── UnityConnectSettings.asset
├── VFXManager.asset
└── VersionControlSettings.asset
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs
2 | Library
3 | /Temp
4 | /Build
5 | CoreCompileInputs.cache
6 | Logs
7 | obj
8 | .vsconfig
9 | *.csproj
10 | *.sln
11 | *.config
12 | UserSettings
13 | packages-lock.json
14 | BillingMode.json
15 | BillingMode.json.meta
16 |
--------------------------------------------------------------------------------
/Assets/Resources.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1c9ab856988c4584a8f3db570b4a1e5a
3 | folderAsset: yes
4 | timeCreated: 1497807515
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Assets/Resources/Crosshair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Resources/Crosshair.png
--------------------------------------------------------------------------------
/Assets/Resources/Crosshair.png.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a44bf4433c320bb439608b0eaec1c054
3 | TextureImporter:
4 | internalIDToNameTable: []
5 | externalObjects: {}
6 | serializedVersion: 11
7 | mipmaps:
8 | mipMapMode: 0
9 | enableMipMap: 1
10 | sRGBTexture: 1
11 | linearTexture: 0
12 | fadeOut: 0
13 | borderMipMap: 0
14 | mipMapsPreserveCoverage: 0
15 | alphaTestReferenceValue: 0.5
16 | mipMapFadeDistanceStart: 1
17 | mipMapFadeDistanceEnd: 3
18 | bumpmap:
19 | convertToNormalMap: 0
20 | externalNormalMap: 0
21 | heightScale: 0.25
22 | normalMapFilter: 0
23 | isReadable: 0
24 | streamingMipmaps: 0
25 | streamingMipmapsPriority: 0
26 | vTOnly: 0
27 | ignoreMasterTextureLimit: 0
28 | grayScaleToAlpha: 0
29 | generateCubemap: 6
30 | cubemapConvolution: 0
31 | seamlessCubemap: 0
32 | textureFormat: 1
33 | maxTextureSize: 2048
34 | textureSettings:
35 | serializedVersion: 2
36 | filterMode: 1
37 | aniso: 16
38 | mipBias: 0
39 | wrapU: 0
40 | wrapV: 0
41 | wrapW: 0
42 | nPOTScale: 0
43 | lightmap: 0
44 | compressionQuality: 50
45 | spriteMode: 1
46 | spriteExtrude: 1
47 | spriteMeshType: 1
48 | alignment: 0
49 | spritePivot: {x: 0.5, y: 0.5}
50 | spritePixelsToUnits: 100
51 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
52 | spriteGenerateFallbackPhysicsShape: 1
53 | alphaUsage: 1
54 | alphaIsTransparency: 1
55 | spriteTessellationDetail: -1
56 | textureType: 8
57 | textureShape: 1
58 | singleChannelComponent: 0
59 | flipbookRows: 1
60 | flipbookColumns: 1
61 | maxTextureSizeSet: 0
62 | compressionQualitySet: 0
63 | textureFormatSet: 0
64 | ignorePngGamma: 0
65 | applyGammaDecoding: 1
66 | platformSettings:
67 | - serializedVersion: 3
68 | buildTarget: DefaultTexturePlatform
69 | maxTextureSize: 32
70 | resizeAlgorithm: 0
71 | textureFormat: -1
72 | textureCompression: 1
73 | compressionQuality: 50
74 | crunchedCompression: 0
75 | allowsAlphaSplitting: 0
76 | overridden: 0
77 | androidETC2FallbackOverride: 0
78 | forceMaximumCompressionQuality_BC6H_BC7: 1
79 | - serializedVersion: 3
80 | buildTarget: Standalone
81 | maxTextureSize: 32
82 | resizeAlgorithm: 0
83 | textureFormat: -1
84 | textureCompression: 1
85 | compressionQuality: 50
86 | crunchedCompression: 0
87 | allowsAlphaSplitting: 0
88 | overridden: 0
89 | androidETC2FallbackOverride: 0
90 | forceMaximumCompressionQuality_BC6H_BC7: 1
91 | - serializedVersion: 3
92 | buildTarget: Android
93 | maxTextureSize: 32
94 | resizeAlgorithm: 0
95 | textureFormat: -1
96 | textureCompression: 1
97 | compressionQuality: 50
98 | crunchedCompression: 0
99 | allowsAlphaSplitting: 0
100 | overridden: 0
101 | androidETC2FallbackOverride: 0
102 | forceMaximumCompressionQuality_BC6H_BC7: 1
103 | spriteSheet:
104 | serializedVersion: 2
105 | sprites: []
106 | outline: []
107 | physicsShape: []
108 | bones: []
109 | spriteID: 5e97eb03825dee720800000000000000
110 | internalID: 0
111 | vertices: []
112 | indices:
113 | edges: []
114 | weights: []
115 | secondaryTextures: []
116 | nameFileIdTable: {}
117 | spritePackingTag:
118 | pSDRemoveMatte: 0
119 | pSDShowRemoveMatteOption: 0
120 | userData:
121 | assetBundleName:
122 | assetBundleVariant:
123 |
--------------------------------------------------------------------------------
/Assets/Resources/Prefabs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8830ff8f41a04cd40b32632b1a88822d
3 | folderAsset: yes
4 | timeCreated: 1499024669
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Assets/Resources/Prefabs/Player.prefab:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Resources/Prefabs/Player.prefab
--------------------------------------------------------------------------------
/Assets/Resources/Prefabs/Player.prefab.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 680a11299af23874bbc0b94457026c5e
3 | timeCreated: 1512220078
4 | licenseType: Pro
5 | NativeFormatImporter:
6 | mainObjectFileID: 100100000
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Assets/Resources/SFX.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c4b5e53d8fefa0848b273ad9c97d5b4c
3 | folderAsset: yes
4 | timeCreated: 1525091452
5 | licenseType: Pro
6 | DefaultImporter:
7 | externalObjects: {}
8 | userData:
9 | assetBundleName:
10 | assetBundleVariant:
11 |
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Footstep1.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Resources/SFX/Footstep1.wav
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Footstep1.wav.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: da9bb2a8b8346ad4cbb676fb30c1059f
3 | timeCreated: 1524508501
4 | licenseType: Pro
5 | AudioImporter:
6 | externalObjects: {}
7 | serializedVersion: 6
8 | defaultSettings:
9 | loadType: 0
10 | sampleRateSetting: 0
11 | sampleRateOverride: 44100
12 | compressionFormat: 1
13 | quality: 1
14 | conversionMode: 0
15 | platformSettingOverrides: {}
16 | forceToMono: 0
17 | normalize: 1
18 | preloadAudioData: 1
19 | loadInBackground: 0
20 | ambisonic: 0
21 | 3D: 1
22 | userData:
23 | assetBundleName:
24 | assetBundleVariant:
25 |
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Footstep3.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Resources/SFX/Footstep3.wav
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Footstep3.wav.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 307142cd55fbf46419949d43caf77ae8
3 | timeCreated: 1524508500
4 | licenseType: Pro
5 | AudioImporter:
6 | externalObjects: {}
7 | serializedVersion: 6
8 | defaultSettings:
9 | loadType: 0
10 | sampleRateSetting: 0
11 | sampleRateOverride: 44100
12 | compressionFormat: 1
13 | quality: 1
14 | conversionMode: 0
15 | platformSettingOverrides: {}
16 | forceToMono: 0
17 | normalize: 1
18 | preloadAudioData: 1
19 | loadInBackground: 0
20 | ambisonic: 0
21 | 3D: 1
22 | userData:
23 | assetBundleName:
24 | assetBundleVariant:
25 |
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Footstep4.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Resources/SFX/Footstep4.wav
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Footstep4.wav.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3e8975e860d60d34a8776aa49b0f7a1f
3 | timeCreated: 1524508500
4 | licenseType: Pro
5 | AudioImporter:
6 | externalObjects: {}
7 | serializedVersion: 6
8 | defaultSettings:
9 | loadType: 0
10 | sampleRateSetting: 0
11 | sampleRateOverride: 44100
12 | compressionFormat: 1
13 | quality: 1
14 | conversionMode: 0
15 | platformSettingOverrides: {}
16 | forceToMono: 0
17 | normalize: 1
18 | preloadAudioData: 1
19 | loadInBackground: 0
20 | ambisonic: 0
21 | 3D: 1
22 | userData:
23 | assetBundleName:
24 | assetBundleVariant:
25 |
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Jump.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Resources/SFX/Jump.wav
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Jump.wav.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: df9999788f5c7fc4393dac671e28fc02
3 | timeCreated: 1524512010
4 | licenseType: Pro
5 | AudioImporter:
6 | externalObjects: {}
7 | serializedVersion: 6
8 | defaultSettings:
9 | loadType: 0
10 | sampleRateSetting: 0
11 | sampleRateOverride: 44100
12 | compressionFormat: 1
13 | quality: 1
14 | conversionMode: 0
15 | platformSettingOverrides: {}
16 | forceToMono: 0
17 | normalize: 1
18 | preloadAudioData: 1
19 | loadInBackground: 0
20 | ambisonic: 0
21 | 3D: 1
22 | userData:
23 | assetBundleName:
24 | assetBundleVariant:
25 |
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Land.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Resources/SFX/Land.wav
--------------------------------------------------------------------------------
/Assets/Resources/SFX/Land.wav.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 37793e39afabb914fae6b01ba884bc0b
3 | timeCreated: 1524512010
4 | licenseType: Pro
5 | AudioImporter:
6 | externalObjects: {}
7 | serializedVersion: 6
8 | defaultSettings:
9 | loadType: 0
10 | sampleRateSetting: 0
11 | sampleRateOverride: 44100
12 | compressionFormat: 1
13 | quality: 1
14 | conversionMode: 0
15 | platformSettingOverrides: {}
16 | forceToMono: 0
17 | normalize: 1
18 | preloadAudioData: 1
19 | loadInBackground: 0
20 | ambisonic: 0
21 | 3D: 1
22 | userData:
23 | assetBundleName:
24 | assetBundleVariant:
25 |
--------------------------------------------------------------------------------
/Assets/Scenes.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 05266d0c5222bd14496c1eb41bbfce35
3 | folderAsset: yes
4 | timeCreated: 1497782827
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Assets/Scenes/TestScene.unity:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/Assets/Scenes/TestScene.unity
--------------------------------------------------------------------------------
/Assets/Scenes/TestScene.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f434e6fc8e255934184b04b6c6b4734e
3 | timeCreated: 1497782827
4 | licenseType: Pro
5 | DefaultImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Assets/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8e0fac89ae6402648bb892e5fea3cc20
3 | folderAsset: yes
4 | timeCreated: 1497781807
5 | licenseType: Pro
6 | DefaultImporter:
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Assets/Scripts/FpsController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using UnityEngine;
3 |
4 | ///
5 | /// Q3-based first person controller
6 | ///
7 | public class FpsController : MonoBehaviour
8 | {
9 | #region Drag Drop
10 | [Header("Components")]
11 | [SerializeField]
12 | private Transform _camTransform = default;
13 |
14 | // Collision resolving is done with respect to this volume
15 | [SerializeField]
16 | private CapsuleCollider _collisionVolume = default;
17 |
18 | // Collision will not happend with these layers
19 | // One of them has to be this controller's own layer
20 | [SerializeField]
21 | private LayerMask _excludedLayers = default;
22 |
23 | [SerializeField]
24 | private bool _debugInfo = default;
25 |
26 | [SerializeField]
27 | private List _groundedRayPositions = default;
28 | #endregion
29 |
30 | #region Movement Parameters
31 | [Header("Movement parameters")]
32 | // The controller can collide with colliders within this radius
33 | [SerializeField]
34 | private float _radius = 2f;
35 |
36 | // Ad-hoc approach to make the controller accelerate faster
37 | [SerializeField]
38 | private float _groundAccelerationCoeff = 500.0f;
39 |
40 | // How fast the controller accelerates while it's not grounded
41 | [SerializeField]
42 | private float _airAccelCoeff = 1f;
43 |
44 | // Air deceleration occurs when the player gives an input that's not aligned with the current velocity
45 | [SerializeField]
46 | private float _airDecelCoeff = 1.5f;
47 |
48 | // Along a dimension, we can't go faster than this
49 | // This dimension is relative to the controller, not global
50 | // Meaning that "max speend along X" means "max speed along 'right side' of the controller"
51 | [SerializeField]
52 | private float _maxSpeedAlongOneDimension = 8f;
53 |
54 | // How fast the controller decelerates on the grounded
55 | [SerializeField]
56 | private float _friction = 15;
57 |
58 | // Stop if under this speed
59 | [SerializeField]
60 | private float _frictionSpeedThreshold = 0.5f;
61 |
62 | // Push force given when jumping
63 | [SerializeField]
64 | private float _jumpStrength = 8f;
65 |
66 | // yeah...
67 | [SerializeField]
68 | private float _gravityAmount = 24f;
69 |
70 | // How precise the controller can change direction while not grounded
71 | [SerializeField]
72 | private float _airControlPrecision = 16f;
73 |
74 | // When moving only forward, increase air control dramatically
75 | [SerializeField]
76 | private float _airControlAdditionForward = 8f;
77 |
78 | // Keyboard and mouse input are enabled
79 | [SerializeField]
80 | private bool _canControl = true;
81 |
82 | // Maybe you wanna make a Wolf3D clone?
83 | [SerializeField]
84 | private bool _verticalLookEnabled = true;
85 |
86 | [SerializeField]
87 | private bool _jumpEnabled = true;
88 |
89 | [Header("Footsteps")]
90 | [SerializeField]
91 | private float _distancePerFootstep = 3f;
92 |
93 | [SerializeField]
94 | private AudioSource _audioSource = default;
95 |
96 | [SerializeField]
97 | private AudioClip[] _footstepClips = default;
98 |
99 | [SerializeField]
100 | private AudioClip _jumpClip = default;
101 |
102 | [SerializeField]
103 | private AudioClip _landClip = default;
104 |
105 | private List _shuffledFootstepClips;
106 | private Vector3 _prevPos;
107 | private float _distanceCovered;
108 | #endregion
109 |
110 | #region Fields
111 | // The real velocity of this controller
112 | private Vector3 _velocity;
113 |
114 | // Raw input taken with GetAxisRaw()
115 | private Vector3 _moveInput;
116 |
117 | // Vertical look
118 | private float _pitch = 0; // We keep track of this value since we want to clamp it
119 | private const float Sensitivity = 150;
120 |
121 | // Caching...
122 | private readonly Collider[] _overlappingColliders = new Collider[10]; // Hope no more is needed
123 | private Transform _ghostJumpRayPosition;
124 |
125 | // Some information to persist
126 | private bool _isGroundedInPrevFrame;
127 | private bool _isGonnaJump;
128 | private Vector3 _wishDirDebug;
129 | #endregion
130 |
131 | private void Start()
132 | {
133 | Application.targetFrameRate = 60; // My laptop is shitty and burn itself to death if not for this
134 | _ghostJumpRayPosition = _groundedRayPositions[_groundedRayPositions.Count - 1];
135 |
136 | _shuffledFootstepClips = new List(_footstepClips);
137 | }
138 |
139 | // Only for debug drawing
140 | private void OnGUI()
141 | {
142 | if (!_debugInfo)
143 | {
144 | return;
145 | }
146 |
147 | // Print current horizontal speed
148 | Vector3 ups = _velocity;
149 | ups.y = 0;
150 | GUI.Box(new Rect(Screen.width / 2f - 50, Screen.height / 2f + 50, 100, 40),
151 | (Mathf.Round(ups.magnitude * 100) / 100).ToString());
152 |
153 | // Draw horizontal speed as a line
154 | Vector2 mid = new(Screen.width / 2, Screen.height / 2); // Should remain integer division, otherwise GUI drawing gets screwed up
155 | Vector3 v = _camTransform.InverseTransformDirectionHorizontal(_velocity) * (_velocity.ToHorizontal().magnitude * 10f);
156 | if (v.ToHorizontal().magnitude > 0.0001)
157 | {
158 | GuiDraw.DrawLine(mid, mid + Vector2.up * -v.z + Vector2.right * v.x, Color.red, 3f);
159 | }
160 |
161 | // Draw input direction
162 | Vector3 w = _camTransform.InverseTransformDirectionHorizontal(_wishDirDebug) * 100;
163 | if (w.magnitude > 0.001)
164 | {
165 | GuiDraw.DrawLine(mid, mid + Vector2.up * -w.z + Vector2.right * w.x, Color.blue, 2f);
166 | }
167 | }
168 |
169 | private void Update()
170 | {
171 | Cursor.lockState = CursorLockMode.Locked; // Keep doing this. We don't want cursor anywhere just yet
172 |
173 | float dt = Time.deltaTime;
174 |
175 | if (_canControl)
176 | {
177 | // We use GetAxisRaw, since we need it to feel as responsive as possible
178 | _moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")).normalized;
179 |
180 | if (_jumpEnabled && Input.GetKeyDown(KeyCode.Space) && !_isGonnaJump)
181 | {
182 | _isGonnaJump = true;
183 | }
184 | else if (Input.GetKeyUp(KeyCode.Space))
185 | {
186 | _isGonnaJump = false;
187 | }
188 |
189 | // Mouse look
190 | if (_verticalLookEnabled)
191 | {
192 | _pitch += Input.GetAxis("Mouse Y") * -Sensitivity * dt;
193 | _pitch = Mathf.Clamp(_pitch, -89, 89);
194 | _camTransform.localRotation = Quaternion.Euler(Vector3.right * _pitch);
195 | }
196 | transform.rotation *= Quaternion.Euler(Input.GetAxis("Mouse X") * Sensitivity * dt * Vector3.up);
197 | }
198 |
199 | // MOVEMENT
200 | Vector3 wishDir = _camTransform.TransformDirectionHorizontal(_moveInput); // We want to go in this direction
201 | _wishDirDebug = wishDir.ToHorizontal();
202 |
203 | bool isGrounded = IsGrounded(out Vector3 groundNormal);
204 |
205 | UpdateFootsteps(_isGonnaJump, isGrounded, isGrounded && !_isGroundedInPrevFrame);
206 |
207 | if (isGrounded) // Ground move
208 | {
209 | // Don't apply friction if just landed or about to jump
210 | if (_isGroundedInPrevFrame && !_isGonnaJump)
211 | {
212 | ApplyFriction(ref _velocity, dt);
213 | }
214 |
215 | Accelerate(ref _velocity, wishDir, _groundAccelerationCoeff, dt);
216 |
217 | // Crop up horizontal velocity component
218 | _velocity = Vector3.ProjectOnPlane(_velocity, groundNormal);
219 | if (_isGonnaJump)
220 | {
221 | // Jump away
222 | _velocity += -Gravity.Down * _jumpStrength;
223 | }
224 | }
225 | else // Air move
226 | {
227 | // If the input doesn't have the same facing with the current velocity
228 | // then slow down instead of speeding up
229 | float coeff = Vector3.Dot(_velocity, wishDir) > 0 ? _airAccelCoeff : _airDecelCoeff;
230 |
231 | Accelerate(ref _velocity, wishDir, coeff, dt);
232 |
233 | if (Mathf.Abs(_moveInput.z) > 0.0001) // Pure side velocity doesn't allow air control
234 | {
235 | ApplyAirControl(ref _velocity, wishDir, dt);
236 | }
237 |
238 | _velocity += Gravity.Down * (_gravityAmount * dt);
239 | }
240 |
241 | Vector3 displacement = _velocity * dt;
242 |
243 | // If we're moving too fast, make sure we don't hollow through any collider
244 | if (displacement.magnitude > _collisionVolume.radius)
245 | {
246 | ClampDisplacement(ref _velocity, ref displacement, transform.position);
247 | }
248 |
249 | transform.position += displacement;
250 |
251 | Vector3 collisionDisplacement = ResolveCollisions(ref _velocity);
252 |
253 | transform.position += collisionDisplacement;
254 | _isGroundedInPrevFrame = isGrounded;
255 |
256 |
257 | // Testing
258 | //if (Input.GetKeyDown(KeyCode.G))
259 | //{
260 | // Gravity.Set(Vector3.right);
261 | // _transform.rotation = Quaternion.LookRotation(Gravity.Forward, -Gravity.Down);
262 | //}
263 | }
264 |
265 | private void Accelerate(ref Vector3 playerVelocity, Vector3 accelDir, float accelCoeff, float dt)
266 | {
267 | // How much speed we already have in the direction we want to speed up
268 | float projSpeed = Vector3.Dot(playerVelocity, accelDir);
269 |
270 | // How much speed we need to add (in that direction) to reach max speed
271 | float addSpeed = _maxSpeedAlongOneDimension - projSpeed;
272 | if (addSpeed <= 0)
273 | {
274 | return;
275 | }
276 |
277 | // How much we are gonna increase our speed
278 | // maxSpeed * dt => the real deal. a = v / t
279 | // accelCoeff => ad hoc approach to make it feel better
280 | float accelAmount = accelCoeff * _maxSpeedAlongOneDimension * dt;
281 |
282 | // If we are accelerating more than in a way that we exceed maxSpeedInOneDimension, crop it to max
283 | if (accelAmount > addSpeed)
284 | {
285 | accelAmount = addSpeed;
286 | }
287 |
288 | playerVelocity += accelDir * accelAmount; // Magic happens here
289 | }
290 |
291 | private void ApplyFriction(ref Vector3 playerVelocity, float dt)
292 | {
293 | float speed = playerVelocity.magnitude;
294 | if (speed <= 0.00001f)
295 | {
296 | return;
297 | }
298 |
299 | float downLimit = Mathf.Max(speed, _frictionSpeedThreshold); // Don't drop below treshold
300 | float dropAmount = speed - (downLimit * _friction * dt);
301 | if (dropAmount < 0)
302 | {
303 | dropAmount = 0;
304 | }
305 |
306 | playerVelocity *= dropAmount / speed; // Reduce the velocity by a certain percent
307 | }
308 |
309 | private void ApplyAirControl(ref Vector3 playerVelocity, Vector3 accelDir, float dt)
310 | {
311 | // This only happens in the horizontal plane
312 | // TODO: Verify that these work with various gravity values
313 | Vector3 playerDirHorz = playerVelocity.ToHorizontal().normalized;
314 | float playerSpeedHorz = playerVelocity.ToHorizontal().magnitude;
315 |
316 | float dot = Vector3.Dot(playerDirHorz, accelDir);
317 | if (dot > 0)
318 | {
319 | float k = _airControlPrecision * dot * dot * dt;
320 |
321 | // CPMA thingy:
322 | // If we want pure forward movement, we have much more air control
323 | bool isPureForward = Mathf.Abs(_moveInput.x) < 0.0001f && Mathf.Abs(_moveInput.z) > 0;
324 | if (isPureForward)
325 | {
326 | k *= _airControlAdditionForward;
327 | }
328 |
329 | // A little bit closer to accelDir
330 | playerDirHorz = playerDirHorz * playerSpeedHorz + accelDir * k;
331 | playerDirHorz.Normalize();
332 |
333 | // Assign new direction, without touching the vertical speed
334 | playerVelocity = (playerDirHorz * playerSpeedHorz).ToHorizontal() + Gravity.Up * playerVelocity.VerticalComponent();
335 | }
336 |
337 | }
338 |
339 | // Calculates the displacement required in order not to be in a world collider
340 | private Vector3 ResolveCollisions(ref Vector3 playerVelocity)
341 | {
342 | // Get nearby colliders
343 | Physics.OverlapSphereNonAlloc(transform.position, _radius + 0.1f,
344 | _overlappingColliders, ~_excludedLayers);
345 |
346 | Vector3 totalDisplacement = Vector3.zero;
347 | HashSet checkedColliderIndices = new();
348 |
349 | // If the player is intersecting with that environment collider, separate them
350 | for (int i = 0; i < _overlappingColliders.Length; i++)
351 | {
352 | // Two player colliders shouldn't resolve collision with the same environment collider
353 | if (checkedColliderIndices.Contains(i))
354 | {
355 | continue;
356 | }
357 |
358 | Collider envColl = _overlappingColliders[i];
359 |
360 | // Skip empty slots
361 | if (envColl == null)
362 | {
363 | continue;
364 | }
365 |
366 | if (Physics.ComputePenetration(
367 | _collisionVolume, _collisionVolume.transform.position, _collisionVolume.transform.rotation,
368 | envColl, envColl.transform.position, envColl.transform.rotation,
369 | out Vector3 collisionNormal, out float collisionDistance))
370 | {
371 | // Ignore very small penetrations
372 | // Required for standing still on slopes
373 | // ... still far from perfect though
374 | if (collisionDistance < 0.015f)
375 | {
376 | continue;
377 | }
378 |
379 | checkedColliderIndices.Add(i);
380 |
381 | // Get outta that collider!
382 | totalDisplacement += collisionNormal * collisionDistance;
383 |
384 | // Crop down the velocity component which is in the direction of penetration
385 | playerVelocity -= Vector3.Project(playerVelocity, collisionNormal);
386 | }
387 | }
388 |
389 | // It's better to be in a clean state in the next resolve call
390 | for (int i = 0; i < _overlappingColliders.Length; i++)
391 | {
392 | _overlappingColliders[i] = null;
393 | }
394 |
395 | return totalDisplacement;
396 | }
397 |
398 | // If one of the rays hit, we're considered to be grounded
399 | private bool IsGrounded(out Vector3 groundNormal)
400 | {
401 | groundNormal = -Gravity.Down;
402 |
403 | bool isGrounded = false;
404 | foreach (Transform t in _groundedRayPositions)
405 | {
406 | // The last one is reserved for ghost jumps
407 | // Don't check that one if already on the ground
408 | if (t == _ghostJumpRayPosition && isGrounded)
409 | {
410 | continue;
411 | }
412 |
413 | if (Physics.Raycast(t.position, Gravity.Down, out RaycastHit hit, 0.51f, ~_excludedLayers))
414 | {
415 | groundNormal = hit.normal;
416 | isGrounded = true;
417 | }
418 | }
419 |
420 | return isGrounded;
421 | }
422 |
423 | // If there's something between the current position and the next, clamp displacement
424 | private void ClampDisplacement(ref Vector3 playerVelocity, ref Vector3 displacement, Vector3 playerPosition)
425 | {
426 | if (Physics.Raycast(playerPosition, playerVelocity.normalized, out RaycastHit hit, displacement.magnitude, ~_excludedLayers))
427 | {
428 | displacement = hit.point - playerPosition;
429 | }
430 | }
431 |
432 | private void UpdateFootsteps(bool isGonnaJump, bool isGrounded, bool isLandedThisFrame)
433 | {
434 | if (_distanceCovered > _distancePerFootstep)
435 | {
436 | _distanceCovered = 0;
437 |
438 | _audioSource.PlayOneShot(_shuffledFootstepClips[0]);
439 | _shuffledFootstepClips.Shuffle();
440 | }
441 |
442 | if (isGonnaJump && isGrounded)
443 | {
444 | _audioSource.PlayOneShot(_jumpClip);
445 | }
446 |
447 | if (isLandedThisFrame && !isGonnaJump)
448 | {
449 | _audioSource.PlayOneShot(_landClip);
450 | }
451 |
452 | if (isGrounded)
453 | {
454 | _distanceCovered += Vector3.Distance(_prevPos.ToHorizontal(), transform.position.ToHorizontal());
455 | }
456 | _prevPos = transform.position;
457 | }
458 |
459 | // Handy when testing
460 | public void ResetAt(Transform t)
461 | {
462 | transform.position = t.position + Vector3.up * 0.5f;
463 | _camTransform.position = transform.position;
464 | _velocity = t.TransformDirection(Vector3.forward);
465 | }
466 | }
467 |
468 | public static class Gravity
469 | {
470 | public static Vector3 Down { get; private set; }
471 | public static Vector3 Forward { get; private set; }
472 | public static Vector3 Up { get { return -Down; } }
473 |
474 | static Gravity()
475 | {
476 | Down = Vector3.down;
477 | Forward = Vector3.forward;
478 | }
479 |
480 | public static void Set(Vector3 down)
481 | {
482 | // Gravity will rotate around this axis with this amount
483 | Vector3 axis = Vector3.Cross(Down, down);
484 | float angle = Vector3.Angle(Down, down);
485 |
486 | Down = down;
487 | Forward = Quaternion.AngleAxis(angle, axis) * Forward;
488 | }
489 | }
490 |
491 | public static class FpsControllerExtensions
492 | {
493 |
494 | public static Vector3 ToHorizontal(this Vector3 v)
495 | {
496 | return Vector3.ProjectOnPlane(v, Gravity.Down);
497 | }
498 |
499 | public static float VerticalComponent(this Vector3 v)
500 | {
501 | return Vector3.Dot(v, Gravity.Up);
502 | }
503 |
504 | public static Vector3 TransformDirectionHorizontal(this Transform t, Vector3 v)
505 | {
506 | return t.TransformDirection(v).ToHorizontal().normalized;
507 | }
508 |
509 | public static Vector3 InverseTransformDirectionHorizontal(this Transform t, Vector3 v)
510 | {
511 | return t.InverseTransformDirection(v).ToHorizontal().normalized;
512 | }
513 |
514 | public static void Shuffle(this IList list)
515 | {
516 | int n = list.Count;
517 | while (n > 1)
518 | {
519 | n--;
520 | int k = Random.Range(0, n + 1);
521 | (list[n], list[k]) = (list[k], list[n]); // Swap
522 | }
523 | }
524 |
525 | }
526 |
527 |
--------------------------------------------------------------------------------
/Assets/Scripts/FpsController.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d51bdb5febf4bac42bbc2f1b4af1ca37
3 | timeCreated: 1497783531
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Assets/Scripts/GuiDraw.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | // Taken from http://wiki.unity3d.com/index.php?title=DrawLine (the link is dead now)
4 | public class GuiDraw
5 | {
6 | //****************************************************************************************************
7 | // static function DrawLine(rect : Rect) : void
8 | // static function DrawLine(rect : Rect, color : Color) : void
9 | // static function DrawLine(rect : Rect, width : float) : void
10 | // static function DrawLine(rect : Rect, color : Color, width : float) : void
11 | // static function DrawLine(Vector2 pointA, Vector2 pointB) : void
12 | // static function DrawLine(Vector2 pointA, Vector2 pointB, color : Color) : void
13 | // static function DrawLine(Vector2 pointA, Vector2 pointB, width : float) : void
14 | // static function DrawLine(Vector2 pointA, Vector2 pointB, color : Color, width : float) : void
15 | //
16 | // Draws a GUI line on the screen.
17 | //
18 | // DrawLine makes up for the severe lack of 2D line rendering in the Unity runtime GUI system.
19 | // This function works by drawing a 1x1 texture filled with a color, which is then scaled
20 | // and rotated by altering the GUI matrix. The matrix is restored afterwards.
21 | //****************************************************************************************************
22 |
23 | public static Texture2D lineTex;
24 |
25 | public static void DrawLine(Rect rect) { DrawLine(rect, GUI.contentColor, 1.0f); }
26 | public static void DrawLine(Rect rect, Color color) { DrawLine(rect, color, 1.0f); }
27 | public static void DrawLine(Rect rect, float width) { DrawLine(rect, GUI.contentColor, width); }
28 | public static void DrawLine(Rect rect, Color color, float width) { DrawLine(new Vector2(rect.x, rect.y), new Vector2(rect.x + rect.width, rect.y + rect.height), color, width); }
29 | public static void DrawLine(Vector2 pointA, Vector2 pointB) { DrawLine(pointA, pointB, GUI.contentColor, 1.0f); }
30 | public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color) { DrawLine(pointA, pointB, color, 1.0f); }
31 | public static void DrawLine(Vector2 pointA, Vector2 pointB, float width) { DrawLine(pointA, pointB, GUI.contentColor, width); }
32 | public static void DrawLine(Vector2 pointA, Vector2 pointB, Color color, float width)
33 | {
34 | // Save the current GUI matrix, since we're going to make changes to it.
35 | Matrix4x4 matrix = GUI.matrix;
36 |
37 | // Generate a single pixel texture if it doesn't exist
38 | if (!lineTex) { lineTex = new Texture2D(1, 1); }
39 |
40 | // Store current GUI color, so we can switch it back later,
41 | // and set the GUI color to the color parameter
42 | Color savedColor = GUI.color;
43 | GUI.color = color;
44 |
45 | // Determine the angle of the line.
46 | float angle = Vector3.Angle(pointB - pointA, Vector2.right);
47 |
48 | // Vector3.Angle always returns a positive number.
49 | // If pointB is above pointA, then angle needs to be negative.
50 | if (pointA.y > pointB.y) { angle = -angle; }
51 |
52 | // Use ScaleAroundPivot to adjust the size of the line.
53 | // We could do this when we draw the texture, but by scaling it here we can use
54 | // non-integer values for the width and length (such as sub 1 pixel widths).
55 | // Note that the pivot point is at +.5 from pointA.y, this is so that the width of the line
56 | // is centered on the origin at pointA.
57 | GUIUtility.ScaleAroundPivot(new Vector2((pointB - pointA).magnitude, width), new Vector2(pointA.x, pointA.y + 0.5f));
58 |
59 | // Set the rotation for the line.
60 | // The angle was calculated with pointA as the origin.
61 | GUIUtility.RotateAroundPivot(angle, pointA);
62 |
63 | // Finally, draw the actual line.
64 | // We're really only drawing a 1x1 texture from pointA.
65 | // The matrix operations done with ScaleAroundPivot and RotateAroundPivot will make this
66 | // render with the proper width, length, and angle.
67 | GUI.DrawTexture(new Rect(pointA.x, pointA.y, 1, 1), lineTex);
68 |
69 | // We're done. Restore the GUI matrix and GUI color to whatever they were before.
70 | GUI.matrix = matrix;
71 | GUI.color = savedColor;
72 | }
73 | }
--------------------------------------------------------------------------------
/Assets/Scripts/GuiDraw.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 027a04a822b40234e85c2e86b7ff14b8
3 | timeCreated: 1497993312
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/FpsController.unitypackage:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/FpsController.unitypackage
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 atil
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Packages/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "com.prenominal.realtimecsg": "https://github.com/LogicalError/realtime-CSG-for-unity.git",
4 | "com.unity.2d.sprite": "1.0.0",
5 | "com.unity.2d.tilemap": "1.0.0",
6 | "com.unity.ads": "3.7.5",
7 | "com.unity.analytics": "3.6.12",
8 | "com.unity.collab-proxy": "1.17.7",
9 | "com.unity.ide.rider": "3.0.18",
10 | "com.unity.ide.visualstudio": "2.0.17",
11 | "com.unity.ide.vscode": "1.2.5",
12 | "com.unity.purchasing": "4.5.2",
13 | "com.unity.test-framework": "1.1.31",
14 | "com.unity.textmeshpro": "3.0.6",
15 | "com.unity.timeline": "1.6.4",
16 | "com.unity.ugui": "1.0.0",
17 | "com.unity.xr.legacyinputhelpers": "2.1.10",
18 | "com.unity.modules.ai": "1.0.0",
19 | "com.unity.modules.androidjni": "1.0.0",
20 | "com.unity.modules.animation": "1.0.0",
21 | "com.unity.modules.assetbundle": "1.0.0",
22 | "com.unity.modules.audio": "1.0.0",
23 | "com.unity.modules.cloth": "1.0.0",
24 | "com.unity.modules.director": "1.0.0",
25 | "com.unity.modules.imageconversion": "1.0.0",
26 | "com.unity.modules.imgui": "1.0.0",
27 | "com.unity.modules.jsonserialize": "1.0.0",
28 | "com.unity.modules.particlesystem": "1.0.0",
29 | "com.unity.modules.physics": "1.0.0",
30 | "com.unity.modules.physics2d": "1.0.0",
31 | "com.unity.modules.screencapture": "1.0.0",
32 | "com.unity.modules.terrain": "1.0.0",
33 | "com.unity.modules.terrainphysics": "1.0.0",
34 | "com.unity.modules.tilemap": "1.0.0",
35 | "com.unity.modules.ui": "1.0.0",
36 | "com.unity.modules.uielements": "1.0.0",
37 | "com.unity.modules.umbra": "1.0.0",
38 | "com.unity.modules.unityanalytics": "1.0.0",
39 | "com.unity.modules.unitywebrequest": "1.0.0",
40 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0",
41 | "com.unity.modules.unitywebrequestaudio": "1.0.0",
42 | "com.unity.modules.unitywebrequesttexture": "1.0.0",
43 | "com.unity.modules.unitywebrequestwww": "1.0.0",
44 | "com.unity.modules.vehicles": "1.0.0",
45 | "com.unity.modules.video": "1.0.0",
46 | "com.unity.modules.vr": "1.0.0",
47 | "com.unity.modules.wind": "1.0.0",
48 | "com.unity.modules.xr": "1.0.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ProjectSettings/AudioManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/AudioManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/ClusterInputManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/ClusterInputManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/DynamicsManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/DynamicsManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/EditorBuildSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/EditorBuildSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/EditorSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/EditorSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/GraphicsSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/GraphicsSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/InputManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/InputManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/MemorySettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!387306366 &1
4 | MemorySettings:
5 | m_ObjectHideFlags: 0
6 | m_EditorMemorySettings:
7 | m_MainAllocatorBlockSize: -1
8 | m_ThreadAllocatorBlockSize: -1
9 | m_MainGfxBlockSize: -1
10 | m_ThreadGfxBlockSize: -1
11 | m_CacheBlockSize: -1
12 | m_TypetreeBlockSize: -1
13 | m_ProfilerBlockSize: -1
14 | m_ProfilerEditorBlockSize: -1
15 | m_BucketAllocatorGranularity: -1
16 | m_BucketAllocatorBucketsCount: -1
17 | m_BucketAllocatorBlockSize: -1
18 | m_BucketAllocatorBlockCount: -1
19 | m_ProfilerBucketAllocatorGranularity: -1
20 | m_ProfilerBucketAllocatorBucketsCount: -1
21 | m_ProfilerBucketAllocatorBlockSize: -1
22 | m_ProfilerBucketAllocatorBlockCount: -1
23 | m_TempAllocatorSizeMain: -1
24 | m_JobTempAllocatorBlockSize: -1
25 | m_BackgroundJobTempAllocatorBlockSize: -1
26 | m_JobTempAllocatorReducedBlockSize: -1
27 | m_TempAllocatorSizeGIBakingWorker: -1
28 | m_TempAllocatorSizeNavMeshWorker: -1
29 | m_TempAllocatorSizeAudioWorker: -1
30 | m_TempAllocatorSizeCloudWorker: -1
31 | m_TempAllocatorSizeGfx: -1
32 | m_TempAllocatorSizeJobWorker: -1
33 | m_TempAllocatorSizeBackgroundWorker: -1
34 | m_TempAllocatorSizePreloadManager: -1
35 | m_PlatformMemorySettings: {}
36 |
--------------------------------------------------------------------------------
/ProjectSettings/NavMeshAreas.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/NavMeshAreas.asset
--------------------------------------------------------------------------------
/ProjectSettings/NetworkManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/NetworkManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/PackageManagerSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!114 &1
4 | MonoBehaviour:
5 | m_ObjectHideFlags: 61
6 | m_CorrespondingSourceObject: {fileID: 0}
7 | m_PrefabInstance: {fileID: 0}
8 | m_PrefabAsset: {fileID: 0}
9 | m_GameObject: {fileID: 0}
10 | m_Enabled: 1
11 | m_EditorHideFlags: 0
12 | m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0}
13 | m_Name:
14 | m_EditorClassIdentifier:
15 | m_EnablePreReleasePackages: 0
16 | m_EnablePackageDependencies: 0
17 | m_AdvancedSettingsExpanded: 1
18 | m_ScopedRegistriesSettingsExpanded: 1
19 | m_SeeAllPackageVersions: 0
20 | oneTimeWarningShown: 0
21 | m_Registries:
22 | - m_Id: main
23 | m_Name:
24 | m_Url: https://packages.unity.com
25 | m_Scopes: []
26 | m_IsDefault: 1
27 | m_Capabilities: 7
28 | m_UserSelectedRegistryName:
29 | m_UserAddingNewScopedRegistry: 0
30 | m_RegistryInfoDraft:
31 | m_Modified: 0
32 | m_ErrorMessage:
33 | m_UserModificationsInstanceId: -852
34 | m_OriginalInstanceId: -854
35 | m_LoadAssets: 0
36 |
--------------------------------------------------------------------------------
/ProjectSettings/Physics2DSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/Physics2DSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/PresetManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/PresetManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/ProjectSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/ProjectSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/ProjectVersion.txt:
--------------------------------------------------------------------------------
1 | m_EditorVersion: 2021.3.17f1
2 | m_EditorVersionWithRevision: 2021.3.17f1 (3e8111cac19d)
3 |
--------------------------------------------------------------------------------
/ProjectSettings/QualitySettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/QualitySettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/SceneTemplateSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "templatePinStates": [],
3 | "dependencyTypeInfos": [
4 | {
5 | "userAdded": false,
6 | "type": "UnityEngine.AnimationClip",
7 | "ignore": false,
8 | "defaultInstantiationMode": 0,
9 | "supportsModification": true
10 | },
11 | {
12 | "userAdded": false,
13 | "type": "UnityEditor.Animations.AnimatorController",
14 | "ignore": false,
15 | "defaultInstantiationMode": 0,
16 | "supportsModification": true
17 | },
18 | {
19 | "userAdded": false,
20 | "type": "UnityEngine.AnimatorOverrideController",
21 | "ignore": false,
22 | "defaultInstantiationMode": 0,
23 | "supportsModification": true
24 | },
25 | {
26 | "userAdded": false,
27 | "type": "UnityEditor.Audio.AudioMixerController",
28 | "ignore": false,
29 | "defaultInstantiationMode": 0,
30 | "supportsModification": true
31 | },
32 | {
33 | "userAdded": false,
34 | "type": "UnityEngine.ComputeShader",
35 | "ignore": true,
36 | "defaultInstantiationMode": 1,
37 | "supportsModification": true
38 | },
39 | {
40 | "userAdded": false,
41 | "type": "UnityEngine.Cubemap",
42 | "ignore": false,
43 | "defaultInstantiationMode": 0,
44 | "supportsModification": true
45 | },
46 | {
47 | "userAdded": false,
48 | "type": "UnityEngine.GameObject",
49 | "ignore": false,
50 | "defaultInstantiationMode": 0,
51 | "supportsModification": true
52 | },
53 | {
54 | "userAdded": false,
55 | "type": "UnityEditor.LightingDataAsset",
56 | "ignore": false,
57 | "defaultInstantiationMode": 0,
58 | "supportsModification": false
59 | },
60 | {
61 | "userAdded": false,
62 | "type": "UnityEngine.LightingSettings",
63 | "ignore": false,
64 | "defaultInstantiationMode": 0,
65 | "supportsModification": true
66 | },
67 | {
68 | "userAdded": false,
69 | "type": "UnityEngine.Material",
70 | "ignore": false,
71 | "defaultInstantiationMode": 0,
72 | "supportsModification": true
73 | },
74 | {
75 | "userAdded": false,
76 | "type": "UnityEditor.MonoScript",
77 | "ignore": true,
78 | "defaultInstantiationMode": 1,
79 | "supportsModification": true
80 | },
81 | {
82 | "userAdded": false,
83 | "type": "UnityEngine.PhysicMaterial",
84 | "ignore": false,
85 | "defaultInstantiationMode": 0,
86 | "supportsModification": true
87 | },
88 | {
89 | "userAdded": false,
90 | "type": "UnityEngine.PhysicsMaterial2D",
91 | "ignore": false,
92 | "defaultInstantiationMode": 0,
93 | "supportsModification": true
94 | },
95 | {
96 | "userAdded": false,
97 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile",
98 | "ignore": false,
99 | "defaultInstantiationMode": 0,
100 | "supportsModification": true
101 | },
102 | {
103 | "userAdded": false,
104 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources",
105 | "ignore": false,
106 | "defaultInstantiationMode": 0,
107 | "supportsModification": true
108 | },
109 | {
110 | "userAdded": false,
111 | "type": "UnityEngine.Rendering.VolumeProfile",
112 | "ignore": false,
113 | "defaultInstantiationMode": 0,
114 | "supportsModification": true
115 | },
116 | {
117 | "userAdded": false,
118 | "type": "UnityEditor.SceneAsset",
119 | "ignore": false,
120 | "defaultInstantiationMode": 0,
121 | "supportsModification": false
122 | },
123 | {
124 | "userAdded": false,
125 | "type": "UnityEngine.Shader",
126 | "ignore": true,
127 | "defaultInstantiationMode": 1,
128 | "supportsModification": true
129 | },
130 | {
131 | "userAdded": false,
132 | "type": "UnityEngine.ShaderVariantCollection",
133 | "ignore": true,
134 | "defaultInstantiationMode": 1,
135 | "supportsModification": true
136 | },
137 | {
138 | "userAdded": false,
139 | "type": "UnityEngine.Texture",
140 | "ignore": false,
141 | "defaultInstantiationMode": 0,
142 | "supportsModification": true
143 | },
144 | {
145 | "userAdded": false,
146 | "type": "UnityEngine.Texture2D",
147 | "ignore": false,
148 | "defaultInstantiationMode": 0,
149 | "supportsModification": true
150 | },
151 | {
152 | "userAdded": false,
153 | "type": "UnityEngine.Timeline.TimelineAsset",
154 | "ignore": false,
155 | "defaultInstantiationMode": 0,
156 | "supportsModification": true
157 | }
158 | ],
159 | "defaultDependencyTypeInfo": {
160 | "userAdded": false,
161 | "type": "",
162 | "ignore": false,
163 | "defaultInstantiationMode": 1,
164 | "supportsModification": true
165 | },
166 | "newSceneOverride": 0
167 | }
--------------------------------------------------------------------------------
/ProjectSettings/TagManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/TagManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/TimeManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/TimeManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/UnityConnectSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/atil/fpscontroller/2cfb4c7d86d56014324529c0ee5c50620d6ce58b/ProjectSettings/UnityConnectSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/VFXManager.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!937362698 &1
4 | VFXManager:
5 | m_ObjectHideFlags: 0
6 | m_IndirectShader: {fileID: 0}
7 | m_CopyBufferShader: {fileID: 0}
8 | m_SortShader: {fileID: 0}
9 | m_StripUpdateShader: {fileID: 0}
10 | m_RenderPipeSettingsPath:
11 | m_FixedTimeStep: 0.016666668
12 | m_MaxDeltaTime: 0.05
13 | m_CompiledVersion: 0
14 | m_RuntimeVersion: 0
15 | m_RuntimeResources: {fileID: 0}
16 |
--------------------------------------------------------------------------------
/ProjectSettings/VersionControlSettings.asset:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!890905787 &1
4 | VersionControlSettings:
5 | m_ObjectHideFlags: 0
6 | m_Mode: Visible Meta Files
7 | m_CollabEditorSettings:
8 | inProgressEnabled: 1
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # First Person Controller for Unity
2 |
3 | This Q3-inspired first person controller is made with two goals in mind:
4 |
5 | - Arcade feeling facilitates strafejumping / bunnyhopping, allowing easy-to-play-hard-to-master movement. It uses similar movement logic used in quake / halflife games to achieve oldschool feel.
6 |
7 | - Avoid using CharacterController and make every step of the logic as transparent as possible.
8 |
9 | ## Quick start:
10 |
11 | - Unpack [the package](FpsController.unitypackage)
12 |
13 | - Add a layer "PlayerCollider" and set the player's collider to be on that layer
14 |
15 | - Add that layer to `FpsController.ExcludedLayers` on the prefab. Collision and grounded checks will ignore these layers
16 |
17 | ## References:
18 |
19 | [Quake3-movement-unity](https://github.com/Zinglish/quake3-movement-unity3d/blob/master/CPMPlayer.cs)
20 |
21 | [Quake3 source](https://github.com/id-Software/Quake-III-Arena/blob/dbe4ddb10315479fc00086f08e25d968b4b43c49/code/game/bg_pmove.c)
22 |
23 | [Half Life 1 SDK source](https://github.com/ValveSoftware/halflife/blob/5d761709a31ce1e71488f2668321de05f791b405/pm_shared/pm_shared.c#L2941)
24 |
25 | [Bunnyhopping from the Programmer's Perspective](http://flafla2.github.io/2015/02/14/bunnyhop.html)
--------------------------------------------------------------------------------