209 lines
6.6 KiB
C#
209 lines
6.6 KiB
C#
using UnityEngine;
|
|
|
|
// Joystick Pack (Asset Store)
|
|
// Drag any Joystick (Fixed/Floating/Dynamic/Variable) into moveJoystick
|
|
[RequireComponent(typeof(Rigidbody))]
|
|
public class CrateEscapePlayerControllerJoystick : MonoBehaviour
|
|
{
|
|
[Header("Joystick")]
|
|
public Joystick moveJoystick; // assign from Joystick Pack
|
|
|
|
[Header("Movement")]
|
|
public float moveSpeed = 4f; // base speed
|
|
public float moveDamp = 12f; // velocity smoothing
|
|
public float maxSpeedMultiplier = 3f; // full-stick multiplier
|
|
[Range(0f, 0.5f)] public float inputDeadZone = 0.1f;
|
|
|
|
[Tooltip("If true, stick is interpreted relative to the camera view.")]
|
|
public bool cameraRelative = true;
|
|
public Transform cameraTransform; // auto-fills with Camera.main if null
|
|
|
|
[Header("Facing")]
|
|
public bool faceMoveDirection = true; // rotate toward move direction
|
|
public float turnSpeed = 540f; // deg/sec rotation speed
|
|
|
|
[Header("Grounding")]
|
|
public LayerMask groundMask = ~0;
|
|
public float groundCheckRadius = 0.2f;
|
|
public Transform groundCheck;
|
|
|
|
[Header("Optional Animator Driver")]
|
|
public ZibuAnimDriver anim;
|
|
|
|
[Header("Input Locks")]
|
|
public float initialLockSeconds = 4f; // joystick disabled for first N seconds
|
|
public bool useUnscaledForInitialLock = false; // set true if you pause time at start
|
|
public bool autoLockOnGameOver = true; // auto-freeze if GameManager reports isGameOver
|
|
|
|
// --- runtime ---
|
|
Rigidbody _rb;
|
|
Vector3 _vel; // smoothed velocity
|
|
Vector3 _moveDirWorld; // desired movement direction in world space
|
|
float _stickMag; // 0..1
|
|
bool _inputEnabled = false; // starts locked; enabled after initialLockSeconds
|
|
|
|
void Awake()
|
|
{
|
|
_rb = GetComponent<Rigidbody>();
|
|
// keep physics stable
|
|
_rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
|
|
|
|
if (!groundCheck) groundCheck = transform;
|
|
if (!anim) anim = GetComponent<ZibuAnimDriver>();
|
|
if (!cameraTransform && Camera.main) cameraTransform = Camera.main.transform;
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
// start locked, then enable after delay
|
|
_inputEnabled = false;
|
|
StopAllCoroutines();
|
|
StartCoroutine(EnableAfterDelay(initialLockSeconds, useUnscaledForInitialLock));
|
|
}
|
|
|
|
System.Collections.IEnumerator EnableAfterDelay(float seconds, bool unscaled)
|
|
{
|
|
if (seconds > 0f)
|
|
{
|
|
if (unscaled) yield return new WaitForSecondsRealtime(seconds);
|
|
else yield return new WaitForSeconds(seconds);
|
|
}
|
|
_inputEnabled = true;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
bool blocked = IsBlocked();
|
|
|
|
if (!blocked)
|
|
ReadJoystick();
|
|
else
|
|
{
|
|
// when blocked, zero input so the player stops + anim goes idle
|
|
_stickMag = 0f;
|
|
_moveDirWorld = Vector3.zero;
|
|
}
|
|
|
|
if (anim)
|
|
{
|
|
float speedUnits = Mathf.Lerp(0f, maxSpeedMultiplier, _stickMag);
|
|
anim.SetGrounded(IsGrounded());
|
|
anim.SetSpeed(speedUnits);
|
|
}
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
bool blocked = IsBlocked();
|
|
|
|
Vector3 targetVel = Vector3.zero;
|
|
|
|
if (!blocked && _stickMag > inputDeadZone)
|
|
{
|
|
Vector3 dir = _moveDirWorld.normalized;
|
|
float targetSpeed = moveSpeed * Mathf.Lerp(0f, maxSpeedMultiplier, _stickMag);
|
|
targetVel = dir * targetSpeed;
|
|
|
|
if (faceMoveDirection)
|
|
{
|
|
Quaternion targetRot = Quaternion.LookRotation(dir, Vector3.up);
|
|
_rb.MoveRotation(Quaternion.RotateTowards(_rb.rotation, targetRot, turnSpeed * Time.fixedDeltaTime));
|
|
}
|
|
}
|
|
|
|
if (blocked)
|
|
{
|
|
// hard-stop while locked
|
|
_vel = Vector3.zero;
|
|
_rb.velocity = Vector3.zero;
|
|
return; // don't MovePosition this frame
|
|
}
|
|
|
|
// Smooth velocity and move
|
|
_vel = Vector3.Lerp(_vel, targetVel, 1f - Mathf.Exp(-moveDamp * Time.fixedDeltaTime));
|
|
_rb.MovePosition(_rb.position + _vel * Time.fixedDeltaTime);
|
|
}
|
|
|
|
void ReadJoystick()
|
|
{
|
|
if (!moveJoystick) { _stickMag = 0f; _moveDirWorld = Vector3.zero; return; }
|
|
|
|
float h = moveJoystick.Horizontal; // -1..1
|
|
float v = moveJoystick.Vertical; // -1..1
|
|
|
|
Vector2 stick = new Vector2(h, v);
|
|
_stickMag = Mathf.Clamp01(stick.magnitude);
|
|
|
|
if (_stickMag <= inputDeadZone) { _moveDirWorld = Vector3.zero; return; }
|
|
|
|
Vector2 n = stick.normalized;
|
|
|
|
if (cameraRelative && cameraTransform)
|
|
{
|
|
// Project camera forward to XZ and build a right vector
|
|
Vector3 camF = cameraTransform.forward; camF.y = 0f;
|
|
camF = camF.sqrMagnitude > 0.0001f ? camF.normalized : Vector3.forward;
|
|
Vector3 camR = new Vector3(camF.z, 0f, -camF.x);
|
|
|
|
_moveDirWorld = camR * n.x + camF * n.y;
|
|
}
|
|
else
|
|
{
|
|
// World-relative (X/Z)
|
|
_moveDirWorld = new Vector3(n.x, 0f, n.y);
|
|
}
|
|
}
|
|
|
|
bool IsGrounded()
|
|
{
|
|
return Physics.CheckSphere(groundCheck.position, groundCheckRadius, groundMask, QueryTriggerInteraction.Ignore);
|
|
}
|
|
|
|
void OnDrawGizmosSelected()
|
|
{
|
|
if (!groundCheck) groundCheck = transform;
|
|
Gizmos.color = Color.yellow;
|
|
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
|
|
}
|
|
|
|
// ---------- Public API ----------
|
|
|
|
/// <summary>Enable/disable all player input & movement immediately.</summary>
|
|
public void LockControls(bool locked)
|
|
{
|
|
_inputEnabled = !locked;
|
|
if (locked)
|
|
{
|
|
_stickMag = 0f;
|
|
_moveDirWorld = Vector3.zero;
|
|
_vel = Vector3.zero;
|
|
if (_rb) _rb.velocity = Vector3.zero;
|
|
if (anim) { anim.SetSpeed(0f); }
|
|
}
|
|
}
|
|
|
|
/// <summary>Enable controls now (e.g., if you want to skip the initial delay).</summary>
|
|
public void EnableControlsNow()
|
|
{
|
|
StopAllCoroutines();
|
|
_inputEnabled = true;
|
|
}
|
|
|
|
// ---------- Helpers ----------
|
|
bool IsBlocked()
|
|
{
|
|
if (!_inputEnabled) return true;
|
|
|
|
if (autoLockOnGameOver)
|
|
{
|
|
var gm = CrateEscapeGameManager.Instance;
|
|
if (gm != null && gm.isGameOver) return true; // freeze after game over
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// For runtime hookup if you spawn the joystick UI
|
|
public void SetJoystick(Joystick j) => moveJoystick = j;
|
|
}
|