206 lines
6.6 KiB
C#
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);
|
|
}
|
|
}
|