@musashishao/agent-kit 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/.agent/agents/ai-asset-factory.md +700 -0
  2. package/.agent/agents/ai-audio-factory.md +503 -0
  3. package/.agent/agents/game-developer.md +4 -4
  4. package/.agent/agents/orchestrator.md +113 -3
  5. package/.agent/agents/project-planner.md +67 -0
  6. package/.agent/agents/unity-mobile-master.md +949 -0
  7. package/.agent/mcp/config/registry.json +65 -51
  8. package/.agent/mcp/servers/notebooklm/README.md +114 -0
  9. package/.agent/mcp/servers/notebooklm/package.json +35 -0
  10. package/.agent/mcp/servers/notebooklm/src/auth/chrome.ts +225 -0
  11. package/.agent/mcp/servers/notebooklm/src/auth/index.ts +1 -0
  12. package/.agent/mcp/servers/notebooklm/src/index.ts +516 -0
  13. package/.agent/mcp/servers/notebooklm/src/services/index.ts +3 -0
  14. package/.agent/mcp/servers/notebooklm/src/services/library.ts +217 -0
  15. package/.agent/mcp/servers/notebooklm/src/services/notebooklm.ts +380 -0
  16. package/.agent/mcp/servers/notebooklm/tsconfig.json +15 -0
  17. package/.agent/mcp-gateway/README.md +169 -20
  18. package/.agent/mcp-gateway/package.json +22 -7
  19. package/.agent/mcp-gateway/src/auth/index.ts +55 -0
  20. package/.agent/mcp-gateway/src/auth/middleware.ts +242 -0
  21. package/.agent/mcp-gateway/src/auth/oauth.ts +462 -0
  22. package/.agent/mcp-gateway/src/auth/scopes.ts +227 -0
  23. package/.agent/mcp-gateway/src/index.ts +252 -105
  24. package/.agent/mcp-gateway/src/observability/index.ts +5 -0
  25. package/.agent/mcp-gateway/src/observability/otel.ts +405 -0
  26. package/.agent/mcp-gateway/src/transports/index.ts +5 -0
  27. package/.agent/mcp-gateway/src/transports/streamableHttp.ts +235 -0
  28. package/.agent/rules/CODEX.md +89 -0
  29. package/.agent/rules/CODE_RULES.md +73 -0
  30. package/.agent/rules/GEMINI.md +25 -0
  31. package/.agent/rules/MEMORY_STATE.md +110 -0
  32. package/.agent/rules/REFERENCE.md +33 -141
  33. package/.agent/rules/REF_SKILLS.md +116 -0
  34. package/.agent/rules/REF_WORKFLOWS.md +81 -0
  35. package/.agent/scripts/ak_cli.py +106 -5
  36. package/.agent/scripts/memory_manager.py +48 -9
  37. package/.agent/skills/anti-hallucination/SKILL.md +295 -0
  38. package/.agent/skills/anti-hallucination/scripts/check_hallucination.py +299 -0
  39. package/.agent/skills/bifurcation-analysis/SKILL.md +56 -0
  40. package/.agent/skills/brainstorming/SKILL.md +80 -6
  41. package/.agent/skills/decision-memory/SKILL.md +317 -0
  42. package/.agent/skills/emergence-detector/SKILL.md +230 -0
  43. package/.agent/skills/emergence-detector/scripts/check_emergence.py +265 -0
  44. package/.agent/skills/explained-qa/SKILL.md +142 -0
  45. package/.agent/skills/explained-qa/game-terminology.md +214 -0
  46. package/.agent/skills/game-development/ai-dialogue-engine/SKILL.md +442 -0
  47. package/.agent/skills/game-development/ai-graphics-generator/SKILL.md +463 -0
  48. package/.agent/skills/game-development/ai-playtest-framework/SKILL.md +570 -0
  49. package/.agent/skills/game-development/camera-systems/SKILL.md +607 -0
  50. package/.agent/skills/game-development/card-battle-engine/SKILL.md +618 -0
  51. package/.agent/skills/game-development/character-controller-3d/SKILL.md +908 -0
  52. package/.agent/skills/game-development/cloud-save-sync/SKILL.md +527 -0
  53. package/.agent/skills/game-development/combat-system/SKILL.md +748 -0
  54. package/.agent/skills/game-development/compliance-rating/SKILL.md +277 -0
  55. package/.agent/skills/game-development/crossplatform-build/SKILL.md +386 -0
  56. package/.agent/skills/game-development/cultivation-progression/SKILL.md +520 -0
  57. package/.agent/skills/game-development/data-driven-balance/SKILL.md +535 -0
  58. package/.agent/skills/game-development/game-analytics-integrator/SKILL.md +410 -0
  59. package/.agent/skills/game-development/game-audio-advanced/SKILL.md +646 -0
  60. package/.agent/skills/game-development/game-economy-designer/SKILL.md +375 -0
  61. package/.agent/skills/game-development/game-marketing/SKILL.md +85 -0
  62. package/.agent/skills/game-development/game-state-manager/SKILL.md +883 -0
  63. package/.agent/skills/game-development/hybrid-game-spec/SKILL.md +220 -0
  64. package/.agent/skills/game-development/inventory-quest/SKILL.md +747 -0
  65. package/.agent/skills/game-development/liveops/SKILL.md +308 -0
  66. package/.agent/skills/game-development/localization/SKILL.md +286 -0
  67. package/.agent/skills/game-development/mobile-input-patterns/SKILL.md +343 -0
  68. package/.agent/skills/game-development/monetization-strategy/SKILL.md +94 -0
  69. package/.agent/skills/game-development/multiplayer-master/SKILL.md +727 -0
  70. package/.agent/skills/game-development/narrative-branching/SKILL.md +593 -0
  71. package/.agent/skills/game-development/procedural-level-ai/SKILL.md +367 -0
  72. package/.agent/skills/game-development/prototyping-rapid/SKILL.md +205 -0
  73. package/.agent/skills/game-development/spec-ecosystem/SKILL.md +155 -0
  74. package/.agent/skills/game-development/spec-ecosystem/decision-log-format.md +129 -0
  75. package/.agent/skills/game-development/spec-ecosystem/templates/PLAN-template.md +178 -0
  76. package/.agent/skills/game-development/spec-ecosystem/templates/SPEC-template.md +110 -0
  77. package/.agent/skills/game-development/spec-ecosystem/templates/TASKS-template.md +156 -0
  78. package/.agent/skills/game-development/survival-systems/SKILL.md +493 -0
  79. package/.agent/skills/game-development/testing-qa/SKILL.md +270 -0
  80. package/.agent/skills/game-development/unity-mobile-optimization/SKILL.md +271 -0
  81. package/.agent/skills/intent-capture/SKILL.md +65 -0
  82. package/.agent/skills/mcp-composition/SKILL.md +362 -0
  83. package/.agent/skills/mcp-observability/SKILL.md +323 -0
  84. package/.agent/skills/mcp-security/SKILL.md +314 -0
  85. package/.agent/skills/trust-spectrum/SKILL.md +291 -0
  86. package/.agent/skills/vibe-coding-guard/SKILL.md +328 -0
  87. package/.agent/templates/AGENTS.game.md +63 -0
  88. package/.agent/templates/docs/WORKFLOW_GUIDE.en.md +100 -0
  89. package/.agent/templates/docs/WORKFLOW_GUIDE.vi.md +100 -0
  90. package/.agent/workflows/ai-agent.md +2 -0
  91. package/.agent/workflows/autofix.md +1 -0
  92. package/.agent/workflows/brainstorm.md +1 -0
  93. package/.agent/workflows/context.md +1 -0
  94. package/.agent/workflows/create.md +39 -8
  95. package/.agent/workflows/dashboard.md +1 -0
  96. package/.agent/workflows/debug.md +14 -0
  97. package/.agent/workflows/deploy.md +14 -0
  98. package/.agent/workflows/enhance.md +44 -0
  99. package/.agent/workflows/gamekit-init.md +177 -0
  100. package/.agent/workflows/gamekit-launch.md +338 -0
  101. package/.agent/workflows/gamekit-plan.md +204 -0
  102. package/.agent/workflows/gamekit-qa.md +153 -0
  103. package/.agent/workflows/gamekit-spec.md +243 -0
  104. package/.agent/workflows/gamekit-tasks.md +208 -0
  105. package/.agent/workflows/marketing.md +2 -0
  106. package/.agent/workflows/next.md +1 -0
  107. package/.agent/workflows/orchestrate.md +12 -0
  108. package/.agent/workflows/pentest.md +2 -0
  109. package/.agent/workflows/plan.md +42 -0
  110. package/.agent/workflows/preview.md +1 -0
  111. package/.agent/workflows/quality.md +1 -0
  112. package/.agent/workflows/saas.md +2 -0
  113. package/.agent/workflows/spec.md +42 -0
  114. package/.agent/workflows/status.md +1 -0
  115. package/.agent/workflows/test.md +14 -0
  116. package/.agent/workflows/ui-ux-pro-max.md +1 -0
  117. package/bin/cli.js +411 -111
  118. package/package.json +1 -2
  119. package/.agent/agents/game-asset-curator.md +0 -317
  120. package/.agent/agents/game-narrative-designer.md +0 -310
  121. package/.agent/agents/game-qa-agent.md +0 -441
  122. package/.agent/workflows/game-prototype.md +0 -154
  123. package/docs/AI_DATA_INFRASTRUCTURE.md +0 -288
  124. package/docs/CHANGELOG_AI_INFRA.md +0 -141
  125. package/docs/MIGRATION_GUIDE_V1.9.md +0 -55
@@ -0,0 +1,908 @@
1
+ ---
2
+ name: character-controller-3d
3
+ description: Advanced 3D character movement. First-person, third-person controllers, advanced mechanics like dash, wall-run, swimming. Complete implementation patterns.
4
+ allowed-tools: Read, Write, Edit, Bash, Grep, WebSearch
5
+ ---
6
+
7
+ # 3D Character Controller Skill
8
+
9
+ > Build responsive, polished character movement for any 3D game.
10
+
11
+ ---
12
+
13
+ ## When to Use
14
+
15
+ - Implementing 3D player movement
16
+ - Building FPS or TPS controllers
17
+ - Advanced mechanics (dash, wall-run, climb)
18
+ - Root motion integration
19
+ - Platformer movement
20
+
21
+ ---
22
+
23
+ ## Controller Type Selection
24
+
25
+ | Type | Best For | Pros | Cons |
26
+ |------|----------|------|------|
27
+ | **CharacterController** | Most games | Simple, built-in | No physics reactions |
28
+ | **Rigidbody** | Physics puzzles | Reacts to forces | Harder to control |
29
+ | **Custom** | Specific needs | Full control | Time investment |
30
+
31
+ ---
32
+
33
+ ## Third-Person Controller
34
+
35
+ ### Complete Implementation
36
+
37
+ ```csharp
38
+ [RequireComponent(typeof(CharacterController))]
39
+ public class ThirdPersonController : MonoBehaviour
40
+ {
41
+ [Header("Movement")]
42
+ [SerializeField] private float walkSpeed = 4f;
43
+ [SerializeField] private float runSpeed = 8f;
44
+ [SerializeField] private float rotationSpeed = 10f;
45
+ [SerializeField] private float acceleration = 10f;
46
+
47
+ [Header("Jump")]
48
+ [SerializeField] private float jumpHeight = 1.5f;
49
+ [SerializeField] private float gravity = -20f;
50
+ [SerializeField] private float coyoteTime = 0.15f;
51
+ [SerializeField] private float jumpBufferTime = 0.15f;
52
+
53
+ [Header("Ground Check")]
54
+ [SerializeField] private LayerMask groundMask;
55
+ [SerializeField] private float groundCheckRadius = 0.3f;
56
+ [SerializeField] private Transform groundCheck;
57
+
58
+ [Header("Slope Handling")]
59
+ [SerializeField] private float maxSlopeAngle = 45f;
60
+ [SerializeField] private float slopeSlideSpeed = 5f;
61
+
62
+ [Header("Camera")]
63
+ [SerializeField] private Transform cameraTransform;
64
+
65
+ // Components
66
+ private CharacterController _controller;
67
+ private Animator _animator;
68
+
69
+ // State
70
+ private Vector3 _velocity;
71
+ private Vector3 _moveDirection;
72
+ private float _currentSpeed;
73
+ private bool _isGrounded;
74
+ private bool _wasGrounded;
75
+ private float _lastGroundedTime;
76
+ private float _jumpBufferedTime;
77
+ private bool _isRunning;
78
+
79
+ // Animator hashes
80
+ private static readonly int SpeedHash = Animator.StringToHash("Speed");
81
+ private static readonly int GroundedHash = Animator.StringToHash("Grounded");
82
+ private static readonly int JumpHash = Animator.StringToHash("Jump");
83
+ private static readonly int FallHash = Animator.StringToHash("Fall");
84
+
85
+ void Awake()
86
+ {
87
+ _controller = GetComponent<CharacterController>();
88
+ _animator = GetComponent<Animator>();
89
+ }
90
+
91
+ void Update()
92
+ {
93
+ GroundCheck();
94
+ HandleMovement();
95
+ HandleJump();
96
+ ApplyGravity();
97
+ HandleSlope();
98
+
99
+ // Apply final movement
100
+ _controller.Move(_velocity * Time.deltaTime);
101
+
102
+ UpdateAnimator();
103
+ }
104
+
105
+ #region Ground Check
106
+
107
+ private void GroundCheck()
108
+ {
109
+ _wasGrounded = _isGrounded;
110
+ _isGrounded = Physics.CheckSphere(
111
+ groundCheck.position,
112
+ groundCheckRadius,
113
+ groundMask
114
+ );
115
+
116
+ // Update coyote time
117
+ if (_isGrounded)
118
+ {
119
+ _lastGroundedTime = Time.time;
120
+ }
121
+
122
+ // Landing
123
+ if (!_wasGrounded && _isGrounded && _velocity.y < 0)
124
+ {
125
+ OnLand();
126
+ }
127
+ }
128
+
129
+ private bool CanCoyoteJump()
130
+ {
131
+ return Time.time - _lastGroundedTime <= coyoteTime;
132
+ }
133
+
134
+ #endregion
135
+
136
+ #region Movement
137
+
138
+ private void HandleMovement()
139
+ {
140
+ // Input
141
+ float h = Input.GetAxisRaw("Horizontal");
142
+ float v = Input.GetAxisRaw("Vertical");
143
+ Vector3 inputDir = new Vector3(h, 0, v).normalized;
144
+
145
+ _isRunning = Input.GetKey(KeyCode.LeftShift);
146
+ float targetSpeed = inputDir.magnitude > 0.1f
147
+ ? (_isRunning ? runSpeed : walkSpeed)
148
+ : 0f;
149
+
150
+ // Smooth speed transition
151
+ _currentSpeed = Mathf.MoveTowards(_currentSpeed, targetSpeed, acceleration * Time.deltaTime);
152
+
153
+ if (inputDir.magnitude > 0.1f)
154
+ {
155
+ // Calculate move direction relative to camera
156
+ float targetAngle = Mathf.Atan2(inputDir.x, inputDir.z) * Mathf.Rad2Deg
157
+ + cameraTransform.eulerAngles.y;
158
+
159
+ // Smooth rotation
160
+ float angle = Mathf.SmoothDampAngle(
161
+ transform.eulerAngles.y,
162
+ targetAngle,
163
+ ref _rotationVelocity,
164
+ 0.1f
165
+ );
166
+ transform.rotation = Quaternion.Euler(0, angle, 0);
167
+
168
+ _moveDirection = Quaternion.Euler(0, targetAngle, 0) * Vector3.forward;
169
+ }
170
+
171
+ // Apply horizontal movement
172
+ Vector3 move = _moveDirection * _currentSpeed;
173
+ _velocity.x = move.x;
174
+ _velocity.z = move.z;
175
+ }
176
+
177
+ private float _rotationVelocity;
178
+
179
+ #endregion
180
+
181
+ #region Jump
182
+
183
+ private void HandleJump()
184
+ {
185
+ // Buffer jump input
186
+ if (Input.GetButtonDown("Jump"))
187
+ {
188
+ _jumpBufferedTime = Time.time;
189
+ }
190
+
191
+ bool jumpBuffered = Time.time - _jumpBufferedTime <= jumpBufferTime;
192
+ bool canJump = (_isGrounded || CanCoyoteJump()) && jumpBuffered;
193
+
194
+ if (canJump && _velocity.y <= 0)
195
+ {
196
+ // Calculate jump velocity from height
197
+ _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity);
198
+ _jumpBufferedTime = 0;
199
+
200
+ _animator?.SetTrigger(JumpHash);
201
+ }
202
+
203
+ // Variable jump height (release early = lower jump)
204
+ if (Input.GetButtonUp("Jump") && _velocity.y > 0)
205
+ {
206
+ _velocity.y *= 0.5f;
207
+ }
208
+ }
209
+
210
+ private void OnLand()
211
+ {
212
+ // Landing effects
213
+ // Play sound, particles, etc.
214
+ }
215
+
216
+ #endregion
217
+
218
+ #region Gravity & Slope
219
+
220
+ private void ApplyGravity()
221
+ {
222
+ if (_isGrounded && _velocity.y < 0)
223
+ {
224
+ _velocity.y = -2f; // Small downward force to stay grounded
225
+ }
226
+ else
227
+ {
228
+ _velocity.y += gravity * Time.deltaTime;
229
+ }
230
+ }
231
+
232
+ private void HandleSlope()
233
+ {
234
+ if (!_isGrounded) return;
235
+
236
+ if (Physics.Raycast(transform.position, Vector3.down, out RaycastHit hit, 2f, groundMask))
237
+ {
238
+ float angle = Vector3.Angle(hit.normal, Vector3.up);
239
+
240
+ if (angle > maxSlopeAngle)
241
+ {
242
+ // Slide down steep slopes
243
+ Vector3 slideDirection = Vector3.ProjectOnPlane(Vector3.down, hit.normal).normalized;
244
+ _velocity += slideDirection * slopeSlideSpeed * Time.deltaTime;
245
+ }
246
+ }
247
+ }
248
+
249
+ #endregion
250
+
251
+ #region Animation
252
+
253
+ private void UpdateAnimator()
254
+ {
255
+ if (_animator == null) return;
256
+
257
+ _animator.SetFloat(SpeedHash, _currentSpeed / runSpeed);
258
+ _animator.SetBool(GroundedHash, _isGrounded);
259
+
260
+ if (!_isGrounded && _velocity.y < -1f)
261
+ {
262
+ _animator.SetBool(FallHash, true);
263
+ }
264
+ else
265
+ {
266
+ _animator.SetBool(FallHash, false);
267
+ }
268
+ }
269
+
270
+ #endregion
271
+
272
+ void OnDrawGizmosSelected()
273
+ {
274
+ if (groundCheck != null)
275
+ {
276
+ Gizmos.color = _isGrounded ? Color.green : Color.red;
277
+ Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
278
+ }
279
+ }
280
+ }
281
+ ```
282
+
283
+ ---
284
+
285
+ ## First-Person Controller
286
+
287
+ ```csharp
288
+ [RequireComponent(typeof(CharacterController))]
289
+ public class FirstPersonController : MonoBehaviour
290
+ {
291
+ [Header("Movement")]
292
+ [SerializeField] private float walkSpeed = 5f;
293
+ [SerializeField] private float sprintSpeed = 8f;
294
+ [SerializeField] private float crouchSpeed = 2.5f;
295
+
296
+ [Header("Jump")]
297
+ [SerializeField] private float jumpForce = 8f;
298
+ [SerializeField] private float gravity = -20f;
299
+
300
+ [Header("Look")]
301
+ [SerializeField] private float mouseSensitivity = 2f;
302
+ [SerializeField] private float maxLookAngle = 85f;
303
+ [SerializeField] private Transform cameraHolder;
304
+
305
+ [Header("Crouch")]
306
+ [SerializeField] private float standHeight = 2f;
307
+ [SerializeField] private float crouchHeight = 1f;
308
+ [SerializeField] private float crouchTransitionSpeed = 10f;
309
+
310
+ [Header("Head Bob")]
311
+ [SerializeField] private bool enableHeadBob = true;
312
+ [SerializeField] private float bobFrequency = 1.5f;
313
+ [SerializeField] private float bobAmplitude = 0.05f;
314
+
315
+ private CharacterController _controller;
316
+ private Vector3 _velocity;
317
+ private float _xRotation;
318
+ private bool _isCrouching;
319
+ private float _currentHeight;
320
+ private float _bobTimer;
321
+ private Vector3 _cameraDefaultPos;
322
+
323
+ void Start()
324
+ {
325
+ _controller = GetComponent<CharacterController>();
326
+ _currentHeight = standHeight;
327
+ _cameraDefaultPos = cameraHolder.localPosition;
328
+
329
+ Cursor.lockState = CursorLockMode.Locked;
330
+ Cursor.visible = false;
331
+ }
332
+
333
+ void Update()
334
+ {
335
+ HandleLook();
336
+ HandleMovement();
337
+ HandleCrouch();
338
+ HandleHeadBob();
339
+ }
340
+
341
+ private void HandleLook()
342
+ {
343
+ float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
344
+ float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
345
+
346
+ // Apply sensitivity from settings
347
+ mouseX *= SettingsManager.Instance?.Settings.MouseSensitivity ?? 1f;
348
+ mouseY *= SettingsManager.Instance?.Settings.MouseSensitivity ?? 1f;
349
+
350
+ // Invert Y if setting enabled
351
+ if (SettingsManager.Instance?.Settings.InvertY ?? false)
352
+ {
353
+ mouseY = -mouseY;
354
+ }
355
+
356
+ // Vertical rotation (camera)
357
+ _xRotation -= mouseY;
358
+ _xRotation = Mathf.Clamp(_xRotation, -maxLookAngle, maxLookAngle);
359
+ cameraHolder.localRotation = Quaternion.Euler(_xRotation, 0, 0);
360
+
361
+ // Horizontal rotation (body)
362
+ transform.Rotate(Vector3.up * mouseX);
363
+ }
364
+
365
+ private void HandleMovement()
366
+ {
367
+ bool isGrounded = _controller.isGrounded;
368
+
369
+ if (isGrounded && _velocity.y < 0)
370
+ {
371
+ _velocity.y = -2f;
372
+ }
373
+
374
+ // Input
375
+ float h = Input.GetAxisRaw("Horizontal");
376
+ float v = Input.GetAxisRaw("Vertical");
377
+
378
+ Vector3 move = transform.right * h + transform.forward * v;
379
+ move = move.normalized;
380
+
381
+ // Speed selection
382
+ float speed = walkSpeed;
383
+ if (_isCrouching)
384
+ speed = crouchSpeed;
385
+ else if (Input.GetKey(KeyCode.LeftShift) && v > 0)
386
+ speed = sprintSpeed;
387
+
388
+ _controller.Move(move * speed * Time.deltaTime);
389
+
390
+ // Jump
391
+ if (Input.GetButtonDown("Jump") && isGrounded && !_isCrouching)
392
+ {
393
+ _velocity.y = Mathf.Sqrt(jumpForce * -2f * gravity);
394
+ }
395
+
396
+ // Gravity
397
+ _velocity.y += gravity * Time.deltaTime;
398
+ _controller.Move(_velocity * Time.deltaTime);
399
+ }
400
+
401
+ private void HandleCrouch()
402
+ {
403
+ if (Input.GetKeyDown(KeyCode.C) || Input.GetKeyDown(KeyCode.LeftControl))
404
+ {
405
+ _isCrouching = !_isCrouching;
406
+ }
407
+
408
+ float targetHeight = _isCrouching ? crouchHeight : standHeight;
409
+
410
+ // Check ceiling before standing
411
+ if (!_isCrouching && _currentHeight < standHeight)
412
+ {
413
+ if (Physics.Raycast(transform.position, Vector3.up, standHeight - crouchHeight + 0.1f))
414
+ {
415
+ _isCrouching = true;
416
+ targetHeight = crouchHeight;
417
+ }
418
+ }
419
+
420
+ _currentHeight = Mathf.Lerp(_currentHeight, targetHeight, crouchTransitionSpeed * Time.deltaTime);
421
+ _controller.height = _currentHeight;
422
+ _controller.center = new Vector3(0, _currentHeight / 2f, 0);
423
+
424
+ // Adjust camera
425
+ cameraHolder.localPosition = new Vector3(
426
+ _cameraDefaultPos.x,
427
+ _currentHeight - 0.2f,
428
+ _cameraDefaultPos.z
429
+ );
430
+ }
431
+
432
+ private void HandleHeadBob()
433
+ {
434
+ if (!enableHeadBob) return;
435
+
436
+ bool isMoving = _controller.velocity.magnitude > 0.1f && _controller.isGrounded;
437
+
438
+ if (isMoving)
439
+ {
440
+ _bobTimer += Time.deltaTime * bobFrequency * (_isCrouching ? 0.5f : 1f);
441
+
442
+ float bobOffsetY = Mathf.Sin(_bobTimer * Mathf.PI * 2) * bobAmplitude;
443
+ float bobOffsetX = Mathf.Cos(_bobTimer * Mathf.PI) * bobAmplitude * 0.5f;
444
+
445
+ cameraHolder.localPosition += new Vector3(bobOffsetX, bobOffsetY, 0);
446
+ }
447
+ else
448
+ {
449
+ _bobTimer = 0;
450
+ }
451
+ }
452
+ }
453
+ ```
454
+
455
+ ---
456
+
457
+ ## Advanced Mechanics
458
+
459
+ ### Dash/Dodge
460
+
461
+ ```csharp
462
+ public class DashController : MonoBehaviour
463
+ {
464
+ [SerializeField] private float dashSpeed = 20f;
465
+ [SerializeField] private float dashDuration = 0.2f;
466
+ [SerializeField] private float dashCooldown = 1f;
467
+ [SerializeField] private int maxDashCharges = 2;
468
+ [SerializeField] private float chargeRegenTime = 2f;
469
+
470
+ [SerializeField] private AnimationCurve dashCurve = AnimationCurve.EaseInOut(0, 1, 1, 0);
471
+
472
+ private CharacterController _controller;
473
+ private int _dashCharges;
474
+ private float _lastDashTime;
475
+ private float _chargeRegenTimer;
476
+ private bool _isDashing;
477
+
478
+ void Start()
479
+ {
480
+ _controller = GetComponent<CharacterController>();
481
+ _dashCharges = maxDashCharges;
482
+ }
483
+
484
+ void Update()
485
+ {
486
+ RegenerateDashCharges();
487
+
488
+ if (Input.GetKeyDown(KeyCode.Space) && CanDash())
489
+ {
490
+ StartCoroutine(Dash());
491
+ }
492
+ }
493
+
494
+ private bool CanDash()
495
+ {
496
+ return !_isDashing
497
+ && _dashCharges > 0
498
+ && Time.time - _lastDashTime >= dashCooldown;
499
+ }
500
+
501
+ private IEnumerator Dash()
502
+ {
503
+ _isDashing = true;
504
+ _dashCharges--;
505
+ _lastDashTime = Time.time;
506
+
507
+ // Determine dash direction
508
+ Vector3 dashDir = GetDashDirection();
509
+
510
+ // Invincibility during dash (optional)
511
+ // GetComponent<Health>().SetInvincible(true);
512
+
513
+ float elapsed = 0f;
514
+
515
+ while (elapsed < dashDuration)
516
+ {
517
+ float curveValue = dashCurve.Evaluate(elapsed / dashDuration);
518
+ Vector3 move = dashDir * dashSpeed * curveValue * Time.deltaTime;
519
+
520
+ _controller.Move(move);
521
+
522
+ elapsed += Time.deltaTime;
523
+ yield return null;
524
+ }
525
+
526
+ // GetComponent<Health>().SetInvincible(false);
527
+ _isDashing = false;
528
+ }
529
+
530
+ private Vector3 GetDashDirection()
531
+ {
532
+ float h = Input.GetAxisRaw("Horizontal");
533
+ float v = Input.GetAxisRaw("Vertical");
534
+
535
+ Vector3 inputDir = new Vector3(h, 0, v).normalized;
536
+
537
+ if (inputDir.magnitude < 0.1f)
538
+ {
539
+ // Dash forward if no input
540
+ return transform.forward;
541
+ }
542
+
543
+ // Dash in input direction relative to camera
544
+ return Camera.main.transform.TransformDirection(inputDir);
545
+ }
546
+
547
+ private void RegenerateDashCharges()
548
+ {
549
+ if (_dashCharges < maxDashCharges)
550
+ {
551
+ _chargeRegenTimer += Time.deltaTime;
552
+
553
+ if (_chargeRegenTimer >= chargeRegenTime)
554
+ {
555
+ _dashCharges++;
556
+ _chargeRegenTimer = 0f;
557
+ }
558
+ }
559
+ }
560
+ }
561
+ ```
562
+
563
+ ### Wall Running
564
+
565
+ ```csharp
566
+ public class WallRunController : MonoBehaviour
567
+ {
568
+ [Header("Wall Detection")]
569
+ [SerializeField] private float wallCheckDistance = 0.7f;
570
+ [SerializeField] private LayerMask wallMask;
571
+ [SerializeField] private float minWallRunHeight = 1f;
572
+
573
+ [Header("Wall Run")]
574
+ [SerializeField] private float wallRunSpeed = 10f;
575
+ [SerializeField] private float wallRunGravity = -2f;
576
+ [SerializeField] private float maxWallRunTime = 2f;
577
+ [SerializeField] private float wallJumpForce = 12f;
578
+ [SerializeField] private float wallJumpSideForce = 8f;
579
+
580
+ [Header("Camera")]
581
+ [SerializeField] private float cameraTiltAngle = 15f;
582
+ [SerializeField] private float tiltSpeed = 10f;
583
+
584
+ private CharacterController _controller;
585
+ private bool _isWallRunning;
586
+ private bool _isWallLeft;
587
+ private bool _isWallRight;
588
+ private float _wallRunTimer;
589
+ private RaycastHit _wallHit;
590
+
591
+ void Update()
592
+ {
593
+ CheckWalls();
594
+ HandleWallRun();
595
+ UpdateCameraTilt();
596
+ }
597
+
598
+ private void CheckWalls()
599
+ {
600
+ _isWallLeft = Physics.Raycast(
601
+ transform.position,
602
+ -transform.right,
603
+ out _wallHit,
604
+ wallCheckDistance,
605
+ wallMask
606
+ );
607
+
608
+ if (!_isWallLeft)
609
+ {
610
+ _isWallRight = Physics.Raycast(
611
+ transform.position,
612
+ transform.right,
613
+ out _wallHit,
614
+ wallCheckDistance,
615
+ wallMask
616
+ );
617
+ }
618
+ else
619
+ {
620
+ _isWallRight = false;
621
+ }
622
+ }
623
+
624
+ private void HandleWallRun()
625
+ {
626
+ bool canWallRun = (_isWallLeft || _isWallRight)
627
+ && !_controller.isGrounded
628
+ && transform.position.y > minWallRunHeight
629
+ && Input.GetAxisRaw("Vertical") > 0;
630
+
631
+ if (canWallRun && !_isWallRunning)
632
+ {
633
+ StartWallRun();
634
+ }
635
+ else if (!canWallRun && _isWallRunning)
636
+ {
637
+ StopWallRun();
638
+ }
639
+
640
+ if (_isWallRunning)
641
+ {
642
+ _wallRunTimer += Time.deltaTime;
643
+
644
+ if (_wallRunTimer >= maxWallRunTime)
645
+ {
646
+ StopWallRun();
647
+ return;
648
+ }
649
+
650
+ // Wall run movement
651
+ Vector3 wallForward = Vector3.Cross(_wallHit.normal, Vector3.up);
652
+
653
+ // Ensure we're running in the right direction
654
+ if (Vector3.Dot(wallForward, transform.forward) < 0)
655
+ {
656
+ wallForward = -wallForward;
657
+ }
658
+
659
+ _controller.Move(wallForward * wallRunSpeed * Time.deltaTime);
660
+
661
+ // Reduced gravity
662
+ _controller.Move(Vector3.up * wallRunGravity * Time.deltaTime);
663
+
664
+ // Wall jump
665
+ if (Input.GetButtonDown("Jump"))
666
+ {
667
+ WallJump();
668
+ }
669
+ }
670
+ }
671
+
672
+ private void StartWallRun()
673
+ {
674
+ _isWallRunning = true;
675
+ _wallRunTimer = 0f;
676
+ }
677
+
678
+ private void StopWallRun()
679
+ {
680
+ _isWallRunning = false;
681
+ }
682
+
683
+ private void WallJump()
684
+ {
685
+ Vector3 jumpDirection = _wallHit.normal * wallJumpSideForce + Vector3.up * wallJumpForce;
686
+
687
+ // Apply force through velocity (you'll need to integrate this with your main controller)
688
+ GetComponent<ThirdPersonController>().SetVelocity(jumpDirection);
689
+
690
+ StopWallRun();
691
+ }
692
+
693
+ private void UpdateCameraTilt()
694
+ {
695
+ float targetTilt = 0f;
696
+
697
+ if (_isWallRunning)
698
+ {
699
+ targetTilt = _isWallLeft ? cameraTiltAngle : -cameraTiltAngle;
700
+ }
701
+
702
+ // Smooth camera tilt
703
+ // camera.transform.localRotation = ...
704
+ }
705
+ }
706
+ ```
707
+
708
+ ### Ledge Climbing
709
+
710
+ ```csharp
711
+ public class LedgeClimbController : MonoBehaviour
712
+ {
713
+ [Header("Detection")]
714
+ [SerializeField] private float ledgeCheckDistance = 1f;
715
+ [SerializeField] private float ledgeCheckHeight = 2f;
716
+ [SerializeField] private LayerMask climbMask;
717
+
718
+ [Header("Climb")]
719
+ [SerializeField] private float climbDuration = 0.5f;
720
+ [SerializeField] private AnimationCurve climbCurve;
721
+
722
+ private CharacterController _controller;
723
+ private bool _isClimbing;
724
+
725
+ void Update()
726
+ {
727
+ if (!_isClimbing && CanClimb())
728
+ {
729
+ if (Input.GetKeyDown(KeyCode.E) || (Input.GetAxisRaw("Vertical") > 0 && Input.GetButtonDown("Jump")))
730
+ {
731
+ StartCoroutine(ClimbLedge());
732
+ }
733
+ }
734
+ }
735
+
736
+ private bool CanClimb()
737
+ {
738
+ if (_controller.isGrounded) return false;
739
+
740
+ // Check for wall in front
741
+ if (!Physics.Raycast(transform.position + Vector3.up * 0.5f, transform.forward, ledgeCheckDistance, climbMask))
742
+ {
743
+ return false;
744
+ }
745
+
746
+ // Check for ledge above
747
+ Vector3 ledgeCheckStart = transform.position + Vector3.up * ledgeCheckHeight + transform.forward * ledgeCheckDistance;
748
+ if (!Physics.Raycast(ledgeCheckStart, Vector3.down, out RaycastHit hit, ledgeCheckHeight - 0.5f, climbMask))
749
+ {
750
+ return false;
751
+ }
752
+
753
+ // Check if there's space on top
754
+ Vector3 targetPos = hit.point + Vector3.up * 0.1f;
755
+ return !Physics.CheckSphere(targetPos, 0.3f, climbMask);
756
+ }
757
+
758
+ private IEnumerator ClimbLedge()
759
+ {
760
+ _isClimbing = true;
761
+ _controller.enabled = false;
762
+
763
+ // Find target position
764
+ Vector3 ledgeCheckStart = transform.position + Vector3.up * ledgeCheckHeight + transform.forward * ledgeCheckDistance;
765
+ Physics.Raycast(ledgeCheckStart, Vector3.down, out RaycastHit hit, ledgeCheckHeight - 0.5f, climbMask);
766
+
767
+ Vector3 startPos = transform.position;
768
+ Vector3 targetPos = hit.point + Vector3.up * 0.1f + transform.forward * 0.5f;
769
+
770
+ // Intermediate position (hands on ledge)
771
+ Vector3 hangPos = new Vector3(
772
+ targetPos.x - transform.forward.x * 0.3f,
773
+ targetPos.y - 0.5f,
774
+ targetPos.z - transform.forward.z * 0.3f
775
+ );
776
+
777
+ float elapsed = 0f;
778
+ float halfDuration = climbDuration * 0.5f;
779
+
780
+ // Rise to hang position
781
+ while (elapsed < halfDuration)
782
+ {
783
+ float t = elapsed / halfDuration;
784
+ transform.position = Vector3.Lerp(startPos, hangPos, climbCurve.Evaluate(t));
785
+ elapsed += Time.deltaTime;
786
+ yield return null;
787
+ }
788
+
789
+ // Climb up
790
+ elapsed = 0f;
791
+ while (elapsed < halfDuration)
792
+ {
793
+ float t = elapsed / halfDuration;
794
+ transform.position = Vector3.Lerp(hangPos, targetPos, climbCurve.Evaluate(t));
795
+ elapsed += Time.deltaTime;
796
+ yield return null;
797
+ }
798
+
799
+ transform.position = targetPos;
800
+ _controller.enabled = true;
801
+ _isClimbing = false;
802
+ }
803
+ }
804
+ ```
805
+
806
+ ---
807
+
808
+ ## Input Buffering
809
+
810
+ ```csharp
811
+ public class InputBuffer
812
+ {
813
+ private struct BufferedInput
814
+ {
815
+ public string Action;
816
+ public float Timestamp;
817
+ }
818
+
819
+ private Queue<BufferedInput> _buffer = new();
820
+ private float _bufferWindow = 0.15f;
821
+
822
+ public void BufferInput(string action)
823
+ {
824
+ _buffer.Enqueue(new BufferedInput
825
+ {
826
+ Action = action,
827
+ Timestamp = Time.time
828
+ });
829
+ }
830
+
831
+ public bool ConsumeInput(string action)
832
+ {
833
+ // Clean expired inputs
834
+ while (_buffer.Count > 0 && Time.time - _buffer.Peek().Timestamp > _bufferWindow)
835
+ {
836
+ _buffer.Dequeue();
837
+ }
838
+
839
+ // Check for action
840
+ foreach (var input in _buffer)
841
+ {
842
+ if (input.Action == action)
843
+ {
844
+ // Remove consumed input
845
+ var temp = new Queue<BufferedInput>();
846
+ while (_buffer.Count > 0)
847
+ {
848
+ var item = _buffer.Dequeue();
849
+ if (item.Action != action || temp.Count > 0)
850
+ {
851
+ temp.Enqueue(item);
852
+ }
853
+ }
854
+ _buffer = temp;
855
+ return true;
856
+ }
857
+ }
858
+
859
+ return false;
860
+ }
861
+ }
862
+
863
+ // Usage in controller
864
+ private InputBuffer _inputBuffer = new();
865
+
866
+ void Update()
867
+ {
868
+ if (Input.GetButtonDown("Jump"))
869
+ {
870
+ _inputBuffer.BufferInput("Jump");
871
+ }
872
+
873
+ // Later, when checking if we can jump:
874
+ if (_isGrounded && _inputBuffer.ConsumeInput("Jump"))
875
+ {
876
+ DoJump();
877
+ }
878
+ }
879
+ ```
880
+
881
+ ---
882
+
883
+ ## Quick Reference
884
+
885
+ | Mechanic | Key Values |
886
+ |----------|------------|
887
+ | **Walk Speed** | 4-5 m/s |
888
+ | **Run Speed** | 8-10 m/s |
889
+ | **Jump Height** | 1-2 m |
890
+ | **Gravity** | -20 to -30 |
891
+ | **Coyote Time** | 0.1-0.2 s |
892
+ | **Jump Buffer** | 0.1-0.15 s |
893
+
894
+ ---
895
+
896
+ ## Anti-Patterns
897
+
898
+ | ❌ Don't | ✅ Do |
899
+ |----------|-------|
900
+ | Check `Input.GetKey` directly | Use input buffering |
901
+ | Hard-coded values | SerializeField everything |
902
+ | No coyote time | Always add tolerance |
903
+ | Physics in Update | Use FixedUpdate for physics |
904
+ | Instant state changes | Smooth transitions |
905
+
906
+ ---
907
+
908
+ > **Remember:** Good movement feels responsive but forgiving. Add tiny cheats for the player.