420 lines
15 KiB
C#
420 lines
15 KiB
C#
using OctoCore.Inputs;
|
|
using System.Globalization;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
namespace OctoCore.Components
|
|
{
|
|
public class OctoPlayer : MonoBehaviour
|
|
{
|
|
public static OctoPlayer Instance { get; private set; }
|
|
|
|
public enum PlayerModes { FirstPerson, ThirdPerson }
|
|
public enum Orientations { World, Camera }
|
|
|
|
[System.Serializable]
|
|
public class StateMachine
|
|
{
|
|
public bool IsFocussed;
|
|
public bool IsPaused;
|
|
public bool IsRunning;
|
|
public float Yaw;
|
|
public float Pitch;
|
|
public float SpeedMultiplier = 1;
|
|
public OctoPlayerDriver Driver;
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class References
|
|
{
|
|
public CharacterController Controller;
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class Settings
|
|
{
|
|
public PlayerModes Mode = PlayerModes.FirstPerson;
|
|
public Orientations Orientation = Orientations.Camera;
|
|
|
|
[Tooltip("Allow position movement.")]
|
|
public bool AllowMove = true;
|
|
|
|
[Tooltip("Allow looking around.")]
|
|
public bool AllowLook = true;
|
|
|
|
[Tooltip("Allow run.")]
|
|
public bool AllowRun = true;
|
|
|
|
[Tooltip("Allow jump.")]
|
|
public bool AllowJump = true;
|
|
|
|
[Tooltip("Walk speed (km/h).")]
|
|
public float WalkSpeed = 10;
|
|
|
|
[Tooltip("Run speed (km/h).")]
|
|
public float RunSpeed = 20;
|
|
|
|
[Tooltip("Jump duration (seconds).")]
|
|
public float JumpDuration = .4f;
|
|
|
|
[Tooltip("Maximum jump height reached at the apex (meters).")]
|
|
public float JumpHeight = .4f;
|
|
|
|
[Tooltip("Downward acceleration applied to the player (m/s²).")]
|
|
public float Gravity = 9.8f;
|
|
}
|
|
|
|
[System.Serializable]
|
|
private class Mapping
|
|
{
|
|
public OctoActions MoveAction;
|
|
public OctoActions LookAction;
|
|
public OctoActions RunAction;
|
|
public OctoActions JumpAction;
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class FirstPerson
|
|
{
|
|
[Header("References")]
|
|
|
|
public Camera Camera;
|
|
|
|
[Header("Settings")]
|
|
|
|
[Tooltip("Horizontal sensitivity (Yaw).")]
|
|
public float SensitivityX = 0.1f;
|
|
|
|
[Tooltip("Vertical sensitivity (Pitch).")]
|
|
public float SensitivityY = 0.1f;
|
|
|
|
[Tooltip("Minimum pitch (looking down).")]
|
|
public float MinPitch = -80f;
|
|
|
|
[Tooltip("Maximum pitch (looking up).")]
|
|
public float MaxPitch = 80f;
|
|
|
|
[Tooltip("Reference yaw.")]
|
|
[Range(-180, 180)] public float ReferenceYaw = 0;
|
|
|
|
[Tooltip("Range yaw.")]
|
|
[Range(0, 360)] public float RangeYaw = 360;
|
|
}
|
|
|
|
[SerializeField, OctoReadOnly] private StateMachine _state;
|
|
[SerializeField] private References _references;
|
|
[SerializeField] private Settings _settings;
|
|
[SerializeField] private Mapping _mapping;
|
|
[SerializeField] private FirstPerson _firstPerson;
|
|
|
|
private bool _wasPausedBeforeUnfocuss;
|
|
private bool _jumpInput;
|
|
private bool _jumpping;
|
|
private bool _runInput;
|
|
private float _runInertia;
|
|
private float _jumpLerp;
|
|
private float _jumpTime;
|
|
private float _jumpAltitude;
|
|
private Vector2 _moveInput;
|
|
private Vector2 _lookInput;
|
|
private Vector3 _moveInertia;
|
|
private Vector3 _moveDirection;
|
|
|
|
public bool IsPaused => _state.IsPaused;
|
|
public float Radius => _references.Controller ? _references.Controller.radius : 0;
|
|
public StateMachine State => _state;
|
|
public FirstPerson _FirstPerson => _firstPerson;
|
|
|
|
void OnEnable()
|
|
{
|
|
if (!Instance) Instance = this;
|
|
else Destroy(gameObject);
|
|
|
|
OctoCore.Instance.Inputs.SubscribeAction(_mapping.MoveAction, Move);
|
|
OctoCore.Instance.Inputs.SubscribeAction(_mapping.LookAction, Look);
|
|
OctoCore.Instance.Inputs.SubscribeAction(_mapping.RunAction, Run);
|
|
OctoCore.Instance.Inputs.SubscribeAction(_mapping.JumpAction, Jump);
|
|
|
|
_state.Yaw = transform.eulerAngles.y;
|
|
|
|
_state.IsFocussed = Application.isFocused;
|
|
|
|
Pause(_state.IsPaused);
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
OctoCore.Instance.Inputs.UnsubscribeAction(_mapping.MoveAction, Move);
|
|
OctoCore.Instance.Inputs.UnsubscribeAction(_mapping.RunAction, Run);
|
|
OctoCore.Instance.Inputs.UnsubscribeAction(_mapping.JumpAction, Jump);
|
|
|
|
if (Instance == this) Instance = null;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
HandleFocus();
|
|
HandleMovement();
|
|
}
|
|
|
|
public void Teleport(string positionAndYaw)
|
|
{
|
|
string[] s = positionAndYaw.Replace(" ", string.Empty).Split(',');
|
|
float x = float.Parse(s[0], CultureInfo.InvariantCulture.NumberFormat);
|
|
float y = float.Parse(s[1], CultureInfo.InvariantCulture.NumberFormat);
|
|
float z = float.Parse(s[2], CultureInfo.InvariantCulture.NumberFormat);
|
|
float a = float.Parse(s[3], CultureInfo.InvariantCulture.NumberFormat);
|
|
|
|
Teleport(new Vector3(x, y, z), Quaternion.Euler(0, a, 0));
|
|
}
|
|
|
|
public void Teleport(Vector3 position, Quaternion rotation)
|
|
{
|
|
_state.IsPaused = _jumpInput = _jumpping = _runInput = false;
|
|
_runInertia = _jumpLerp = _jumpTime = _jumpAltitude = 0;
|
|
_moveInput = _lookInput = Vector2.zero;
|
|
_moveInertia = _moveDirection = Vector3.zero;
|
|
_state.Yaw = rotation.eulerAngles.y;
|
|
_state.Pitch = 0;
|
|
|
|
if (_references.Controller)
|
|
_references.Controller.enabled = false;
|
|
|
|
transform.position = position;
|
|
transform.rotation = rotation;
|
|
|
|
if (_references.Controller)
|
|
_references.Controller.enabled = true;
|
|
}
|
|
|
|
public void Pause(bool pause)
|
|
{
|
|
_state.IsPaused = pause;
|
|
|
|
if (OctoCore.Instance)
|
|
OctoCore.Instance.UI.SetCursor();
|
|
}
|
|
|
|
public void SetSppedMultiplier(float multiplier) { _state.SpeedMultiplier = multiplier; }
|
|
|
|
public void AllowMove(bool allow) { _settings.AllowMove = allow; }
|
|
|
|
public void Drive(OctoPlayerDriver driver) { _state.Driver = driver; }
|
|
public void ReleaseDrive() { _state.Driver = null; }
|
|
|
|
public void SetYaw(float yaw) { _state.Yaw = yaw; }
|
|
public void SetPitch(float pitch) { _state.Yaw = pitch; }
|
|
|
|
private void Move(Vector2 value) { _moveInput = value; }
|
|
private void Look(Vector2 value) { _lookInput = value; }
|
|
private void Run() { _runInput = _settings.AllowRun; }
|
|
private void Jump() { _jumpInput = _settings.AllowJump; }
|
|
|
|
private void HandleFocus()
|
|
{
|
|
bool escapePressed = false;
|
|
bool mouseClicked = false;
|
|
#if ENABLE_INPUT_SYSTEM
|
|
escapePressed = Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame;
|
|
mouseClicked =
|
|
Mouse.current != null &&
|
|
(
|
|
Mouse.current.leftButton.wasPressedThisFrame ||
|
|
Mouse.current.rightButton.wasPressedThisFrame ||
|
|
Mouse.current.middleButton.wasPressedThisFrame ||
|
|
Mouse.current.forwardButton.wasPressedThisFrame ||
|
|
Mouse.current.backButton.wasPressedThisFrame
|
|
);
|
|
#else
|
|
escapePressed = Input.GetKeyDown(KeyCode.Escape);
|
|
mouseClicked =
|
|
Input.GetMouseButtonDown(0) ||
|
|
Input.GetMouseButtonDown(1) ||
|
|
Input.GetMouseButtonDown(2);
|
|
#endif
|
|
|
|
|
|
if (!Application.isFocused || escapePressed)
|
|
{
|
|
if (_state.IsFocussed)
|
|
{
|
|
_state.IsFocussed = false;
|
|
_wasPausedBeforeUnfocuss = _state.IsPaused;
|
|
Pause(true);
|
|
}
|
|
}
|
|
else if (!_state.IsFocussed && mouseClicked)
|
|
{
|
|
_state.IsFocussed = true;
|
|
if (!_wasPausedBeforeUnfocuss)
|
|
Pause(false);
|
|
}
|
|
}
|
|
|
|
private void HandleMovement()
|
|
{
|
|
_moveInput = (_state.IsPaused || !_state.IsFocussed || !_settings.AllowMove) ? Vector2.zero : Vector2.ClampMagnitude(_moveInput, 1);
|
|
Camera cam = (_firstPerson.Camera && _firstPerson.Camera.enabled && _firstPerson.Camera.gameObject.activeInHierarchy) ? _firstPerson.Camera : Camera.main;
|
|
|
|
bool accelerating = _moveInput.magnitude > .1f;
|
|
bool isGrounded = _references.Controller ? _references.Controller.isGrounded : true;
|
|
float t = Time.deltaTime / 3.6f;
|
|
|
|
// Move
|
|
|
|
Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y);
|
|
|
|
if (_settings.Orientation == Orientations.Camera && cam)
|
|
{
|
|
Vector3 camForward = cam.transform.forward;
|
|
camForward.y = 0;
|
|
camForward.Normalize();
|
|
|
|
Vector3 camRight = cam.transform.right;
|
|
camRight.y = 0;
|
|
camRight.Normalize();
|
|
|
|
move = camRight * move.x + camForward * move.z;
|
|
}
|
|
|
|
if (_state.Driver)
|
|
_state.Driver.ApplyMoveInfluence(move, t, out move);
|
|
|
|
float deltaSmooth = t * (accelerating ? 30 : 15);
|
|
_moveDirection = Vector3.Lerp(_moveDirection, move, deltaSmooth * 1.5f);
|
|
|
|
deltaSmooth *= 1 - Vector3.Cross(_moveInertia, _moveDirection).magnitude;
|
|
_moveInertia = Vector3.Lerp(_moveInertia, move, deltaSmooth);
|
|
|
|
if (!accelerating && _moveInertia.magnitude < .01f) _moveInertia = Vector3.zero;
|
|
|
|
_runInertia = Mathf.Lerp(_runInertia, _runInput ? 1 : 0, t * (accelerating ? 5 : 3));
|
|
_state.IsRunning = _runInertia > .1f;
|
|
|
|
Vector3 moveDelta = _moveInertia * t * Mathf.Lerp(_settings.WalkSpeed, _settings.RunSpeed, _runInertia) * _state.SpeedMultiplier;
|
|
|
|
if (isGrounded && _jumpInput && !_jumpping)
|
|
{
|
|
_jumpInput = false;
|
|
_jumpping = true;
|
|
_jumpLerp = 0;
|
|
_jumpTime = Time.timeSinceLevelLoad;
|
|
_jumpAltitude = transform.position.y;
|
|
}
|
|
|
|
float jumpOffset = 0;
|
|
|
|
if (_jumpping)
|
|
{
|
|
_jumpInput = false;
|
|
_jumpLerp = (Time.timeSinceLevelLoad - _jumpTime) / _settings.JumpDuration;
|
|
|
|
if (_jumpLerp >= 1 || (_jumpLerp > .3f && isGrounded))
|
|
{
|
|
_jumpping = false;
|
|
_jumpLerp = 0;
|
|
}
|
|
else
|
|
{
|
|
float x = _jumpLerp;
|
|
x = x * 2 - 1;
|
|
|
|
float y;
|
|
if (x < 0) y = Mathf.Sqrt(1f - x * x);
|
|
else y = Mathf.Cos(x * .5f * Mathf.PI);
|
|
|
|
y = _jumpAltitude + y * _settings.JumpHeight;
|
|
|
|
jumpOffset = y - transform.position.y;
|
|
}
|
|
}
|
|
|
|
if (_jumpping)
|
|
moveDelta.y = jumpOffset;
|
|
else
|
|
moveDelta.y -= _settings.Gravity * Time.deltaTime;
|
|
|
|
if (_references.Controller && _references.Controller.enabled)
|
|
_references.Controller.Move(moveDelta);
|
|
else
|
|
transform.position += moveDelta;
|
|
|
|
// Rotate
|
|
|
|
switch (_settings.Mode)
|
|
{
|
|
case PlayerModes.FirstPerson:
|
|
if (cam && !_state.IsPaused)
|
|
{
|
|
Vector2 delta = (_state.IsPaused || !_state.IsFocussed) ? Vector2.zero : _lookInput;
|
|
|
|
if (_settings.AllowLook)
|
|
{
|
|
float mouseX = delta.x * _firstPerson.SensitivityX;
|
|
float mouseY = delta.y * _firstPerson.SensitivityY * -1;
|
|
|
|
_state.Yaw += mouseX;
|
|
float halfRange = _firstPerson.RangeYaw * 0.5f;
|
|
float deltaYaw = Mathf.DeltaAngle(_firstPerson.ReferenceYaw, _state.Yaw);
|
|
deltaYaw = Mathf.Clamp(deltaYaw, -halfRange, halfRange);
|
|
_state.Yaw = _firstPerson.ReferenceYaw + deltaYaw;
|
|
|
|
_state.Pitch = Mathf.Clamp(_state.Pitch + mouseY, _firstPerson.MinPitch, _firstPerson.MaxPitch);
|
|
}
|
|
|
|
if (_state.Driver)
|
|
_state.Driver.ApplyLookInfluence(_state.Yaw, _state.Pitch, Time.deltaTime, out _state.Yaw, out _state.Pitch);
|
|
|
|
Quaternion YawRot = Quaternion.Euler(0, _state.Yaw, 0);
|
|
Quaternion PitchRot = Quaternion.Euler(_state.Pitch, 0, 0);
|
|
|
|
transform.rotation = YawRot;
|
|
cam.transform.localRotation = PitchRot;
|
|
}
|
|
break;
|
|
|
|
case PlayerModes.ThirdPerson:
|
|
{
|
|
Quaternion targetRot = transform.rotation;
|
|
if (_moveInertia.magnitude > .002f)
|
|
{
|
|
Vector3 offset = _moveInertia - _moveDirection;
|
|
offset *= _moveInertia.magnitude * _runInertia;
|
|
offset = Vector3.ClampMagnitude(offset * 1f, 1f);
|
|
Vector3 up = (Vector3.up - offset).normalized;
|
|
Quaternion lookRot = Quaternion.LookRotation(_moveInertia, up);
|
|
targetRot = Quaternion.Lerp(targetRot, lookRot, t * 30);
|
|
}
|
|
transform.rotation = targetRot;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Reset
|
|
|
|
_moveInput = Vector2.zero;
|
|
_lookInput = Vector2.zero;
|
|
_runInput = false;
|
|
}
|
|
|
|
private void SetMode()
|
|
{
|
|
switch (_settings.Mode)
|
|
{
|
|
case PlayerModes.FirstPerson:
|
|
// Activate First Person Camera
|
|
break;
|
|
|
|
case PlayerModes.ThirdPerson:
|
|
// Activate Third Person Camera
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*public static float Arc(float x)
|
|
{
|
|
x = Mathf.Clamp01(x) * 2 - 1f;
|
|
return Mathf.Sqrt(1f - x * x);
|
|
}*/
|
|
}
|
|
} |