using UnityEngine; using DG.Tweening; public class PenWriter : MonoBehaviour { [Header("Assign the transform that moves up/down (pen or tip root)")] public Transform pen; // If null, uses this.transform [Header("Motion Settings")] [Min(0f)] public float minDown = 0.2f; // minimum down stroke depth (in local units) [Min(0f)] public float maxDown = 0.6f; // maximum down stroke depth [Min(0f)] public float minDur = 0.04f; // min duration per tap [Min(0f)] public float maxDur = 0.10f; // max duration per tap [Range(0f, 1f)] public float upFraction = 0.35f; // fraction of tap duration for the up motion (snappy up) private Vector3 localUpAxis; private Vector3 baseLocalPos; private bool writing; void Awake() { if (!pen) pen = transform; baseLocalPos = pen.localPosition; localUpAxis = transform.InverseTransformDirection(Vector3.up); } public void StartWriting() { if (writing) return; writing = true; // Ensure any previous tweens are stopped and we reset to rest DOTween.Kill(pen, complete: false); pen.localPosition = baseLocalPos; // We’ll add a callback that keeps enqueuing the next tap until stopped EnqueueNextTap(); } public void StopWriting(bool snapToRest = true) { writing = false; DOTween.Kill(pen); if (snapToRest) pen.localPosition = baseLocalPos; else { // Smoothly return to rest pen.DOLocalMove(baseLocalPos, 0.08f) .SetEase(Ease.OutSine); } } private void EnqueueNextTap() { if (!writing) return; float depth = Random.Range(minDown, maxDown); float dur = Random.Range(minDur, maxDur); Vector3 downPos = baseLocalPos - localUpAxis * depth; float upDur = Mathf.Max(0.01f, dur * upFraction); float downDur = Mathf.Max(0.01f, dur - upDur); // Down (contact), then Up (lift) pen.DOLocalMove(downPos, downDur).OnComplete(() => { pen.DOLocalMove(baseLocalPos, upDur).OnComplete(() => { if (writing) EnqueueNextTap(); }); }); } }