IEnumerator <float> Attack() { var attackRangeSqrd = settings.AttackRange * settings.AttackRange; float t = 0; state = State.Attack; anim.SetFloat(CharacterAnimationHelper.DirZ, 0); anim.SetFloat(CharacterAnimationHelper.Speed, 0); // Make the enemy rotate towards the target while waiting for SecondsBeforeAttack while (t < settings.SecondsBeforeAttack) { t += Time.deltaTime; var dist = (target.position - transform.position).WithY(0); var targetLookRot = Quaternion.LookRotation(dist, Vector3.up); transform.rotation = Quaternion.Slerp(transform.rotation, targetLookRot, t / settings.SecondsBeforeAttack); var sqrMagn = dist.sqrMagnitude; if (sqrMagn > attackRangeSqrd) { yield return(MEC.Timing.WaitForSeconds(0.15f)); Reset(); yield break; } yield return(0f); } // Attack using one of the three attack moves var attackHash = CharacterAnimationHelper.GetHashForCombo(Random.Range(0, 3)); anim.CrossFadeInFixedTime(attackHash, 0.1f); // Hit scanning will be registered via animation events t = 0; while (t < settings.SecondsAfterAttack) { t += Time.deltaTime; yield return(0f); } Reset(); void Reset() { state = State.Chase; anim.SetFloat(CharacterAnimationHelper.DirZ, 1); anim.SetFloat(CharacterAnimationHelper.Speed, 1); } }
void HandleAttack() { if ((state == State.FreeToDoActions || state == State.Attacking) && currentCombo < 2 && Input.GetMouseButtonDown(0)) { var percentage = timeSincePrevAttack / (float)anim.GetCurrentAnimatorStateInfo(0).length; bool canAttack = percentage >= settings.minComboSkipTime; if (canAttack) { PlayNextCombo(); } } timeSincePrevAttack += Time.deltaTime; if (timeSincePrevAttack >= settings.maxSecondsToKeepCombo && currentCombo != -1) { ResetCombo(); } void PlayNextCombo() { currentCombo++; timeSincePrevAttack = 0; state = State.Attacking; MEC.Timing.KillCoroutines(attackCommand); attackCommand = MEC.Timing.RunCoroutine(DoAttackStuff()); var t = currentCombo == -1 ? 0 : settings.comboSkipTimeCurve.Evaluate(timeSincePrevAttack / settings.maxSecondsToKeepCombo); anim.CrossFadeInFixedTime(CharacterAnimationHelper.GetHashForCombo(currentCombo), 0.2f, 0, t); } void ResetCombo() { currentCombo = -1; if (state == State.Attacking) { state = State.FreeToDoActions; } } IEnumerator <float> DoAttackStuff() { const float totalTimeToRotate = 0.25f; const float totalTimeToAnimMove = 0.25f; float t = 0; var extents = hitCollider.bounds.extents; enemiesHit.Clear(); while (t < totalTimeToRotate) { var targetRotY = Mathf.MoveTowardsAngle(transform.eulerAngles.y, targetLookRot.eulerAngles.y, settings.AttackRotationSpeed * Time.deltaTime); transform.eulerAngles = transform.eulerAngles.WithY(targetRotY); t += Time.deltaTime; yield return(0f); } var curve = CharacterAnimationHelper.GetSettingForCombo(currentCombo, settings); t = 0; while (t < totalTimeToAnimMove) { t += Time.deltaTime; cc.Move(curve.Evaluate(t / totalTimeToAnimMove) * transform.forward * settings.moveForcePerAttack * Time.deltaTime); ScanForHitsAndAttack(); yield return(0f); } } void ScanForHitsAndAttack() { int hitAmount = Physics.OverlapBoxNonAlloc(hitCollider.bounds.center, hitCollider.bounds.extents, results, Quaternion.identity, LayerHelper.enemyLayer, QueryTriggerInteraction.Collide); for (int i = 0; i < hitAmount; i++) { var col = results[i]; if (!col || enemiesHit.Contains(col)) { return; } var enemy = col.GetComponent <Enemy>(); enemy.OnDamaged?.Invoke(settings.Damage); enemiesHit.Add(col); } } }