This commit is contained in:
2025-05-28 10:05:03 +08:00
parent 45c935c5b6
commit ecd25ce410
3706 changed files with 715515 additions and 20 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 27054f2851dc4314a8359af559137e6b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,143 @@
using UnityEngine;
public class Crouch : MonoBehaviour
{
public KeyCode key = KeyCode.LeftControl;
[Header("Slow Movement")]
[Tooltip("Movement to slow down when crouched.")]
public FirstPersonMovement movement;
[Tooltip("Movement speed when crouched.")]
public float movementSpeed = 2;
[Header("Low Head")]
[Tooltip("Head to lower when crouched.")]
public Transform headToLower;
[HideInInspector]
public float? defaultHeadYLocalPosition;
public float crouchYHeadPosition = 1;
[Tooltip("Collider to lower when crouched.")]
public CapsuleCollider colliderToLower;
[HideInInspector]
public float? defaultColliderHeight;
public bool IsCrouched { get; private set; }
public event System.Action CrouchStart, CrouchEnd;
void Reset()
{
// Try to get components.
movement = GetComponentInParent<FirstPersonMovement>();
headToLower = movement.GetComponentInChildren<Camera>().transform;
colliderToLower = movement.GetComponentInChildren<CapsuleCollider>();
}
void LateUpdate()
{
if (Input.GetKey(key))
{
// Enforce a low head.
if (headToLower)
{
// If we don't have the defaultHeadYLocalPosition, get it now.
if (!defaultHeadYLocalPosition.HasValue)
{
defaultHeadYLocalPosition = headToLower.localPosition.y;
}
// Lower the head.
headToLower.localPosition = new Vector3(headToLower.localPosition.x, crouchYHeadPosition, headToLower.localPosition.z);
}
// Enforce a low colliderToLower.
if (colliderToLower)
{
// If we don't have the defaultColliderHeight, get it now.
if (!defaultColliderHeight.HasValue)
{
defaultColliderHeight = colliderToLower.height;
}
// Get lowering amount.
float loweringAmount;
if(defaultHeadYLocalPosition.HasValue)
{
loweringAmount = defaultHeadYLocalPosition.Value - crouchYHeadPosition;
}
else
{
loweringAmount = defaultColliderHeight.Value * .5f;
}
// Lower the colliderToLower.
colliderToLower.height = Mathf.Max(defaultColliderHeight.Value - loweringAmount, 0);
colliderToLower.center = Vector3.up * colliderToLower.height * .5f;
}
// Set IsCrouched state.
if (!IsCrouched)
{
IsCrouched = true;
SetSpeedOverrideActive(true);
CrouchStart?.Invoke();
}
}
else
{
if (IsCrouched)
{
// Rise the head back up.
if (headToLower)
{
headToLower.localPosition = new Vector3(headToLower.localPosition.x, defaultHeadYLocalPosition.Value, headToLower.localPosition.z);
}
// Reset the colliderToLower's height.
if (colliderToLower)
{
colliderToLower.height = defaultColliderHeight.Value;
colliderToLower.center = Vector3.up * colliderToLower.height * .5f;
}
// Reset IsCrouched.
IsCrouched = false;
SetSpeedOverrideActive(false);
CrouchEnd?.Invoke();
}
}
}
#region Speed override.
void SetSpeedOverrideActive(bool state)
{
// Stop if there is no movement component.
if(!movement)
{
return;
}
// Update SpeedOverride.
if (state)
{
// Try to add the SpeedOverride to the movement component.
if (!movement.speedOverrides.Contains(SpeedOverride))
{
movement.speedOverrides.Add(SpeedOverride);
}
}
else
{
// Try to remove the SpeedOverride from the movement component.
if (movement.speedOverrides.Contains(SpeedOverride))
{
movement.speedOverrides.Remove(SpeedOverride);
}
}
}
float SpeedOverride() => movementSpeed;
#endregion
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 27b67ddb3d23b8340b125aede56d7e8c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 174710
packageName: Mini First Person Controller
packageVersion: 1.1.0
assetPath: Assets/Mini First Person Controller/Scripts/Components/Crouch.cs
uploadId: 442605

View File

@@ -0,0 +1,197 @@
using System.Linq;
using UnityEngine;
public class FirstPersonAudio : MonoBehaviour
{
public FirstPersonMovement character;
public GroundCheck groundCheck;
[Header("Step")]
public AudioSource stepAudio;
public AudioSource runningAudio;
[Tooltip("Minimum velocity for moving audio to play")]
/// <summary> "Minimum velocity for moving audio to play" </summary>
public float velocityThreshold = .01f;
Vector2 lastCharacterPosition;
Vector2 CurrentCharacterPosition => new Vector2(character.transform.position.x, character.transform.position.z);
[Header("Landing")]
public AudioSource landingAudio;
public AudioClip[] landingSFX;
[Header("Jump")]
public Jump jump;
public AudioSource jumpAudio;
public AudioClip[] jumpSFX;
[Header("Crouch")]
public Crouch crouch;
public AudioSource crouchStartAudio, crouchedAudio, crouchEndAudio;
public AudioClip[] crouchStartSFX, crouchEndSFX;
AudioSource[] MovingAudios => new AudioSource[] { stepAudio, runningAudio, crouchedAudio };
void Reset()
{
// Setup stuff.
character = GetComponentInParent<FirstPersonMovement>();
groundCheck = (transform.parent ?? transform).GetComponentInChildren<GroundCheck>();
stepAudio = GetOrCreateAudioSource("Step Audio");
runningAudio = GetOrCreateAudioSource("Running Audio");
landingAudio = GetOrCreateAudioSource("Landing Audio");
// Setup jump audio.
jump = GetComponentInParent<Jump>();
if (jump)
{
jumpAudio = GetOrCreateAudioSource("Jump audio");
}
// Setup crouch audio.
crouch = GetComponentInParent<Crouch>();
if (crouch)
{
crouchStartAudio = GetOrCreateAudioSource("Crouch Start Audio");
crouchStartAudio = GetOrCreateAudioSource("Crouched Audio");
crouchStartAudio = GetOrCreateAudioSource("Crouch End Audio");
}
}
void OnEnable() => SubscribeToEvents();
void OnDisable() => UnsubscribeToEvents();
void FixedUpdate()
{
// Play moving audio if the character is moving and on the ground.
float velocity = Vector3.Distance(CurrentCharacterPosition, lastCharacterPosition);
if (velocity >= velocityThreshold && groundCheck && groundCheck.isGrounded)
{
if (crouch && crouch.IsCrouched)
{
SetPlayingMovingAudio(crouchedAudio);
}
else if (character.IsRunning)
{
SetPlayingMovingAudio(runningAudio);
}
else
{
SetPlayingMovingAudio(stepAudio);
}
}
else
{
SetPlayingMovingAudio(null);
}
// Remember lastCharacterPosition.
lastCharacterPosition = CurrentCharacterPosition;
}
/// <summary>
/// Pause all MovingAudios and enforce play on audioToPlay.
/// </summary>
/// <param name="audioToPlay">Audio that should be playing.</param>
void SetPlayingMovingAudio(AudioSource audioToPlay)
{
// Pause all MovingAudios.
foreach (var audio in MovingAudios.Where(audio => audio != audioToPlay && audio != null))
{
audio.Pause();
}
// Play audioToPlay if it was not playing.
if (audioToPlay && !audioToPlay.isPlaying)
{
audioToPlay.Play();
}
}
#region Play instant-related audios.
void PlayLandingAudio() => PlayRandomClip(landingAudio, landingSFX);
void PlayJumpAudio() => PlayRandomClip(jumpAudio, jumpSFX);
void PlayCrouchStartAudio() => PlayRandomClip(crouchStartAudio, crouchStartSFX);
void PlayCrouchEndAudio() => PlayRandomClip(crouchEndAudio, crouchEndSFX);
#endregion
#region Subscribe/unsubscribe to events.
void SubscribeToEvents()
{
// PlayLandingAudio when Grounded.
groundCheck.Grounded += PlayLandingAudio;
// PlayJumpAudio when Jumped.
if (jump)
{
jump.Jumped += PlayJumpAudio;
}
// Play crouch audio on crouch start/end.
if (crouch)
{
crouch.CrouchStart += PlayCrouchStartAudio;
crouch.CrouchEnd += PlayCrouchEndAudio;
}
}
void UnsubscribeToEvents()
{
// Undo PlayLandingAudio when Grounded.
groundCheck.Grounded -= PlayLandingAudio;
// Undo PlayJumpAudio when Jumped.
if (jump)
{
jump.Jumped -= PlayJumpAudio;
}
// Undo play crouch audio on crouch start/end.
if (crouch)
{
crouch.CrouchStart -= PlayCrouchStartAudio;
crouch.CrouchEnd -= PlayCrouchEndAudio;
}
}
#endregion
#region Utility.
/// <summary>
/// Get an existing AudioSource from a name or create one if it was not found.
/// </summary>
/// <param name="name">Name of the AudioSource to search for.</param>
/// <returns>The created AudioSource.</returns>
AudioSource GetOrCreateAudioSource(string name)
{
// Try to get the audiosource.
AudioSource result = System.Array.Find(GetComponentsInChildren<AudioSource>(), a => a.name == name);
if (result)
return result;
// Audiosource does not exist, create it.
result = new GameObject(name).AddComponent<AudioSource>();
result.spatialBlend = 1;
result.playOnAwake = false;
result.transform.SetParent(transform, false);
return result;
}
static void PlayRandomClip(AudioSource audio, AudioClip[] clips)
{
if (!audio || clips.Length <= 0)
return;
// Get a random clip. If possible, make sure that it's not the same as the clip that is already on the audiosource.
AudioClip clip = clips[Random.Range(0, clips.Length)];
if (clips.Length > 1)
while (clip == audio.clip)
clip = clips[Random.Range(0, clips.Length)];
// Play the clip.
audio.clip = clip;
audio.Play();
}
#endregion
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6f45facc01772ba47a9ae17bb7061b71
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 174710
packageName: Mini First Person Controller
packageVersion: 1.1.0
assetPath: Assets/Mini First Person Controller/Scripts/Components/FirstPersonAudio.cs
uploadId: 442605

View File

@@ -0,0 +1,41 @@
using UnityEngine;
[ExecuteInEditMode]
public class GroundCheck : MonoBehaviour
{
[Tooltip("Maximum distance from the ground.")]
public float distanceThreshold = .15f;
[Tooltip("Whether this transform is grounded now.")]
public bool isGrounded = true;
/// <summary>
/// Called when the ground is touched again.
/// </summary>
public event System.Action Grounded;
const float OriginOffset = .001f;
Vector3 RaycastOrigin => transform.position + Vector3.up * OriginOffset;
float RaycastDistance => distanceThreshold + OriginOffset;
void LateUpdate()
{
// Check if we are grounded now.
bool isGroundedNow = Physics.Raycast(RaycastOrigin, Vector3.down, distanceThreshold * 2);
// Call event if we were in the air and we are now touching the ground.
if (isGroundedNow && !isGrounded)
{
Grounded?.Invoke();
}
// Update isGrounded.
isGrounded = isGroundedNow;
}
void OnDrawGizmosSelected()
{
// Draw a line in the Editor to show whether we are touching the ground.
Debug.DrawLine(RaycastOrigin, RaycastOrigin + Vector3.down * RaycastDistance, isGrounded ? Color.white : Color.red);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 084c01ab381b9da4082c1f84cf70751b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 174710
packageName: Mini First Person Controller
packageVersion: 1.1.0
assetPath: Assets/Mini First Person Controller/Scripts/Components/GroundCheck.cs
uploadId: 442605

View File

@@ -0,0 +1,34 @@
using UnityEngine;
public class Jump : MonoBehaviour
{
Rigidbody rigidbody;
public float jumpStrength = 2;
public event System.Action Jumped;
[SerializeField, Tooltip("Prevents jumping when the transform is in mid-air.")]
GroundCheck groundCheck;
void Reset()
{
// Try to get groundCheck.
groundCheck = GetComponentInChildren<GroundCheck>();
}
void Awake()
{
// Get rigidbody.
rigidbody = GetComponent<Rigidbody>();
}
void LateUpdate()
{
// Jump when the Jump button is pressed and we are on the ground.
if (Input.GetButtonDown("Jump") && (!groundCheck || groundCheck.isGrounded))
{
rigidbody.AddForce(Vector3.up * 100 * jumpStrength);
Jumped?.Invoke();
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 8f977f53412d81140ab13b3fca57cca2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 174710
packageName: Mini First Person Controller
packageVersion: 1.1.0
assetPath: Assets/Mini First Person Controller/Scripts/Components/Jump.cs
uploadId: 442605

View File

@@ -0,0 +1,31 @@
using UnityEngine;
[ExecuteInEditMode]
public class Zoom : MonoBehaviour
{
Camera camera;
public float defaultFOV = 60;
public float maxZoomFOV = 15;
[Range(0, 1)]
public float currentZoom;
public float sensitivity = 1;
void Awake()
{
// Get the camera on this gameObject and the defaultZoom.
camera = GetComponent<Camera>();
if (camera)
{
defaultFOV = camera.fieldOfView;
}
}
void Update()
{
// Update the currentZoom and the camera's fieldOfView.
currentZoom += Input.mouseScrollDelta.y * sensitivity * .05f;
currentZoom = Mathf.Clamp01(currentZoom);
camera.fieldOfView = Mathf.Lerp(defaultFOV, maxZoomFOV, currentZoom);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 780f90e951d635f439326ea715cd97e9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 174710
packageName: Mini First Person Controller
packageVersion: 1.1.0
assetPath: Assets/Mini First Person Controller/Scripts/Components/Zoom.cs
uploadId: 442605

View File

@@ -0,0 +1,39 @@
using UnityEngine;
public class FirstPersonLook : MonoBehaviour
{
[SerializeField]
Transform character;
public float sensitivity = 2;
public float smoothing = 1.5f;
Vector2 velocity;
Vector2 frameVelocity;
void Reset()
{
// Get the character from the FirstPersonMovement in parents.
character = GetComponentInParent<FirstPersonMovement>().transform;
}
void Start()
{
// Lock the mouse cursor to the game screen.
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
// Get smooth velocity.
Vector2 mouseDelta = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
Vector2 rawFrameVelocity = Vector2.Scale(mouseDelta, Vector2.one * sensitivity);
frameVelocity = Vector2.Lerp(frameVelocity, rawFrameVelocity, 1 / smoothing);
velocity += frameVelocity;
velocity.y = Mathf.Clamp(velocity.y, -90, 90);
// Rotate camera up-down and controller left-right from velocity.
transform.localRotation = Quaternion.AngleAxis(-velocity.y, Vector3.right);
character.localRotation = Quaternion.AngleAxis(velocity.x, Vector3.up);
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: ec72980442fddac4d86e75ab24b4d027
timeCreated: 1513577414
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 174710
packageName: Mini First Person Controller
packageVersion: 1.1.0
assetPath: Assets/Mini First Person Controller/Scripts/FirstPersonLook.cs
uploadId: 442605

View File

@@ -0,0 +1,44 @@
using System.Collections.Generic;
using UnityEngine;
public class FirstPersonMovement : MonoBehaviour
{
public float speed = 5;
[Header("Running")]
public bool canRun = true;
public bool IsRunning { get; private set; }
public float runSpeed = 9;
public KeyCode runningKey = KeyCode.LeftShift;
Rigidbody rigidbody;
/// <summary> Functions to override movement speed. Will use the last added override. </summary>
public List<System.Func<float>> speedOverrides = new List<System.Func<float>>();
void Awake()
{
// Get the rigidbody on this.
rigidbody = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
// Update IsRunning from input.
IsRunning = canRun && Input.GetKey(runningKey);
// Get targetMovingSpeed.
float targetMovingSpeed = IsRunning ? runSpeed : speed;
if (speedOverrides.Count > 0)
{
targetMovingSpeed = speedOverrides[speedOverrides.Count - 1]();
}
// Get targetVelocity from input.
Vector2 targetVelocity =new Vector2( Input.GetAxis("Horizontal") * targetMovingSpeed, Input.GetAxis("Vertical") * targetMovingSpeed);
// Apply movement.
rigidbody.linearVelocity = transform.rotation * new Vector3(targetVelocity.x, rigidbody.linearVelocity.y, targetVelocity.y);
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 97fca36bd81de7c4cafce7bbf02ca611
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 174710
packageName: Mini First Person Controller
packageVersion: 1.1.0
assetPath: Assets/Mini First Person Controller/Scripts/FirstPersonMovement.cs
uploadId: 442605