USER
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Unity.Properties;
using Unity.VisualScripting;
using UnityEngine;
public class FirstPersonController : MonoBehaviour
{
public CharacterController characterController;
public Camera mainCamera;
private Camera playerCamera;
private Vector3 moveDirection;
private Vector2 currentInput;
private float rotationX = 0;
public bool CanMove { get; private set; } = true;
private bool isSprinting => canSprint && !isCrouching && Input.GetKey(sprintKey);
private bool ShouldJump => Input.GetKeyDown(jumpKey) && !isCrouching && characterController.isGrounded;
private bool ShouldCrouch => Input.GetKeyDown(crouchKey) && !duringCrouchAnimation && characterController.isGrounded;
[Header("Functional Options")]
[SerializeField] private bool canSprint = true;
[SerializeField] private bool canJump = true;
[SerializeField] private bool canCrouch = true;
[SerializeField] private bool canUseHeadbob = true;
[SerializeField] private bool canInteract = true;
//[SerializeField] private bool useFootsteps = true;
[Header("Controls")]
[SerializeField] private KeyCode sprintKey = KeyCode.LeftShift;
[SerializeField] private KeyCode jumpKey = KeyCode.Space;
[SerializeField] private KeyCode crouchKey = KeyCode.LeftControl;
[SerializeField] private KeyCode interactKey = KeyCode.E;
[Header("Movement Parameters")]
[SerializeField] private float walkSpeed = 5.0f;
[SerializeField] private float sprintSpeed = 9.5f;
[SerializeField] private float crouchSpeed = 3.0f;
private float targetSpeed;
[Header("Look Parameters")]
[SerializeField, Range(1, 10)] private float lookSpeedX = 2.0f;
[SerializeField, Range(1, 10)] private float lookSpeedY = 2.0f;
[SerializeField, Range(1, 180)] private float upperLookLimit = 80.0f;
[SerializeField, Range(1, 180)] private float lowerLookLimit = 80.0f;
[Header("Jumping Parameters")]
[SerializeField] private float jumpForce = 9.0f;
[SerializeField] private float gravity = 30.0f;
[Header("Croch Parameters")]
[SerializeField] private float crouchHeight = 0.5f;
[SerializeField] private float standHeight = 2f;
[SerializeField] private float timeToCrouch = 0.25f;
[SerializeField] private Vector3 crouchingCenter = new Vector3(0, 0.5f, 0);
[SerializeField] private Vector3 standingCenter = new Vector3(0, 0, 0);
private bool isCrouching;
private bool duringCrouchAnimation;
[Header("Headbob Parameters")]
[SerializeField] private float walkBobSpeed = 14f;
[SerializeField] private float walkBobAmount = 0.05f;
[SerializeField] private float sprintBobSpeed = 18f;
[SerializeField] private float sprintBobAmount = 0.11f;
[SerializeField] private float crouchBobSpeed = 8f;
[SerializeField] private float crouchBobAmount = 0.025f;
private float defaultYPos = 0;
private float defaultXPos = 0;
private float timer;
/*[Header("Footsteps Parameters")]
[SerializeField] private float baseStepSpeed = 0.5f;
[SerializeField] private float crouchStepMultiplier = 1.5f;
[SerializeField] private float sprintStepMultiplier = 0.6f;
[SerializeField] private AudioSource footstepAudioSource = default;
[SerializeField] private AudioClip[] woodClips = default;
[SerializeField] private AudioClip[] metalClips = default;
[SerializeField] private AudioClip[] grassClips = default;
private float footstepTimer = 0;
private float GetCurrentOffset => isCrouching ? baseStepSpeed * crouchStepMultiplier : IsSprinting ? baseStepSpeed * sprintStepMultiplier : baseStepSpeed;
*/
[Header("FOV Manager")]
[SerializeField] private float walkingFOV = 70f;
[SerializeField] private float sprintingFOV = 90f;
[SerializeField] private float speedFOVchange = 5f;
private float targetFOV;
[Header("Interaction")]
[SerializeField] private Vector3 interactionRayPoint = default;
[SerializeField] private float interactionDistance = default;
[SerializeField] private LayerMask interactionLayer = default;
private Interactable currentInteractable;
[Header("Wall Running")]
[SerializeField] private LayerMask whatIsWall;
[SerializeField] private float wallRunForce;
[SerializeField] private float wallRunSpeed;
[SerializeField] private float wallJumpUpForce;
[SerializeField] private float wallJumpSlideForce;
[SerializeField] private float maxWallRunTime;
[SerializeField] private float WallRunTimer;
[SerializeField] private bool isWallRunning;
[Header("Wall Checker")]
[SerializeField] private float wallCheckDistance;
[SerializeField] private float minJumpHeight;
[NonSerialized] private RaycastHit wallLeftHit;
[NonSerialized] private RaycastHit wallRightHit;
[SerializeField] private bool wallLeft;
[SerializeField] private bool wallRight;
[Header("Exiting Wall Run")]
[NonSerialized] private bool exitingWall;
[SerializeField] private float exitWallTime;
[NonSerialized] private float exitWallTimer;
public Transform orientation;
public MovementState state;
public enum MovementState {
wallrunning
}
void Awake() {
playerCamera = GetComponentInChildren<Camera>();
characterController = GetComponent<CharacterController>();
defaultYPos = playerCamera.transform.localPosition.y;
defaultXPos = playerCamera.transform.localPosition.x;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void Update() {
if (CanMove) {
HandleMovementInput();
HandleMouseLook();
ManagerFOV();
if (canJump)
HandleJump();
if (canCrouch)
HandleCrouch();
if (canUseHeadbob)
HandleHeadbob();
if (canInteract)
HandleInteractionCheck();
HandleInteractionInput();
//if (useFootsteps)
// HandleFootsteps();
SpeedControl();
ApplyFinalMovements();
}
}
private void FixedUpdate() {
}
private void CheckForWall() {
wallLeft = Physics.Raycast(transform.position, -orientation.right, out wallLeftHit, wallCheckDistance, whatIsWall);
wallRight = Physics.Raycast(transform.position, orientation.right, out wallRightHit, wallCheckDistance, whatIsWall);
}
private bool AboveGround() {
return !characterController.isGrounded;
}
private void StateMachine() {
}
private void StartWallRun() {
isWallRunning = true;
}
private void WallRunMovement() {
}
private void StopWallRun() {
isWallRunning = false;
}
/*private void HandleFootsteps() {
if (!characterController.isGrounded) return;
if (currentInput == Vector2.zero) return;
footstepTimer -= Time.deltaTime;
if (footstepTimer <= 0) {
footstepAudioSource.pitch = UnityEngine.Random.Range(0.9f, 1.1f);
if (Physics.Raycast(characterController.transform.position, Vector3.down, out RaycastHit hit, 3)) {
switch(hit.collider.tag) {
case "Footsteps/WOOD":
footstepAudioSource.PlayOneShot(woodClips[UnityEngine.Random.Range(0, woodClips.Length - 1)]);
break;
case "Footsteps/METALIC":
footstepAudioSource.PlayOneShot(metalClips[UnityEngine.Random.Range(0, metalClips.Length - 1)]);
break;
case "Footsteps/GRASS":
footstepAudioSource.PlayOneShot(grassClips[UnityEngine.Random.Range(0, grassClips.Length - 1)]);
break;
default:
footstepAudioSource.PlayOneShot(metalClips[UnityEngine.Random.Range(0, metalClips.Length - 1)]);
break;
}
}
footstepTimer = GetCurrentOffset;
}
}*/
private void HandleInteractionCheck() {
if (Physics.Raycast(playerCamera.ViewportPointToRay(interactionRayPoint), out RaycastHit hit, interactionDistance)) {
if (hit.collider.gameObject.layer == 31 && (currentInteractable == null || hit.collider.gameObject.GetInstanceID() != currentInteractable.GetInstanceID())) {
hit.collider.TryGetComponent(out currentInteractable);
if(currentInteractable)
currentInteractable.OnFocus();
}
}
else if (currentInteractable) {
currentInteractable.OnLoseFocus();
currentInteractable = null;
}
}
private void HandleInteractionInput() {
if (Input.GetKeyDown(interactKey) && currentInteractable != null && Physics.Raycast(playerCamera.ViewportPointToRay(interactionRayPoint), out RaycastHit hit, interactionDistance, interactionLayer)) {
currentInteractable.OnInteract();
}
}
private void HandleMovementInput() {
currentInput = new Vector2((isSprinting && !isWallRunning ? sprintSpeed : isWallRunning ? wallRunSpeed : isCrouching ? crouchSpeed : walkSpeed) * Input.GetAxis("Vertical"),
(isSprinting && !isWallRunning ? sprintSpeed : isWallRunning ? wallRunSpeed : isCrouching ? crouchSpeed : walkSpeed) * Input.GetAxis("Horizontal"));
float moveDirectionY = moveDirection.y;
moveDirection = (transform.TransformDirection(Vector3.forward) * currentInput.x) + (transform.TransformDirection(Vector3.right) * currentInput.y);
moveDirection.y = moveDirectionY;
}
private void HandleMouseLook() {
rotationX -= Input.GetAxis("Mouse Y") * lookSpeedY;
rotationX = Mathf.Clamp(rotationX, -upperLookLimit, lowerLookLimit);
playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * lookSpeedX, 0);
}
private void HandleJump() {
if (ShouldJump) {
moveDirection.y = jumpForce;
}
}
private void HandleCrouch() {
if (ShouldCrouch) {
StartCoroutine(CrouchStand());
}
}
private void HandleHeadbob() {
if (!characterController.isGrounded) return;
if (Mathf.Abs(moveDirection.x) > 0.1f || Mathf.Abs(moveDirection.z) > 0.1f) {
timer += Time.deltaTime * (isCrouching ? crouchBobSpeed : isSprinting ? sprintBobSpeed : walkBobSpeed);
playerCamera.transform.localPosition = new Vector3 (
defaultXPos + Mathf.Sin(timer) * (isCrouching ? crouchBobAmount * 0.25f : isSprinting ? sprintBobAmount * 0.25f : walkBobAmount * 0.25f),
defaultYPos + Mathf.Sin(timer) * (isCrouching ? crouchBobAmount : isSprinting ? sprintBobAmount : walkBobAmount),
playerCamera.transform.localPosition.z
);
}
}
private void ApplyFinalMovements() {
if (!characterController.isGrounded) {
moveDirection.y -= gravity * Time.deltaTime;
}
characterController.Move(moveDirection * Time.deltaTime);
}
private void ManagerFOV() {
targetFOV = isSprinting && characterController.velocity.magnitude >= sprintSpeed * 0.5f ? sprintingFOV : walkingFOV;
mainCamera.fieldOfView = Mathf.Lerp(mainCamera.fieldOfView, targetFOV, speedFOVchange * Time.deltaTime);
}
private IEnumerator CrouchStand() {
if (isCrouching && Physics.Raycast(playerCamera.transform.position, Vector3.up, 1f))
yield break;
duringCrouchAnimation = true;
float timeElapsed = 0;
float targetHeight = isCrouching ? standHeight : crouchHeight;
float currentHeight = characterController.height;
Vector3 targetCenter = isCrouching ? standingCenter : crouchingCenter;
Vector3 currentCenter = characterController.center;
while(timeElapsed < timeToCrouch) {
characterController.height = Mathf.Lerp(currentHeight, targetHeight, timeElapsed/timeToCrouch);
characterController.center = Vector3.Lerp(currentCenter, targetCenter, timeElapsed/timeToCrouch);
timeElapsed += Time.deltaTime;
yield return null;
}
characterController.height = targetHeight;
characterController.center = targetCenter;
isCrouching = !isCrouching;
duringCrouchAnimation = false;
}
private void SpeedControl() {
Vector3 flatVel = new Vector3(characterController.velocity.x, 0, characterController.velocity.z);
targetSpeed = isCrouching ? crouchSpeed : isSprinting ? sprintSpeed : walkSpeed;
if (flatVel.magnitude > targetSpeed) {
flatVel = flatVel.normalized * targetSpeed;
moveDirection.x = flatVel.x;
moveDirection.z = flatVel.z;
}
}
}
Пожалуйста, вставь это в мой код, будь внимателен пожалуйстаASSISTANT
Давайте объединим систему управления персонажем в стиле FPS с механизмом бега по стенам, который вы предоставили ранее. Я добавлю необходимые методы и поля для обработки бега по стенам в ваш класс `FirstPersonController`.
Вот адаптированный код:
```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirstPersonController : MonoBehaviour
{
public CharacterController characterController;
public Camera mainCamera;
private Camera playerCamera;
private Vector3 moveDirection;
private Vector2 currentInput;
private float rotationX = 0;
public bool CanMove { get; private set; } = true;
#region Movement Options
private bool isSprinting => canSprint && !isCrouching && Input.GetKey(sprintKey);
private bool ShouldJump => Input.GetKeyDown(jumpKey) && !isCrouching && characterController.isGrounded;
private bool ShouldCrouch => Input.GetKeyDown(crouchKey) && !duringCrouchAnimation && characterController.isGrounded;
#endregion
#region Functional Options
[Header("Functional Options")]
[SerializeField] private bool canSprint = true;
[SerializeField] private bool canJump = true;
[SerializeField] private bool canCrouch = true;
[SerializeField] private bool canUseHeadbob = true;
[SerializeField] private bool canInteract = true;
#endregion
#region Controls
[Header("Controls")]
[SerializeField] private KeyCode sprintKey = KeyCode.LeftShift;
[SerializeField] private KeyCode jumpKey = KeyCode.Space;
[SerializeField] private KeyCode crouchKey = KeyCode.LeftControl;
[SerializeField] private KeyCode interactKey = KeyCode.E;
#endregion
#region Movement Parameters
[Header("Movement Parameters")]
[SerializeField] private float walkSpeed = 5.0f;
[SerializeField] private float sprintSpeed = 9.5f;
[SerializeField] private float crouchSpeed = 3.0f;
private float targetSpeed;
#endregion
#region Look Parameters
[Header("Look Parameters")]
[SerializeField, Range(1, 10)] private float lookSpeedX = 2.0f;
[SerializeField, Range(1, 10)] private float lookSpeedY = 2.0f;
[SerializeField, Range(1, 180)] private float upperLookLimit = 80.0f;
[SerializeField, Range(1, 180)] private float lowerLookLimit = 80.0f;
#endregion
#region Jumping Parameters
[Header("Jumping Parameters")]
[SerializeField] private float jumpForce = 9.0f;
[SerializeField] private float gravity = 30.0f;
#endregion
#region Crouch Parameters
[Header("Crouch Parameters")]
[SerializeField] private float crouchHeight = 0.5f;
[SerializeField] private float standHeight = 2f;
[SerializeField] private float timeToCrouch = 0.25f;
[SerializeField] private Vector3 crouchingCenter = new Vector3(0, 0.5f, 0);
[SerializeField] private Vector3 standingCenter = new Vector3(0, 0, 0);
private bool isCrouching;
private bool duringCrouchAnimation;
#endregion
#region Headbob Parameters
[Header("Headbob Parameters")]
[SerializeField] private float walkBobSpeed = 14f;
[SerializeField] private float walkBobAmount = 0.05f;
[SerializeField] private float sprintBobSpeed = 18f;
[SerializeField] private float sprintBobAmount = 0.11f;
[SerializeField] private float crouchBobSpeed = 8f;
[SerializeField] private float crouchBobAmount = 0.025f;
private float defaultYPos = 0;
private float defaultXPos = 0;
private float timer;
#endregion
#region FOV Manager
[Header("FOV Manager")]
[SerializeField] private float walkingFOV = 70f;
[SerializeField] private float sprintingFOV = 90f;
[SerializeField] private float speedFOVchange = 5f;
private float targetFOV;
#endregion
#region Interaction
[Header("Interaction")]
[SerializeField] private Vector3 interactionRayPoint = default;
[SerializeField] private float interactionDistance = default;
[SerializeField] private LayerMask interactionLayer = default;
private Interactable currentInteractable;
#endregion
#region Wall Running
[Header("Wall Running")]
[SerializeField] private LayerMask whatIsWall;
[SerializeField] private float wallRunForce;
[SerializeField] private float wallRunSpeed;
[SerializeField] private float wallJumpUpForce;
[SerializeField] private float wallJumpSlideForce;
[SerializeField] private float maxWallRunTime;
private float wallRunTimer;
private bool isWallRunning;
[Header("Wall Checker")]
[SerializeField] private float wallCheckDistance;
[SerializeField] private float minJumpHeight;
private RaycastHit wallLeftHit;
private RaycastHit wallRightHit;
private bool wallLeft;
private bool wallRight;
[Header("Exiting Wall Run")]
private bool exitingWall;
[SerializeField] private float exitWallTime;
private float exitWallTimer;
public Transform orientation;
#endregion
void Awake() {
playerCamera = GetComponentInChildren<Camera>();
characterController = GetComponent<CharacterController>();
defaultYPos = playerCamera.transform.localPosition.y;
defaultXPos = playerCamera.transform.localPosition.x;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
void Update() {
if (CanMove) {
HandleMovementInput();
HandleMouseLook();
ManagerFOV();
if (canJump) {
HandleJump();
}
if (canCrouch) {
HandleCrouch();
}
if (canUseHeadbob) {
HandleHeadbob();
}
if (canInteract) {
HandleInteractionCheck();
HandleInteractionInput();
}
CheckForWall();
StateMachine();
SpeedControl();
ApplyFinalMovements();
}
}
private void FixedUpdate() {
if (isWallRunning) {
WallRunMovement();
}
}
private void CheckForWall() {
wallLeft = Physics.Raycast(transform.position, -orientation.right, out wallLeftHit, wallCheckDistance, whatIsWall);
wallRight = Physics.Raycast(transform.position, orientation.right, out wallRightHit, wallCheckDistance, whatIsWall);
}
private bool AboveGround() {
return !characterController.isGrounded;
}
private void StateMachine() {
// Проверка состояния игрока
if ((wallLeft || wallRight) && Input.GetAxis("Vertical") > 0 && AboveGround() && !exitingWall) {
if (!isWallRunning) {
StartWallRun();
}
if (Input.GetKeyDown(jumpKey)) {
WallJump();
}
} else if (exitingWall) {
if (isWallRunning) {
StopWallRun();
}
if (exitWallTimer > 0) {
exitWallTimer -= Time.deltaTime;
}
if (exitWallTimer <= 0) {
exitingWall = false;
}
} else {
if (isWallRunning) {
StopWallRun();
}
}
}
private void StartWallRun() {
isWallRunning = true;
moveDirection.y = 0; // Сбросить вертикальную скорость
}
private void WallRunMovement() {
// Получение нормали стены
Vector3 wallNormal = wallRight ? wallRightHit.normal : wallLeftHit.normal;
Vector3 wallForward = Vector3.Cross(transform.up, wallNormal).normalized;
// Перемещение по стене
moveDirection = wallForward * wallRunSpeed;
moveDirection.y = 0; // Установить вертикальную скорость в 0, чтобы избежать падения
}
private void StopWallRun() {
isWallRunning = false;
}
private void WallJump() {
exitingWall = true;
exitWallTimer = exitWallTime;
Vector3 wallNormal = wallRight ? wallRightHit.normal : wallLeftHit.normal;
Vector3 jumpDirection = transform.up * wallJumpUpForce + wallNormal * wallJumpSlideForce;
moveDirection.y = jumpDirection.y; // Устанавливаем вертикальную скорость для прыжка
}
private void HandleMovementInput() {
currentInput = new Vector2(
(isSprinting && !isWallRunning ? sprintSpeed : isWallRunning ? wallRunSpeed : isCrouching ? crouchSpeed : walkSpeed) * Input.GetAxis("Vertical"),
(isSprinting && !isWallRunning ? sprintSpeed : isWallRunning ? wallRunSpeed : isCrouching ? crouchSpeed : walkSpeed) * Input.GetAxis("Horizontal")
);
float moveDirectionY = moveDirection.y;
moveDirection = (transform.TransformDirection(Vector3.forward) * currentInput.x) + (transform.TransformDirection(Vector3.right) * currentInput.y);
moveDirection.y = moveDirectionY;
}
private void HandleMouseLook() {
rotationX -= Input.GetAxis("Mouse Y") * lookSpeedY;
rotationX = Mathf.Clamp(rotationX, -upperLookLimit, lowerLookLimit);
playerCamera.transform.localRotation = Quaternion.Euler(rotationX, 0, 0);
transform.rotation *= Quaternion.Euler(0, Input.GetAxis("Mouse X") * lookSpeedX, 0);
}
private void HandleJump() {
if (ShouldJump) {
moveDirection.y = jumpForce;
}
}
private void HandleCrouch() {
if (ShouldCrouch) {
StartCoroutine(CrouchStand());
}
}
private void HandleHeadbob() {
if (!characterController.isGrounded) return;
if (Mathf.Abs(moveDirection.x) > 0.1f || Mathf.Abs(moveDirection.z) > 0.1f) {
timer += Time.deltaTime * (isCrouching ? crouchBobSpeed : isSprinting ? sprintBobSpeed : walkBobSpeed);
playerCamera.transform.localPosition = new Vector3(
defaultXPos + Mathf.Sin(timer) * (isCrouching ? crouchBobAmount * 0.25f : isSprinting ? sprintBobAmount * 0.25f : walkBobAmount * 0.25f),
defaultYPos + Mathf.Sin(timer) * (isCrouching ? crouchBobAmount : isSprinting ? sprintBobAmount : walkBobAmount),
playerCamera.transform.localPosition.z
);
}
}
private void ApplyFinalMovements() {
if (!characterController.isGrounded) {
moveDirection.y -= gravity * Time.deltaTime;
}
characterController.Move(moveDirection * Time.deltaTime);
}
private void ManagerFOV() {
targetFOV = isSprinting && characterController.velocity.magnitude >= sprintSpeed * 0.5f ? sprintingFOV : walkingFOV;
mainCamera.fieldOfView = Mathf.Lerp(mainCamera.fieldOfView, targetFOV, speedFOVchange * Time.deltaTime);
}
private IEnumerator CrouchStand() {
if (isCrouching && Physics.Raycast(playerCamera.transform.position, Vector3.up, 1f))
yield break;
duringCrouchAnimation = true;
float timeElapsed = 0;
float targetHeight = isCrouching ? standHeight : crouchHeight;
float currentHeight = characterController.height;
Vector3 targetCenter = isCrouching ? standingCenter : crouchingCenter;
Vector3 currentCenter = characterController.center;
while(timeElapsed < timeToCrouch) {
characterController.height = Mathf.Lerp(currentHeight, targetHeight, timeElapsed/timeToCrouch);
characterController.center = Vector3.Lerp(currentCenter, targetCenter, timeElapsed/timeToCrouch);
timeElapsed += Time.deltaTime;
yield return null;
}
characterController.height = targetHeight;
characterController.center = targetCenter;
isCrouching = !isCrouching;
duringCrouchAnimation = false;
}
private void SpeedControl() {
Vector3 flatVel = new Vector3(characterController.velocity.x, 0, characterController.velocity.z);
targetSpeed = isCrouching ? crouchSpeed : isSprinting ? sprintSpeed : walkSpeed;
if (flatVel.magnitude > targetSpeed) {
flatVel = flatVel.normalized * targetSpeed;
moveDirection.x = flatVel.x;
moveDirection.z = flatVel.z;
}
}
}
```
### Изменения и дополнения:
1. **Структура класса**: Я добавил блоки региона для стенового бега, взаимодействия и других аспектов, чтобы сделать код легче для чтения.
2. **Методы бега по стенам**: `CheckForWall`, `StateMachine`, `StartWallRun`, `WallRunMovement`, `StopWallRun` и `WallJump` добавлены, чтобы обеспечить функциональность бега по стенам.
3. **Обработка состояний**: В `StateMachine` добавлена логика для проверки, когда начинать и заканчивать бег по стенам.
4. **Параметры прыжка**: Учитывая состояние бега по стенам, вертикальная скорость корректируется.
Попробуйте интегрировать этот код в вашу игру и дайте знать, если требуется внести дополнительные изменения!