Speed is everything for a ninja. I’ve successfully refined the core combat mechanics to include a high-speed dash attack. By combining C# coroutines with polished animations, the character now executes a seamless ‘dash-strike-return’ maneuver that elevates the overall gameplay experience.




using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour
{
private Animator anim;
private bool isAttackingAction = false; // Manages the overall state of the attack sequence
[Header("Attack Settings")]
public float myAttackSpeed = 1.0f; // Animation playback speed
private float timer = 0f;
[Header("Dash & Return Settings")]
public float dashDistance = 2.0f; // Distance to dash forward
public float dashDuration = 0.1f; // Duration of the forward dash (fast)
public float stayDuration = 0.2f; // Time spent at the attack point
public float returnDuration = 0.3f; // Duration of the return movement
[Header("Effect Settings")]
public ParticleSystem punchParticle;
public Transform hitPointTransform;
void Start()
{
anim = GetComponent<Animator>();
}
void Update()
{
// Increased delay to account for the return movement duration
float attackDelay = (0.5f / myAttackSpeed) + 1.2f;
timer += Time.deltaTime;
// Execute attack when the timer exceeds the delay and no attack is currently in progress
if (timer >= attackDelay && !isAttackingAction)
{
ExecuteAttack();
timer = 0f;
}
}
void ExecuteAttack()
{
// Trigger animations and set speed
anim.SetFloat("atkSpeed", myAttackSpeed);
anim.SetTrigger("doKick");
// Play particle effects at the designated HitPoint
if (punchParticle != null)
{
punchParticle.transform.position = hitPointTransform.position;
punchParticle.Play();
}
// Start the Dash-Strike-Return coroutine sequence
StartCoroutine(HitAndRunSequence());
}
IEnumerator HitAndRunSequence()
{
isAttackingAction = true;
Vector3 originalPos = transform.position; // Store the starting position
Vector3 targetPos = originalPos + (transform.right * dashDistance);
// 1. Forward Dash: Move quickly toward the target
float elapsed = 0f;
while (elapsed < dashDuration)
{
transform.position = Vector3.Lerp(originalPos, targetPos, elapsed / dashDuration);
elapsed += Time.deltaTime;
yield return null;
}
transform.position = targetPos;
// 2. Strike Window: Wait briefly while the strike animation plays
yield return new WaitForSeconds(stayDuration);
// 3. Return: Smoothly move back to the original starting position
elapsed = 0f;
while (elapsed < returnDuration)
{
transform.position = Vector3.Lerp(targetPos, originalPos, elapsed / returnDuration);
elapsed += Time.deltaTime;
yield return null;
}
transform.position = originalPos;
isAttackingAction = false; // Sequence complete
}
}