From 817edd293825e4ad884eca09aa37d48fad6adbf3 Mon Sep 17 00:00:00 2001 From: Hazim Bin Ijaz Date: Thu, 4 Sep 2025 16:21:53 +0500 Subject: [PATCH] Added jump double jump and mid air movement --- Assets/Scenes/ChaseRun.unity | 39 +- .../Scripts/ChaseOn/ChasePlayerController.cs | 465 ++++++++++++------ ProjectSettings/DynamicsManager.asset | 2 +- 3 files changed, 341 insertions(+), 165 deletions(-) diff --git a/Assets/Scenes/ChaseRun.unity b/Assets/Scenes/ChaseRun.unity index 656fed8..df72dc8 100644 --- a/Assets/Scenes/ChaseRun.unity +++ b/Assets/Scenes/ChaseRun.unity @@ -2500,9 +2500,22 @@ MonoBehaviour: m_EditorClassIdentifier: moveSpeed: 1 laneDistance: 0.4 - jumpPower: 0.6 - jumpDuration: 1.2 laneSwitchSpeed: 10 + maxHoldTime: 0.25 + initialJumpVelocity: 6.5 + holdUpwardAcceleration: 35 + lowJumpGravityMultiplier: 2 + fallGravityMultiplier: 2.5 + coyoteTime: 0.12 + jumpBufferTime: 0.12 + maxVerticalSpeed: 15 + allowAirLaneSwitch: 1 + groundProbe: {fileID: 0} + groundMask: + serializedVersion: 2 + m_Bits: 4294967295 + groundCheckRadius: 0.2 + groundCheckExtraDown: 0.06 useSpeedBlendTree: 1 speedParamName: Speed runSpeedParamValue: 2 @@ -2513,11 +2526,13 @@ MonoBehaviour: secondHitWindow: 10 stateWaitTimeout: 3 waitingForGameOver: 0 - fallingShortHash: 0 validateStatesOnStart: 1 runTag: Run fallTag: Fall fallingTag: Falling + runShortHash: 0 + fallShortHash: 0 + fallingShortHash: 0 --- !u!65 &1704108065 BoxCollider: m_ObjectHideFlags: 0 @@ -6238,7 +6253,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!23 &3681778087044869278 MeshRenderer: m_ObjectHideFlags: 0 @@ -8840,6 +8855,18 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 6341515697253773609, guid: 515cc30092d66fe4dba290f9d6415a60, type: 3} + propertyPath: initialJumpVelocity + value: 4 + objectReference: {fileID: 0} + - target: {fileID: 6341515697253773609, guid: 515cc30092d66fe4dba290f9d6415a60, type: 3} + propertyPath: fallGravityMultiplier + value: 1.5 + objectReference: {fileID: 0} + - target: {fileID: 6341515697253773609, guid: 515cc30092d66fe4dba290f9d6415a60, type: 3} + propertyPath: holdUpwardAcceleration + value: 20 + objectReference: {fileID: 0} - target: {fileID: 7027419816435059383, guid: 515cc30092d66fe4dba290f9d6415a60, type: 3} propertyPath: m_Material value: @@ -8848,6 +8875,10 @@ PrefabInstance: propertyPath: m_Name value: ChasePlayer objectReference: {fileID: 0} + - target: {fileID: 7336801398090946996, guid: 515cc30092d66fe4dba290f9d6415a60, type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] diff --git a/Assets/Scripts/ChaseOn/ChasePlayerController.cs b/Assets/Scripts/ChaseOn/ChasePlayerController.cs index 32d2168..9406802 100644 --- a/Assets/Scripts/ChaseOn/ChasePlayerController.cs +++ b/Assets/Scripts/ChaseOn/ChasePlayerController.cs @@ -1,76 +1,132 @@ using System.Collections; -using DG.Tweening; using UnityEngine; +/// +/// Endless runner controller: +/// - Forward motion (−Z), 3 lanes with smooth switching (ground & air) +/// - Tap = small jump, Hold (within window) = higher jump +/// - Coyote time + jump buffer for forgiving inputs +/// - Robust ground detection (collider-based sphere cast with optional probe) +/// - Animator driving preserved; obstacle/fall flow preserved +/// +[RequireComponent(typeof(Rigidbody))] public class ChasePlayerController : MonoBehaviour { + // ===== Movement ===== [Header("Movement")] public float moveSpeed = 5f; - public float laneDistance = 2.5f; // 0=Left,1=Mid,2=Right - public float jumpPower = 0.6f; - public float jumpDuration = 1.2f; - public float laneSwitchSpeed = 10f; // used as units per second + [SerializeField] private float laneDistance = 2.5f; // 0=Left,1=Mid,2=Right + [SerializeField] private float laneSwitchSpeed = 10f; // units/sec + // ===== Jump (Tap / Hold) ===== + [Header("Jump (Tap/Hold)")] + [SerializeField] private float maxHoldTime = 0.25f; // hold window + [SerializeField] private float initialJumpVelocity = 6.5f; + [SerializeField] private float holdUpwardAcceleration = 35f; + [SerializeField] private float lowJumpGravityMultiplier = 2.0f; + [SerializeField] private float fallGravityMultiplier = 2.5f; + [SerializeField] private float coyoteTime = 0.12f; // after leaving ground + [SerializeField] private float jumpBufferTime = 0.12f; // before landing + [SerializeField] private float maxVerticalSpeed = 15f; + + [Header("Air Control")] + [SerializeField] private bool allowAirLaneSwitch = true; + + // ===== Ground Check ===== + [Header("Ground Check")] + [Tooltip("Optional feet probe; if null, uses collider bounds.")] + [SerializeField] private Transform groundProbe; + [SerializeField] private LayerMask groundMask = ~0; + [SerializeField] private float groundCheckRadius = 0.22f; + [SerializeField] private float groundCheckExtraDown = 0.06f; + + // ===== Animator Driving ===== [Header("Animator Driving")] - public bool useSpeedBlendTree = true; - public string speedParamName = "Speed"; - public float runSpeedParamValue = 2f; - public string runStateName = "Locomotion"; - public int baseLayer = 0; + [SerializeField] private bool useSpeedBlendTree = true; + [SerializeField] private string speedParamName = "Speed"; + [SerializeField] private float runSpeedParamValue = 2f; + [SerializeField] private string runStateName = "Locomotion"; + [SerializeField] private int baseLayer = 0; - [Header("Fall / GameOver Logic")] + // ===== Fall / GameOver ===== + [Header("Fall / GameOver")] public string fallStateName = "Fall"; public string fallingStateName = "Falling"; - public float secondHitWindow = 10f; - public float stateWaitTimeout = 3f; + [SerializeField] private float secondHitWindow = 10f; + [SerializeField] private float stateWaitTimeout = 3f; + public static System.Action OnMoveSpeedChanged; - int currentLane = 1; - Rigidbody rb; - Animator animator; - bool isGrounded = true; - bool isJumping = false; + // ----- runtime state ----- + private int currentLane = 1; // 0,1,2 + private float targetLaneX = 0f; - Vector2 startTouchPosition; - bool swipeDetected = false; - float minSwipeDistance = 50f; + private Rigidbody rb; + private Animator animator; + private Collider col; // any non-trigger collider - float lastObstacleHitTime = -999f; - public bool waitingForGameOver = false; + private bool isGrounded = true; + private bool isJumping = false; + private float lastGroundedTime = -999f; + private float jumpPressTime = -999f; + private float jumpHoldTimer = 0f; + private bool jumpHeld = false; - int runShortHash, fallShortHash; - public int fallingShortHash; - [SerializeField] bool validateStatesOnStart = true; - [SerializeField] string runTag = "Run"; - [SerializeField] string fallTag = "Fall"; - [SerializeField] string fallingTag = "Falling"; + private Vector2 startTouchPosition; + private bool swipeDetected = false; + private const float MIN_SWIPE = 50f; - float originalMoveSpeed; - bool unableToMove = false; + private float lastObstacleHitTime = -999f; + [HideInInspector] public bool waitingForGameOver = false; - ChaseScoreManager scoreManager; + [SerializeField] private bool validateStatesOnStart = true; + [SerializeField] private string runTag = "Run"; + [SerializeField] private string fallTag = "Fall"; + [SerializeField] private string fallingTag = "Falling"; - void Start() + public int runShortHash, fallShortHash, fallingShortHash; + private bool hasSpeedFloat, hasIsGroundedBool, hasJumpTrigger, hasLandTrigger; + + private float originalMoveSpeed; + private bool unableToMove = false; + + private ChaseScoreManager scoreManager; + + // ========= Unity ========= + private void Start() { + // Components rb = GetComponent(); animator = GetComponent(); + col = GetComponent(); scoreManager = FindObjectOfType(); - runShortHash = Animator.StringToHash(runStateName); - fallShortHash = Animator.StringToHash(fallStateName); + // Rigidbody setup (smooth & robust) + rb.interpolation = RigidbodyInterpolation.Interpolate; + rb.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; + rb.constraints = RigidbodyConstraints.FreezeRotation; + + // Animator hashes + runShortHash = Animator.StringToHash(runStateName); + fallShortHash = Animator.StringToHash(fallStateName); fallingShortHash = Animator.StringToHash(fallingStateName); + // Validate states (optional) if (validateStatesOnStart) { - if (!animator.HasState(baseLayer, runShortHash)) - Debug.LogError($"Run state '{runStateName}' not found"); - if (!animator.HasState(baseLayer, fallShortHash)) - Debug.LogError($"Fall state '{fallStateName}' not found"); - if (!animator.HasState(baseLayer, fallingShortHash)) - Debug.LogError($"Falling state '{fallingStateName}' not found"); + if (!animator.HasState(baseLayer, runShortHash)) Debug.LogError($"Run state '{runStateName}' not found"); + if (!animator.HasState(baseLayer, fallShortHash)) Debug.LogError($"Fall state '{fallStateName}' not found"); + if (!animator.HasState(baseLayer, fallingShortHash)) Debug.LogError($"Falling state '{fallingStateName}' not found"); } + // Cache animator parameters once + hasSpeedFloat = HasParam(speedParamName, AnimatorControllerParameterType.Float); + hasIsGroundedBool = HasParam("IsGrounded", AnimatorControllerParameterType.Bool); + hasJumpTrigger = HasParam("Jump", AnimatorControllerParameterType.Trigger); + hasLandTrigger = HasParam("Land", AnimatorControllerParameterType.Trigger); + originalMoveSpeed = moveSpeed; + targetLaneX = LaneToX(currentLane); ForceRunStart(); } @@ -80,194 +136,293 @@ public class ChasePlayerController : MonoBehaviour OnMoveSpeedChanged?.Invoke(newSpeed); } - void Update() + private void Update() { + UpdateGrounded(); DriveRunAnimation(); if (!unableToMove) { - HandleInput(); - HandleSwipe(); + ReadKeyboardInput(); + ReadTouchInput(); + HandleJumpHold(); } + + ApplyBetterJumpGravity(); + ClampVerticalSpeed(); } - void FixedUpdate() + private void FixedUpdate() { - MoveForward(); + MoveForwardAndSide(); } - void HandleInput() + // ========= Input ========= + private void ReadKeyboardInput() { - if (isJumping) return; + // Lane switching (in-air allowed if enabled) + if ((isGrounded || allowAirLaneSwitch) && Input.GetKeyDown(KeyCode.LeftArrow) && currentLane > 0) + SetLane(currentLane - 1); - if (Input.GetKeyDown(KeyCode.LeftArrow) && currentLane > 0) + if ((isGrounded || allowAirLaneSwitch) && Input.GetKeyDown(KeyCode.RightArrow) && currentLane < 2) + SetLane(currentLane + 1); + + // Jump + if (Input.GetKeyDown(KeyCode.Space)) { - currentLane--; - TweenToLaneX((currentLane - 1) * laneDistance); + jumpPressTime = Time.time; + TryStartJump(); + jumpHeld = true; + jumpHoldTimer = 0f; } - - if (Input.GetKeyDown(KeyCode.RightArrow) && currentLane < 2) + if (Input.GetKeyUp(KeyCode.Space)) { - currentLane++; - TweenToLaneX((currentLane - 1) * laneDistance); + jumpHeld = false; } - - if (Input.GetKeyDown(KeyCode.Space) && isGrounded) - Jump(); } - void HandleSwipe() + private void ReadTouchInput() { if (Input.touchCount != 1) return; + var t = Input.GetTouch(0); - var touch = Input.GetTouch(0); - switch (touch.phase) + switch (t.phase) { case TouchPhase.Began: - startTouchPosition = touch.position; + startTouchPosition = t.position; swipeDetected = true; + jumpHeld = true; + jumpHoldTimer = 0f; + jumpPressTime = Time.time; break; case TouchPhase.Ended: - if (!swipeDetected) return; - Vector2 swipe = touch.position - startTouchPosition; + if (!swipeDetected) { jumpHeld = false; break; } + Vector2 swipe = t.position - startTouchPosition; - if (swipe.magnitude >= minSwipeDistance && !isJumping) + if (swipe.magnitude >= MIN_SWIPE) { if (Mathf.Abs(swipe.x) > Mathf.Abs(swipe.y)) { - if (swipe.x > 0 && currentLane < 2) - { - currentLane++; - TweenToLaneX((currentLane - 1) * laneDistance); - } - else if (swipe.x < 0 && currentLane > 0) - { - currentLane--; - TweenToLaneX((currentLane - 1) * laneDistance); - } - } - else if (swipe.y > 0 && isGrounded) - { - Jump(); + if ((isGrounded || allowAirLaneSwitch) && swipe.x > 0 && currentLane < 2) SetLane(currentLane + 1); + else if ((isGrounded || allowAirLaneSwitch) && swipe.x < 0 && currentLane > 0) SetLane(currentLane - 1); } + else if (swipe.y > 0) TryStartJump(); + } + else + { + // Tap + TryStartJump(); } swipeDetected = false; + jumpHeld = false; break; } } - void TweenToLaneX(float targetX) - { - float distance = Mathf.Abs(rb.position.x - targetX); - float duration = distance / laneSwitchSpeed; + // ========= Movement ========= + private float LaneToX(int laneIndex) => (laneIndex - 1) * laneDistance; - rb.DOMoveX(targetX, duration).SetEase(Ease.OutQuad); + private void SetLane(int laneIndex) + { + currentLane = Mathf.Clamp(laneIndex, 0, 2); + targetLaneX = LaneToX(currentLane); } - void MoveForward() + private void MoveForwardAndSide() { - rb.MovePosition(rb.position + Vector3.back * moveSpeed * Time.fixedDeltaTime); + // Forward (−Z) + Vector3 forwardStep = Vector3.back * moveSpeed * Time.fixedDeltaTime; + + // Side toward target lane X + float newX = Mathf.MoveTowards(rb.position.x, targetLaneX, laneSwitchSpeed * Time.fixedDeltaTime); + Vector3 targetPos = new Vector3(newX, rb.position.y, rb.position.z) + forwardStep; + + rb.MovePosition(targetPos); } - void Jump() + // ========= Jump System ========= + private void TryStartJump() { - if (!isGrounded) return; + bool canCoyote = (Time.time - lastGroundedTime) <= coyoteTime; - isGrounded = false; - isJumping = true; - rb.velocity = Vector3.zero; - rb.useGravity = false; + if ((isGrounded || canCoyote) && !waitingForGameOver) + { + var v = rb.velocity; + v.y = initialJumpVelocity; + rb.velocity = v; - float forwardDisplacement = moveSpeed * jumpDuration; - Vector3 jumpTarget = rb.position + Vector3.back * forwardDisplacement; + isJumping = true; + isGrounded = false; + jumpPressTime = -999f; // consume buffer - rb.DOJump(jumpTarget, jumpPower, 1, jumpDuration) - .SetEase(Ease.Linear) - .OnStart(() => - { - SafeSetTrigger("Jump"); - SafeSetBool("IsGrounded", false); - }) - .OnComplete(() => - { - rb.useGravity = true; - isGrounded = true; - isJumping = false; - SafeSetTrigger("Land"); - SafeSetBool("IsGrounded", true); - ForceRunStart(); - }); + if (hasJumpTrigger) animator.SetTrigger("Jump"); + if (hasIsGroundedBool) animator.SetBool("IsGrounded", false); + } } - void OnCollisionEnter(Collision collision) + private void HandleJumpHold() + { + if (!isJumping || !jumpHeld) return; + + if (jumpHoldTimer < maxHoldTime && rb.velocity.y > 0f) + { + rb.AddForce(Vector3.up * holdUpwardAcceleration, ForceMode.Acceleration); + jumpHoldTimer += Time.deltaTime; + } + } + + private void ApplyBetterJumpGravity() + { + if (rb == null) return; + + if (rb.velocity.y < 0f) + { + rb.AddForce(Physics.gravity * (fallGravityMultiplier - 1f), ForceMode.Acceleration); + } + else if (!jumpHeld && rb.velocity.y > 0f) + { + rb.AddForce(Physics.gravity * (lowJumpGravityMultiplier - 1f), ForceMode.Acceleration); + } + } + + private void ClampVerticalSpeed() + { + var v = rb.velocity; + if (v.y > maxVerticalSpeed) v.y = maxVerticalSpeed; + if (v.y < -maxVerticalSpeed) v.y = -maxVerticalSpeed; + rb.velocity = v; + } + + // ========= Grounding ========= + private void UpdateGrounded() + { + bool groundedNow = GroundProbe(out _); + + if (groundedNow) + { + if (!isGrounded) + { + if (hasLandTrigger) animator.SetTrigger("Land"); + if (hasIsGroundedBool) animator.SetBool("IsGrounded", true); + isJumping = false; + ForceRunStart(); + } + + isGrounded = true; + lastGroundedTime = Time.time; + + // Buffered jump pressed just before landing + if ((Time.time - jumpPressTime) <= jumpBufferTime) + { + jumpPressTime = -999f; + TryStartJump(); + } + } + else + { + isGrounded = false; + if (hasIsGroundedBool) animator.SetBool("IsGrounded", false); + } + } + + private bool GroundProbe(out Vector3 hitPoint) + { + hitPoint = transform.position; + + if (col != null) + { + Bounds b = col.bounds; + Vector3 feet = new Vector3(b.center.x, b.min.y + 0.02f, b.center.z); + Vector3 origin = groundProbe ? groundProbe.position : feet; + + // SphereCast small distance down + if (Physics.SphereCast(origin, groundCheckRadius, Vector3.down, out RaycastHit hit, + groundCheckExtraDown + 0.02f, groundMask, QueryTriggerInteraction.Ignore)) + { + hitPoint = hit.point; + return true; + } + + // Fallback overlap + if (Physics.CheckSphere(origin + Vector3.down * groundCheckExtraDown, groundCheckRadius, groundMask, QueryTriggerInteraction.Ignore)) + { + hitPoint = origin + Vector3.down * groundCheckExtraDown; + return true; + } + return false; + } + + // No collider → simple overlap at probe or near feet + Vector3 o = groundProbe ? groundProbe.position : (transform.position + Vector3.down * 0.95f); + bool ok = Physics.CheckSphere(o, groundCheckRadius, groundMask, QueryTriggerInteraction.Ignore); + if (ok) hitPoint = o; + return ok; + } + + private void OnCollisionStay(Collision collision) + { + // Extra safety: contact with ground layer & low vertical speed => grounded + if (((1 << collision.gameObject.layer) & groundMask) != 0 && Mathf.Abs(rb.velocity.y) < 0.1f) + { + isGrounded = true; + lastGroundedTime = Time.time; + } + } + + private void OnCollisionEnter(Collision collision) { if (collision.gameObject.CompareTag("Ground")) { isGrounded = true; - SafeSetBool("IsGrounded", true); + lastGroundedTime = Time.time; + isJumping = false; + if (hasIsGroundedBool) animator.SetBool("IsGrounded", true); ForceRunStart(); } } - void ForceRunStart(bool ignoreGuards = false) + // ========= Animator ========= + private void ForceRunStart(bool ignoreGuards = false) { if (!ignoreGuards && (IsInOrGoingTo(fallShortHash) || IsInOrGoingTo(fallingShortHash) || waitingForGameOver)) return; - if (useSpeedBlendTree && HasParameter(speedParamName, AnimatorControllerParameterType.Float)) - { + if (useSpeedBlendTree && hasSpeedFloat) animator.SetFloat(speedParamName, runSpeedParamValue); - } else if (!string.IsNullOrEmpty(runStateName)) - { animator.CrossFadeInFixedTime(runStateName, 0.1f, baseLayer, 0f); - } } - void DriveRunAnimation() + private void DriveRunAnimation() { if (IsInOrGoingTo(fallShortHash) || IsInOrGoingTo(fallingShortHash) || isJumping) return; - if (useSpeedBlendTree && HasParameter(speedParamName, AnimatorControllerParameterType.Float)) + if (useSpeedBlendTree && hasSpeedFloat) { animator.SetFloat(speedParamName, runSpeedParamValue, 0.1f, Time.deltaTime); } else { - var st = animator.GetCurrentAnimatorStateInfo(baseLayer); + var st = animator.GetCurrentAnimatorStateInfo(baseLayer); var nxt = animator.GetNextAnimatorStateInfo(baseLayer); - bool inRunNow = st.shortNameHash == runShortHash || st.IsTag(runTag); + bool inRunNow = st.shortNameHash == runShortHash || st.IsTag(runTag); bool goingToRun = animator.IsInTransition(baseLayer) && (nxt.shortNameHash == runShortHash || nxt.IsTag(runTag)); if (!isJumping && !inRunNow && !goingToRun) - { animator.CrossFadeInFixedTime(runStateName, 0.1f, baseLayer, 0f); - } } } - bool HasParameter(string name, AnimatorControllerParameterType type) + private bool HasParam(string name, AnimatorControllerParameterType type) { foreach (var p in animator.parameters) if (p.type == type && p.name == name) return true; return false; } - void SafeSetTrigger(string trig) - { - if (HasParameter(trig, AnimatorControllerParameterType.Trigger)) - animator.SetTrigger(trig); - } - - void SafeSetBool(string param, bool v) - { - if (HasParameter(param, AnimatorControllerParameterType.Bool)) - animator.SetBool(param, v); - } - - bool IsInOrGoingTo(int shortHash) + private bool IsInOrGoingTo(int shortHash) { var st = animator.GetCurrentAnimatorStateInfo(baseLayer); if (st.shortNameHash == shortHash) return true; @@ -279,49 +434,41 @@ public class ChasePlayerController : MonoBehaviour return false; } - // ----------------- Obstacle/Fall flow ----------------- - + // ========= Obstacle / Fall flow (kept) ========= public void OnObstacleHit() { if (waitingForGameOver) return; - // Second hit within window → Falling then GameOver if (Time.time - lastObstacleHitTime <= secondHitWindow) { waitingForGameOver = true; - //SetMoveSpeed(0); moveSpeed = 0; StartCoroutine(PlayStateAndGameOver(fallingStateName, fallingShortHash)); } else { - // First hit → Fall only (no GameOver), then resume run when Fall finishes lastObstacleHitTime = Time.time; PlayStateOnce(fallStateName); - originalMoveSpeed = moveSpeed; // remember whatever it was right now - //SetMoveSpeed(0f); // optional: pause forward motion during stumble + originalMoveSpeed = moveSpeed; moveSpeed = 0; - StartCoroutine(ResumeRunAfter(fallStateName, fallShortHash)); } } - void PlayStateOnce(string stateName, float xfade = 0.08f) + private void PlayStateOnce(string stateName, float xfade = 0.08f) { - if (string.IsNullOrEmpty(stateName) || animator == null) return; - animator.CrossFadeInFixedTime(stateName, xfade, baseLayer, 0f); + if (!string.IsNullOrEmpty(stateName)) + animator.CrossFadeInFixedTime(stateName, xfade, baseLayer, 0f); } - IEnumerator ResumeRunAfter(string stateName, int shortHash, float xfade = 0.1f) + private IEnumerator ResumeRunAfter(string stateName, int shortHash, float xfade = 0.1f) { float t0 = Time.time; while (!IsInOrGoingTo(shortHash) && !TimedOut(t0)) yield return null; while (animator.IsInTransition(baseLayer)) yield return null; - // Wait for completion OR force resume after timeout t0 = Time.time; - while (!StateFinished(shortHash) && !TimedOut(t0)) - yield return null; + while (!StateFinished(shortHash) && !TimedOut(t0)) yield return null; if (!waitingForGameOver) { @@ -330,6 +477,7 @@ public class ChasePlayerController : MonoBehaviour animator.CrossFadeInFixedTime(runStateName, 0.1f, baseLayer, 0f); } } + public IEnumerator PlayStateAndGameOver(string stateName, int shortHash, float xfade = 0.08f) { unableToMove = true; @@ -341,26 +489,23 @@ public class ChasePlayerController : MonoBehaviour animator.CrossFadeInFixedTime(stateName, xfade, baseLayer, 0f); - // Wait to enter target state float t0 = Time.time; while (!IsInOrGoingTo(shortHash) && !TimedOut(t0)) yield return null; while (animator.IsInTransition(baseLayer)) yield return null; - // Wait for it to finish (non-looping recommended) t0 = Time.time; while (!StateFinished(shortHash) && !TimedOut(t0)) yield return null; if (scoreManager) scoreManager.GameOver(); } - bool StateFinished(int shortHash) + private bool StateFinished(int shortHash) { var st = animator.GetCurrentAnimatorStateInfo(baseLayer); return st.shortNameHash == shortHash && !animator.IsInTransition(baseLayer) && st.normalizedTime >= 1f; } - bool TimedOut(float startTime) - { - return stateWaitTimeout > 0f && (Time.time - startTime) > stateWaitTimeout; - } + private bool TimedOut(float startTime) => + stateWaitTimeout > 0f && (Time.time - startTime) > stateWaitTimeout; + } diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset index 0d01522..68463c0 100644 --- a/ProjectSettings/DynamicsManager.asset +++ b/ProjectSettings/DynamicsManager.asset @@ -18,7 +18,7 @@ PhysicsManager: m_ClothInterCollisionDistance: 0 m_ClothInterCollisionStiffness: 0 m_ContactsGeneration: 1 - m_LayerCollisionMatrix: fffffffffffffffffffffffffffaffffffffffffffffffffffffffff7ffffffff7fffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_LayerCollisionMatrix: fffffffffffffffffffffffffffbffffffffffffffffffffffffffff7ffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff m_SimulationMode: 0 m_AutoSyncTransforms: 0 m_ReuseCollisionCallbacks: 1