├── LICENSE
├── LICENSE.meta
├── Quake3Movement.meta
├── Quake3Movement
├── Demo.meta
├── Demo
│ ├── DemoScene.unity
│ ├── DemoScene.unity.meta
│ ├── DevGrid.mat
│ ├── DevGrid.mat.meta
│ ├── GridTexture.tga
│ └── GridTexture.tga.meta
├── Scripts.meta
└── Scripts
│ ├── MouseLook.cs
│ ├── MouseLook.cs.meta
│ ├── Q3PlayerController.cs
│ ├── Q3PlayerController.cs.meta
│ ├── Q3PlayerDebug.cs
│ └── Q3PlayerDebug.cs.meta
├── readme.md
└── readme.md.meta
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/LICENSE.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 82a42cde4d3c318498bed29c68252c77
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Quake3Movement.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b9104c4cd6043c94bbd7afe101365143
3 | folderAsset: yes
4 | timeCreated: 1518912078
5 | licenseType: Free
6 | DefaultImporter:
7 | externalObjects: {}
8 | userData:
9 | assetBundleName:
10 | assetBundleVariant:
11 |
--------------------------------------------------------------------------------
/Quake3Movement/Demo.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2c0c7a5c82a49a4418c54959811c2193
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Quake3Movement/Demo/DemoScene.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: bca40ad4dce30034d93f9cdb2a5aea0b
3 | timeCreated: 1518911892
4 | licenseType: Free
5 | DefaultImporter:
6 | externalObjects: {}
7 | userData:
8 | assetBundleName:
9 | assetBundleVariant:
10 |
--------------------------------------------------------------------------------
/Quake3Movement/Demo/DevGrid.mat:
--------------------------------------------------------------------------------
1 | %YAML 1.1
2 | %TAG !u! tag:unity3d.com,2011:
3 | --- !u!21 &2100000
4 | Material:
5 | serializedVersion: 6
6 | m_ObjectHideFlags: 0
7 | m_PrefabParentObject: {fileID: 0}
8 | m_PrefabInternal: {fileID: 0}
9 | m_Name: DevGrid
10 | m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
11 | m_ShaderKeywords:
12 | m_LightmapFlags: 4
13 | m_EnableInstancingVariants: 0
14 | m_DoubleSidedGI: 0
15 | m_CustomRenderQueue: -1
16 | stringTagMap: {}
17 | disabledShaderPasses: []
18 | m_SavedProperties:
19 | serializedVersion: 3
20 | m_TexEnvs:
21 | - _BumpMap:
22 | m_Texture: {fileID: 0}
23 | m_Scale: {x: 1, y: 1}
24 | m_Offset: {x: 0, y: 0}
25 | - _DetailAlbedoMap:
26 | m_Texture: {fileID: 0}
27 | m_Scale: {x: 0, y: 0}
28 | m_Offset: {x: 0, y: 0}
29 | - _DetailMask:
30 | m_Texture: {fileID: 0}
31 | m_Scale: {x: 1, y: 1}
32 | m_Offset: {x: 0, y: 0}
33 | - _DetailNormalMap:
34 | m_Texture: {fileID: 0}
35 | m_Scale: {x: 1, y: 1}
36 | m_Offset: {x: 0, y: 0}
37 | - _EmissionMap:
38 | m_Texture: {fileID: 0}
39 | m_Scale: {x: 128, y: 128}
40 | m_Offset: {x: 0, y: 0}
41 | - _MainTex:
42 | m_Texture: {fileID: 2800000, guid: d26bb6c8aebf04b4f8014c4990146141, type: 3}
43 | m_Scale: {x: 128, y: 128}
44 | m_Offset: {x: 0, y: 0}
45 | - _MetallicGlossMap:
46 | m_Texture: {fileID: 0}
47 | m_Scale: {x: 1, y: 1}
48 | m_Offset: {x: 0, y: 0}
49 | - _OcclusionMap:
50 | m_Texture: {fileID: 0}
51 | m_Scale: {x: 1, y: 1}
52 | m_Offset: {x: 0, y: 0}
53 | - _ParallaxMap:
54 | m_Texture: {fileID: 0}
55 | m_Scale: {x: 1, y: 1}
56 | m_Offset: {x: 0, y: 0}
57 | m_Floats:
58 | - _BumpScale: 1
59 | - _Cutoff: 0.5
60 | - _DetailNormalMapScale: 1
61 | - _DstBlend: 0
62 | - _GlossMapScale: 1
63 | - _Glossiness: 0
64 | - _GlossyReflections: 1
65 | - _Metallic: 0
66 | - _Mode: 0
67 | - _OcclusionStrength: 1
68 | - _Parallax: 0.02
69 | - _SmoothnessTextureChannel: 0
70 | - _SpecularHighlights: 1
71 | - _SrcBlend: 1
72 | - _UVSec: 0
73 | - _ZWrite: 1
74 | m_Colors:
75 | - _Color: {r: 1, g: 1, b: 1, a: 1}
76 | - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
77 |
--------------------------------------------------------------------------------
/Quake3Movement/Demo/DevGrid.mat.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 77b53e8b78caa8545b9c671e23aba398
3 | timeCreated: 1518911743
4 | licenseType: Free
5 | NativeFormatImporter:
6 | externalObjects: {}
7 | mainObjectFileID: 2100000
8 | userData:
9 | assetBundleName:
10 | assetBundleVariant:
11 |
--------------------------------------------------------------------------------
/Quake3Movement/Demo/GridTexture.tga:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IsaiahKelly/quake3-movement-for-unity/71f8c72a929e13adc477208770a5e61e7a28c421/Quake3Movement/Demo/GridTexture.tga
--------------------------------------------------------------------------------
/Quake3Movement/Demo/GridTexture.tga.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d26bb6c8aebf04b4f8014c4990146141
3 | timeCreated: 1518911731
4 | licenseType: Free
5 | TextureImporter:
6 | fileIDToRecycleName: {}
7 | externalObjects: {}
8 | serializedVersion: 4
9 | mipmaps:
10 | mipMapMode: 0
11 | enableMipMap: 1
12 | sRGBTexture: 1
13 | linearTexture: 0
14 | fadeOut: 0
15 | borderMipMap: 0
16 | mipMapsPreserveCoverage: 0
17 | alphaTestReferenceValue: 0.5
18 | mipMapFadeDistanceStart: 1
19 | mipMapFadeDistanceEnd: 3
20 | bumpmap:
21 | convertToNormalMap: 0
22 | externalNormalMap: 0
23 | heightScale: 0.25
24 | normalMapFilter: 0
25 | isReadable: 0
26 | grayScaleToAlpha: 0
27 | generateCubemap: 6
28 | cubemapConvolution: 0
29 | seamlessCubemap: 0
30 | textureFormat: 1
31 | maxTextureSize: 2048
32 | textureSettings:
33 | serializedVersion: 2
34 | filterMode: -1
35 | aniso: -1
36 | mipBias: -1
37 | wrapU: -1
38 | wrapV: -1
39 | wrapW: -1
40 | nPOTScale: 1
41 | lightmap: 0
42 | compressionQuality: 50
43 | spriteMode: 0
44 | spriteExtrude: 1
45 | spriteMeshType: 1
46 | alignment: 0
47 | spritePivot: {x: 0.5, y: 0.5}
48 | spriteBorder: {x: 0, y: 0, z: 0, w: 0}
49 | spritePixelsToUnits: 100
50 | alphaUsage: 1
51 | alphaIsTransparency: 0
52 | spriteTessellationDetail: -1
53 | textureType: 0
54 | textureShape: 1
55 | maxTextureSizeSet: 0
56 | compressionQualitySet: 0
57 | textureFormatSet: 0
58 | platformSettings:
59 | - buildTarget: DefaultTexturePlatform
60 | maxTextureSize: 2048
61 | resizeAlgorithm: 0
62 | textureFormat: -1
63 | textureCompression: 1
64 | compressionQuality: 50
65 | crunchedCompression: 0
66 | allowsAlphaSplitting: 0
67 | overridden: 0
68 | androidETC2FallbackOverride: 0
69 | spriteSheet:
70 | serializedVersion: 2
71 | sprites: []
72 | outline: []
73 | physicsShape: []
74 | spritePackingTag:
75 | userData:
76 | assetBundleName:
77 | assetBundleVariant:
78 |
--------------------------------------------------------------------------------
/Quake3Movement/Scripts.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 9cd76b6cde812544e9cfc625a68ff15e
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Quake3Movement/Scripts/MouseLook.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace Q3Movement
5 | {
6 | ///
7 | /// Custom script based on the version from the Standard Assets.
8 | ///
9 | [Serializable]
10 | public class MouseLook
11 | {
12 | [SerializeField] private float m_XSensitivity = 2f;
13 | [SerializeField] private float m_YSensitivity = 2f;
14 | [SerializeField] private bool m_ClampVerticalRotation = true;
15 | [SerializeField] private float m_MinimumX = -90F;
16 | [SerializeField] private float m_MaximumX = 90F;
17 | [SerializeField] private bool m_Smooth = false;
18 | [SerializeField] private float m_SmoothTime = 5f;
19 | [SerializeField] private bool m_LockCursor = true;
20 |
21 | private Quaternion m_CharacterTargetRot;
22 | private Quaternion m_CameraTargetRot;
23 | private bool m_cursorIsLocked = true;
24 |
25 | public void Init(Transform character, Transform camera)
26 | {
27 | m_CharacterTargetRot = character.localRotation;
28 | m_CameraTargetRot = camera.localRotation;
29 | }
30 |
31 | public void LookRotation(Transform character, Transform camera)
32 | {
33 | float yRot = Input.GetAxis("Mouse X") * m_XSensitivity;
34 | float xRot = Input.GetAxis("Mouse Y") * m_YSensitivity;
35 |
36 | m_CharacterTargetRot *= Quaternion.Euler(0f, yRot, 0f);
37 | m_CameraTargetRot *= Quaternion.Euler(-xRot, 0f, 0f);
38 |
39 | if (m_ClampVerticalRotation)
40 | {
41 | m_CameraTargetRot = ClampRotationAroundXAxis(m_CameraTargetRot);
42 | }
43 |
44 | if (m_Smooth)
45 | {
46 | character.localRotation = Quaternion.Slerp(character.localRotation, m_CharacterTargetRot,
47 | m_SmoothTime * Time.deltaTime);
48 | camera.localRotation = Quaternion.Slerp(camera.localRotation, m_CameraTargetRot,
49 | m_SmoothTime * Time.deltaTime);
50 | }
51 | else
52 | {
53 | character.localRotation = m_CharacterTargetRot;
54 | camera.localRotation = m_CameraTargetRot;
55 | }
56 |
57 | UpdateCursorLock();
58 | }
59 |
60 | public void SetCursorLock(bool value)
61 | {
62 | m_LockCursor = value;
63 | if (!m_LockCursor)
64 | {//we force unlock the cursor if the user disable the cursor locking helper
65 | Cursor.lockState = CursorLockMode.None;
66 | Cursor.visible = true;
67 | }
68 | }
69 |
70 | public void UpdateCursorLock()
71 | {
72 | //if the user set "lockCursor" we check & properly lock the cursos
73 | if (m_LockCursor)
74 | {
75 | InternalLockUpdate();
76 | }
77 | }
78 |
79 | private void InternalLockUpdate()
80 | {
81 | if (Input.GetKeyUp(KeyCode.Escape))
82 | {
83 | m_cursorIsLocked = false;
84 | }
85 | else if (Input.GetMouseButtonUp(0))
86 | {
87 | m_cursorIsLocked = true;
88 | }
89 |
90 | if (m_cursorIsLocked)
91 | {
92 | Cursor.lockState = CursorLockMode.Locked;
93 | Cursor.visible = false;
94 | }
95 | else if (!m_cursorIsLocked)
96 | {
97 | Cursor.lockState = CursorLockMode.None;
98 | Cursor.visible = true;
99 | }
100 | }
101 |
102 | private Quaternion ClampRotationAroundXAxis(Quaternion q)
103 | {
104 | q.x /= q.w;
105 | q.y /= q.w;
106 | q.z /= q.w;
107 | q.w = 1.0f;
108 |
109 | float angleX = 2.0f * Mathf.Rad2Deg * Mathf.Atan(q.x);
110 |
111 | angleX = Mathf.Clamp(angleX, m_MinimumX, m_MaximumX);
112 |
113 | q.x = Mathf.Tan(0.5f * Mathf.Deg2Rad * angleX);
114 |
115 | return q;
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Quake3Movement/Scripts/MouseLook.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3feaa5f827897f64997c0dc6960e1c5f
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/Quake3Movement/Scripts/Q3PlayerController.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Q3Movement
4 | {
5 | ///
6 | /// This script handles Quake III CPM(A) mod style player movement logic.
7 | ///
8 | [RequireComponent(typeof(CharacterController))]
9 | public class Q3PlayerController : MonoBehaviour
10 | {
11 | [System.Serializable]
12 | public class MovementSettings
13 | {
14 | public float MaxSpeed;
15 | public float Acceleration;
16 | public float Deceleration;
17 |
18 | public MovementSettings(float maxSpeed, float accel, float decel)
19 | {
20 | MaxSpeed = maxSpeed;
21 | Acceleration = accel;
22 | Deceleration = decel;
23 | }
24 | }
25 |
26 | [Header("Aiming")]
27 | [SerializeField] private Camera m_Camera;
28 | [SerializeField] private MouseLook m_MouseLook = new MouseLook();
29 |
30 | [Header("Movement")]
31 | [SerializeField] private float m_Friction = 6;
32 | [SerializeField] private float m_Gravity = 20;
33 | [SerializeField] private float m_JumpForce = 8;
34 | [Tooltip("Automatically jump when holding jump button")]
35 | [SerializeField] private bool m_AutoBunnyHop = false;
36 | [Tooltip("How precise air control is")]
37 | [SerializeField] private float m_AirControl = 0.3f;
38 | [SerializeField] private MovementSettings m_GroundSettings = new MovementSettings(7, 14, 10);
39 | [SerializeField] private MovementSettings m_AirSettings = new MovementSettings(7, 2, 2);
40 | [SerializeField] private MovementSettings m_StrafeSettings = new MovementSettings(1, 50, 50);
41 |
42 | ///
43 | /// Returns player's current speed.
44 | ///
45 | public float Speed { get { return m_Character.velocity.magnitude; } }
46 |
47 | private CharacterController m_Character;
48 | private Vector3 m_MoveDirectionNorm = Vector3.zero;
49 | private Vector3 m_PlayerVelocity = Vector3.zero;
50 |
51 | // Used to queue the next jump just before hitting the ground.
52 | private bool m_JumpQueued = false;
53 |
54 | // Used to display real time friction values.
55 | private float m_PlayerFriction = 0;
56 |
57 | private Vector3 m_MoveInput;
58 | private Transform m_Tran;
59 | private Transform m_CamTran;
60 |
61 | private void Start()
62 | {
63 | m_Tran = transform;
64 | m_Character = GetComponent();
65 |
66 | if (!m_Camera)
67 | m_Camera = Camera.main;
68 |
69 | m_CamTran = m_Camera.transform;
70 | m_MouseLook.Init(m_Tran, m_CamTran);
71 | }
72 |
73 | private void Update()
74 | {
75 | m_MoveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
76 | m_MouseLook.UpdateCursorLock();
77 | QueueJump();
78 |
79 | // Set movement state.
80 | if (m_Character.isGrounded)
81 | {
82 | GroundMove();
83 | }
84 | else
85 | {
86 | AirMove();
87 | }
88 |
89 | // Rotate the character and camera.
90 | m_MouseLook.LookRotation(m_Tran, m_CamTran);
91 |
92 | // Move the character.
93 | m_Character.Move(m_PlayerVelocity * Time.deltaTime);
94 | }
95 |
96 | // Queues the next jump.
97 | private void QueueJump()
98 | {
99 | if (m_AutoBunnyHop)
100 | {
101 | m_JumpQueued = Input.GetButton("Jump");
102 | return;
103 | }
104 |
105 | if (Input.GetButtonDown("Jump") && !m_JumpQueued)
106 | {
107 | m_JumpQueued = true;
108 | }
109 |
110 | if (Input.GetButtonUp("Jump"))
111 | {
112 | m_JumpQueued = false;
113 | }
114 | }
115 |
116 | // Handle air movement.
117 | private void AirMove()
118 | {
119 | float accel;
120 |
121 | var wishdir = new Vector3(m_MoveInput.x, 0, m_MoveInput.z);
122 | wishdir = m_Tran.TransformDirection(wishdir);
123 |
124 | float wishspeed = wishdir.magnitude;
125 | wishspeed *= m_AirSettings.MaxSpeed;
126 |
127 | wishdir.Normalize();
128 | m_MoveDirectionNorm = wishdir;
129 |
130 | // CPM Air control.
131 | float wishspeed2 = wishspeed;
132 | if (Vector3.Dot(m_PlayerVelocity, wishdir) < 0)
133 | {
134 | accel = m_AirSettings.Deceleration;
135 | }
136 | else
137 | {
138 | accel = m_AirSettings.Acceleration;
139 | }
140 |
141 | // If the player is ONLY strafing left or right
142 | if (m_MoveInput.z == 0 && m_MoveInput.x != 0)
143 | {
144 | if (wishspeed > m_StrafeSettings.MaxSpeed)
145 | {
146 | wishspeed = m_StrafeSettings.MaxSpeed;
147 | }
148 |
149 | accel = m_StrafeSettings.Acceleration;
150 | }
151 |
152 | Accelerate(wishdir, wishspeed, accel);
153 | if (m_AirControl > 0)
154 | {
155 | AirControl(wishdir, wishspeed2);
156 | }
157 |
158 | // Apply gravity
159 | m_PlayerVelocity.y -= m_Gravity * Time.deltaTime;
160 | }
161 |
162 | // Air control occurs when the player is in the air, it allows players to move side
163 | // to side much faster rather than being 'sluggish' when it comes to cornering.
164 | private void AirControl(Vector3 targetDir, float targetSpeed)
165 | {
166 | // Only control air movement when moving forward or backward.
167 | if (Mathf.Abs(m_MoveInput.z) < 0.001 || Mathf.Abs(targetSpeed) < 0.001)
168 | {
169 | return;
170 | }
171 |
172 | float zSpeed = m_PlayerVelocity.y;
173 | m_PlayerVelocity.y = 0;
174 | /* Next two lines are equivalent to idTech's VectorNormalize() */
175 | float speed = m_PlayerVelocity.magnitude;
176 | m_PlayerVelocity.Normalize();
177 |
178 | float dot = Vector3.Dot(m_PlayerVelocity, targetDir);
179 | float k = 32;
180 | k *= m_AirControl * dot * dot * Time.deltaTime;
181 |
182 | // Change direction while slowing down.
183 | if (dot > 0)
184 | {
185 | m_PlayerVelocity.x *= speed + targetDir.x * k;
186 | m_PlayerVelocity.y *= speed + targetDir.y * k;
187 | m_PlayerVelocity.z *= speed + targetDir.z * k;
188 |
189 | m_PlayerVelocity.Normalize();
190 | m_MoveDirectionNorm = m_PlayerVelocity;
191 | }
192 |
193 | m_PlayerVelocity.x *= speed;
194 | m_PlayerVelocity.y = zSpeed; // Note this line
195 | m_PlayerVelocity.z *= speed;
196 | }
197 |
198 | // Handle ground movement.
199 | private void GroundMove()
200 | {
201 | // Do not apply friction if the player is queueing up the next jump
202 | if (!m_JumpQueued)
203 | {
204 | ApplyFriction(1.0f);
205 | }
206 | else
207 | {
208 | ApplyFriction(0);
209 | }
210 |
211 | var wishdir = new Vector3(m_MoveInput.x, 0, m_MoveInput.z);
212 | wishdir = m_Tran.TransformDirection(wishdir);
213 | wishdir.Normalize();
214 | m_MoveDirectionNorm = wishdir;
215 |
216 | var wishspeed = wishdir.magnitude;
217 | wishspeed *= m_GroundSettings.MaxSpeed;
218 |
219 | Accelerate(wishdir, wishspeed, m_GroundSettings.Acceleration);
220 |
221 | // Reset the gravity velocity
222 | m_PlayerVelocity.y = -m_Gravity * Time.deltaTime;
223 |
224 | if (m_JumpQueued)
225 | {
226 | m_PlayerVelocity.y = m_JumpForce;
227 | m_JumpQueued = false;
228 | }
229 | }
230 |
231 | private void ApplyFriction(float t)
232 | {
233 | // Equivalent to VectorCopy();
234 | Vector3 vec = m_PlayerVelocity;
235 | vec.y = 0;
236 | float speed = vec.magnitude;
237 | float drop = 0;
238 |
239 | // Only apply friction when grounded.
240 | if (m_Character.isGrounded)
241 | {
242 | float control = speed < m_GroundSettings.Deceleration ? m_GroundSettings.Deceleration : speed;
243 | drop = control * m_Friction * Time.deltaTime * t;
244 | }
245 |
246 | float newSpeed = speed - drop;
247 | m_PlayerFriction = newSpeed;
248 | if (newSpeed < 0)
249 | {
250 | newSpeed = 0;
251 | }
252 |
253 | if (speed > 0)
254 | {
255 | newSpeed /= speed;
256 | }
257 |
258 | m_PlayerVelocity.x *= newSpeed;
259 | // playerVelocity.y *= newSpeed;
260 | m_PlayerVelocity.z *= newSpeed;
261 | }
262 |
263 | // Calculates acceleration based on desired speed and direction.
264 | private void Accelerate(Vector3 targetDir, float targetSpeed, float accel)
265 | {
266 | float currentspeed = Vector3.Dot(m_PlayerVelocity, targetDir);
267 | float addspeed = targetSpeed - currentspeed;
268 | if (addspeed <= 0)
269 | {
270 | return;
271 | }
272 |
273 | float accelspeed = accel * Time.deltaTime * targetSpeed;
274 | if (accelspeed > addspeed)
275 | {
276 | accelspeed = addspeed;
277 | }
278 |
279 | m_PlayerVelocity.x += accelspeed * targetDir.x;
280 | m_PlayerVelocity.z += accelspeed * targetDir.z;
281 | }
282 | }
283 | }
--------------------------------------------------------------------------------
/Quake3Movement/Scripts/Q3PlayerController.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 689c7b4cc1dbae54cbfa41c0c84fb174
3 | timeCreated: 1518909934
4 | licenseType: Free
5 | MonoImporter:
6 | externalObjects: {}
7 | serializedVersion: 2
8 | defaultReferences: []
9 | executionOrder: 0
10 | icon: {instanceID: 0}
11 | userData:
12 | assetBundleName:
13 | assetBundleVariant:
14 |
--------------------------------------------------------------------------------
/Quake3Movement/Scripts/Q3PlayerDebug.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 |
3 | namespace Q3Movement
4 | {
5 | ///
6 | /// This script is used to display real-time statistics to help with tweaking and debugging.
7 | ///
8 | [RequireComponent(typeof(Q3PlayerController))]
9 | public class Q3PlayerDebug : MonoBehaviour
10 | {
11 | [Tooltip("How many times per second to update stats")]
12 | [SerializeField] private float m_RefreshRate = 4;
13 |
14 | private int m_FrameCount = 0;
15 | private float m_Time = 0;
16 | private float m_FPS = 0;
17 | private float m_TopSpeed = 0;
18 | private Q3PlayerController m_Player;
19 |
20 | private void Start()
21 | {
22 | m_Player = GetComponent();
23 | }
24 |
25 | private void LateUpdate()
26 | {
27 | // Calculate frames-per-second.
28 | m_FrameCount++;
29 | m_Time += Time.deltaTime;
30 | if (m_Time > 1.0 / m_RefreshRate)
31 | {
32 | m_FPS = Mathf.Round(m_FrameCount / m_Time);
33 | m_FrameCount = 0;
34 | m_Time -= 1.0f / m_RefreshRate;
35 | }
36 |
37 | // Calculate top velocity.
38 | if (m_Player.Speed > m_TopSpeed)
39 | {
40 | m_TopSpeed = m_Player.Speed;
41 | }
42 | }
43 |
44 | private void OnGUI()
45 | {
46 | GUI.Box(new Rect(0, 0, 130, 60),
47 | "FPS: " + m_FPS + "\n" +
48 | "Speed: " + Mathf.Round(m_Player.Speed * 100) / 100 + " (ups)\n" +
49 | "Top: " + Mathf.Round(m_TopSpeed * 100) / 100 + " (ups)");
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/Quake3Movement/Scripts/Q3PlayerDebug.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 56a75dc49fa305c4d87d2854fe203452
3 | timeCreated: 1518909804
4 | licenseType: Free
5 | MonoImporter:
6 | externalObjects: {}
7 | serializedVersion: 2
8 | defaultReferences: []
9 | executionOrder: 0
10 | icon: {instanceID: 0}
11 | userData:
12 | assetBundleName:
13 | assetBundleVariant:
14 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | Implementation of Quake III "vanilla" and Challenge ProMode Arena (CPMA) strafe jumping mechanics in the Unity engine.
2 |
3 | This is my own updated and enhanced fork of the original scripts created by [WiggleWizard](https://github.com/WiggleWizard). So most of the credit goes to him for porting it over. I am just trying to improve upon it some here.
4 |
5 | ## Notes:
6 |
7 | ### Coordinate System
8 | Quake uses a right-handed coordinate system while Unity uses a left-handed one. So coordinate values (X,Y,Z) have been swapped to reflect this difference.
9 |
10 | ### World Scale
11 | UPS (units per second) is measured in Unity units (meters) and not idTech units.
12 |
13 | ### Configuration
14 | Default script values emulate Quake III Arena movement with CPM(A) physics.
15 |
16 | ### Demo Assets
17 | Demo scene meshes were built with ProBuilder 4.4 so this package must be installed in your project for demo scene to function.
18 |
--------------------------------------------------------------------------------
/readme.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a235d40150426c94983e2bf2e4ef0db0
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------