MiniGames/Assets/Scripts/CrateEscape/CratePlayerController.cs
2025-08-14 20:29:09 +05:00

206 lines
6.6 KiB
C#

using UnityEngine;
using DG.Tweening;
/// Hold anywhere to move at base speed; drag up to boost speed.
/// Tap left/right half of screen for rotation.
/// Keyboard still works (Up hold to move, Left/Right to rotate).
[RequireComponent(typeof(Rigidbody))]
public class CratePlayerController : MonoBehaviour
{
[Header("Movement")]
public float moveSpeed = 4f; // base forward speed
public float moveDamp = 12f; // smoothing of velocity changes
public float maxSpeedMultiplier = 3f; // boost cap when swiping up
[Header("Rotation (single-step)")]
public float quarterTurnDuration = 0.12f;
public Ease quarterTurnEase = Ease.OutCubic;
[Header("Touch Settings")]
public float tapMaxDuration = 0.18f; // max time to register as tap
public float tapMaxMovement = 22f; // px movement allowance for tap
public float swipeUpDeadZonePx = 10f; // upward drift before speed changes
public float swipeUpPixelsForMax = 260f; // px up to hit max multiplier
[Header("Grounding")]
public LayerMask groundMask = ~0;
public float groundCheckRadius = 0.2f;
public Transform groundCheck;
[Header("Optional Animator Driver")]
public ZibuAnimDriver anim;
Rigidbody _rb;
Tween _rotTween;
Vector3 _vel;
// Touch state
int _fingerId = -1;
Vector2 _startPos;
float _startTime;
bool _movingViaTouch = false;
float _touchSpeedMult = 1f;
float _targetY; // rotation target memory
void Awake()
{
_rb = GetComponent<Rigidbody>();
_rb.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
if (!groundCheck) groundCheck = transform;
if (!anim) anim = GetComponent<ZibuAnimDriver>();
}
void Update()
{
ReadKeyboard();
ReadTouch();
if (anim)
{
// Use desired speed (instant) instead of current velocity (which lags due to damping)
bool forwardHeld = _movingViaTouch || Input.GetKey(KeyCode.UpArrow);
float mult = _movingViaTouch ? _touchSpeedMult : 1f;
float speedUnits = forwardHeld ? mult : 0f; // 0=idle, 1=run, 2=runfast, 3=sprint
anim.SetGrounded(IsGrounded());
anim.SetSpeed(Mathf.Clamp(speedUnits, 0f, maxSpeedMultiplier));
}
}
void FixedUpdate()
{
bool forwardHeld = _movingViaTouch || Input.GetKey(KeyCode.UpArrow);
float mult = _movingViaTouch ? _touchSpeedMult : 1f;
Vector3 targetVel = forwardHeld ? transform.forward * (moveSpeed * mult) : Vector3.zero;
_vel = Vector3.Lerp(_vel, targetVel, 1f - Mathf.Exp(-moveDamp * Time.fixedDeltaTime));
_rb.MovePosition(_rb.position + _vel * Time.fixedDeltaTime);
}
// ---------------- Keyboard ----------------
void ReadKeyboard()
{
if (Input.GetKeyDown(KeyCode.LeftArrow)) StepTurn(-1);
if (Input.GetKeyDown(KeyCode.RightArrow)) StepTurn(+1);
}
// ---------------- Touch ----------------
void ReadTouch()
{
if (Input.touchCount == 0)
{
if (_fingerId != -1)
{
_movingViaTouch = false;
_touchSpeedMult = 1f;
_fingerId = -1;
}
return;
}
for (int i = 0; i < Input.touchCount; i++)
{
var t = Input.GetTouch(i);
// Start tracking first finger
if (t.phase == TouchPhase.Began && _fingerId == -1)
{
_fingerId = t.fingerId;
_startPos = t.position;
_startTime = Time.time;
_movingViaTouch = true; // start moving immediately
_touchSpeedMult = 1f; // base speed
}
if (t.fingerId != _fingerId) continue;
Vector2 delta = t.position - _startPos;
float dt = Time.time - _startTime;
// Boost speed if swiping upward
if (delta.y > swipeUpDeadZonePx)
{
float up = Mathf.Clamp(delta.y, 0f, swipeUpPixelsForMax);
float t01 = up / Mathf.Max(1f, swipeUpPixelsForMax);
_touchSpeedMult = Mathf.Lerp(1f, maxSpeedMultiplier, t01);
}
else
{
_touchSpeedMult = 1f;
}
if (t.phase == TouchPhase.Ended || t.phase == TouchPhase.Canceled)
{
bool isTap = (dt <= tapMaxDuration) && (delta.magnitude <= tapMaxMovement);
if (isTap)
{
// tap rotation
if (_startPos.x <= Screen.width * 0.5f) StepTurn(-1);
else StepTurn(+1);
}
// stop movement
_movingViaTouch = false;
_touchSpeedMult = 1f;
_fingerId = -1;
}
}
}
// ---------------- Turning ----------------
public void StepTurn(int direction)
{
if (direction == 0) return;
float baseY = transform.eulerAngles.y;
if (_rotTween != null && _rotTween.active && _rotTween.IsPlaying())
baseY = _targetY;
_targetY = Snap90(baseY + 90f * Mathf.Sign(direction));
// ensure only one tween & unlock yaw for the duration
_rotTween?.Kill(false);
// Unlock Y so tween can rotate
var frozen = _rb.constraints;
_rb.constraints = frozen & ~RigidbodyConstraints.FreezeRotationY;
_rotTween = transform
.DORotate(new Vector3(0f, _targetY, 0f), quarterTurnDuration, RotateMode.Fast)
.SetEase(quarterTurnEase)
.OnKill(() =>
{
// Re-freeze Y after rotation completes or is killed
_rb.constraints = frozen | RigidbodyConstraints.FreezeRotationY;
})
.OnComplete(() =>
{
// also re-freeze on normal completion (redundant but safe)
_rb.constraints = frozen | RigidbodyConstraints.FreezeRotationY;
});
}
static float Snap90(float y)
{
float snapped = Mathf.Round(y / 90f) * 90f;
return Mathf.Repeat(snapped, 360f);
}
// ---------------- Grounding ----------------
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);
}
}