using System.Collections.Generic; using UnityEngine; public class MainCharacterCombat : MonoBehaviour { [Header("自动攻击")] [Min(0f)] public float attackRange = 8f; [Min(0f)] public float attackPower = 50f; [Min(0f)] public float attackInterval = 2f; [Header("模型朝向修正")] public float modelFacingOffset = 0f; [Header("动画根运动")] public bool useRootMotion = false; [Header("法术球")] public GameObject projectilePrefab; public Transform attackPos; [Header("召唤单位")] public GameObject humanSoldierPrefab; public GameObject archerPrefab; public float summonDistance = 1.5f; public float summonSideOffset = 0.8f; public float summonSearchStep = 0.8f; public int summonSearchCount = 5; public float summonDepthSearchStep = 1f; public int summonDepthSearchCount = 2; public float summonAnimationTimeout = 2.5f; [Min(0f)] public float projectileSpeed = 12f; [Min(0f)] public float projectileLifetime = 5f; private Animator animator; private MainCharacterDebugMovement movement; private GameObject target; private float attackCooldown; private float attackElapsed; private bool attacking; private bool attackEffectTriggered; private GameObject pendingSummonPrefab; private float summonElapsed; private bool summonSpawned; private bool summoning; private void Start() { animator = GetComponent(); movement = GetComponent(); if (animator != null) { animator.applyRootMotion = useRootMotion; } } private void Update() { if (attackCooldown > 0f) { attackCooldown -= Time.deltaTime; } HandleSummonInput(); if (summoning) { summonElapsed += Time.deltaTime; CheckSummonFinished(); return; } if (attacking) { attackElapsed += Time.deltaTime; CheckAttackFinished(); return; } if (movement == null || movement.IsMoving || animator == null) { return; } target = FindNearestEnemy(); if (target == null || attackCooldown > 0f) { return; } FaceTarget(target); SetAttackAnimationSpeed(); animator.SetInteger("speed", 0); animator.ResetTrigger("attack"); animator.SetTrigger("attack"); attacking = true; attackElapsed = 0f; attackEffectTriggered = false; } public void OnAttackEffect() { if (attackEffectTriggered || !attacking || target == null || !target.activeInHierarchy) { return; } attackEffectTriggered = true; FireProjectile(target); } private void HandleSummonInput() { if (Input.GetKeyDown(KeyCode.Alpha1)) { RequestSummon(1); } else if (Input.GetKeyDown(KeyCode.Alpha2)) { RequestSummon(2); } } public bool RequestSummon(int slot) { if (summoning || animator == null) { return false; } GameObject prefab = slot == 1 ? humanSoldierPrefab : slot == 2 ? archerPrefab : null; if (prefab == null) { return false; } if (movement != null) { movement.StopMovement(); } if (attacking) { FinishAttack(); } BeginSummon(prefab); return true; } private void BeginSummon(GameObject prefab) { if (prefab == null) { return; } pendingSummonPrefab = prefab; summonElapsed = 0f; summonSpawned = false; summoning = true; animator.speed = 1f; animator.SetInteger("speed", 0); animator.ResetTrigger("skill"); animator.SetTrigger("skill"); } public void OnSkillHit1() { if (!summoning || summonSpawned || pendingSummonPrefab == null) { return; } Vector3 direction = transform.forward; direction.y = 0f; direction.Normalize(); Vector3 spawnPosition; if (!TryFindSummonPosition(direction, out spawnPosition)) { Debug.LogWarning("主角附近没有可用的召唤位置,本次召唤取消"); FinishSummon(); return; } GameObject summoned = Instantiate( pendingSummonPrefab, spawnPosition, Quaternion.LookRotation(direction, Vector3.up)); summoned.tag = "Friend"; summonSpawned = true; pendingSummonPrefab = null; Debug.Log("主角召唤单位:" + summoned.name + " position=" + spawnPosition); } private bool TryFindSummonPosition(Vector3 direction, out Vector3 position) { Vector3 side = Vector3.Cross(Vector3.up, direction).normalized; Vector3[] directions = { direction, -direction }; foreach (Vector3 forward in directions) { for (int depthIndex = 0; depthIndex <= summonDepthSearchCount; depthIndex++) { float depthDistance = summonDistance + depthIndex * summonDepthSearchStep; Vector3 center = transform.position + forward * depthDistance; for (int sideIndex = 0; sideIndex <= summonSearchCount; sideIndex++) { if (sideIndex == 0 && TryUseSummonCandidate(center, out position)) { return true; } float sideDistance = summonSideOffset + (sideIndex - 1) * summonSearchStep; if (sideIndex > 0) { if (TryUseSummonCandidate(center + side * sideDistance, out position) || TryUseSummonCandidate(center - side * sideDistance, out position)) { return true; } } } } } position = transform.position; return false; } private bool TryUseSummonCandidate(Vector3 candidate, out Vector3 position) { candidate.y = transform.position.y; if (IsInsideMovementBounds(candidate) && IsSummonPositionFree(candidate)) { position = candidate; return true; } position = transform.position; return false; } private bool IsSummonPositionFree(Vector3 position) { const float radius = 0.55f; const float height = 2f; Vector3 center = position + Vector3.up * (height * 0.5f); Collider[] hits = Physics.OverlapCapsule( center - Vector3.up * (height * 0.5f - radius), center + Vector3.up * (height * 0.5f - radius), radius); foreach (Collider hit in hits) { if (hit == null || hit.transform.root == transform.root) { continue; } if (hit.gameObject.name == "BattlePlatform" || hit.transform.root.name == "BattlePlatform") { continue; } if (hit.GetComponentInParent() != null) { continue; } return false; } return true; } private bool IsInsideMovementBounds(Vector3 position) { return movement == null || (position.x >= movement.xLimits.x && position.x <= movement.xLimits.y && position.z >= movement.zLimits.x && position.z <= movement.zLimits.y); } private void CheckSummonFinished() { if (summonElapsed >= summonAnimationTimeout) { Debug.LogWarning("主角召唤动画超时,已解除召唤状态"); FinishSummon(); return; } AnimatorClipInfo[] clips = animator.GetCurrentAnimatorClipInfo(0); foreach (AnimatorClipInfo clipInfo in clips) { string clipName = clipInfo.clip.name.ToLowerInvariant(); if ((clipName.Contains("skill") || clipName.Contains("召唤")) && animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f) { FinishSummon(); return; } } } private void FinishSummon() { summoning = false; summonSpawned = false; pendingSummonPrefab = null; summonElapsed = 0f; animator.speed = 1f; animator.SetInteger("speed", 0); } private void CheckAttackFinished() { if (attackElapsed >= 2.4f) { FinishAttack(); return; } AnimatorClipInfo[] clips = animator.GetCurrentAnimatorClipInfo(0); bool playingAttack = false; foreach (AnimatorClipInfo clipInfo in clips) { string clipName = clipInfo.clip.name.ToLowerInvariant(); if (clipName.Contains("attack") || clipName.Contains("攻击") || clipName.Contains("普攻")) { playingAttack = true; break; } } if (!playingAttack) { return; } if (animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f) { FinishAttack(); } } private void SetAttackAnimationSpeed() { float clipDuration = GetAttackAnimationDuration(); if (clipDuration > 0f && attackInterval > 0f) { animator.speed = clipDuration / attackInterval; } else { animator.speed = 1f; } } private float GetAttackAnimationDuration() { if (animator == null || animator.runtimeAnimatorController == null) { return 0f; } var overrideController = animator.runtimeAnimatorController as AnimatorOverrideController; if (overrideController != null) { var overrides = new List>(); overrideController.GetOverrides(overrides); foreach (var pair in overrides) { if (IsAttackAnimation(pair.Key)) { return pair.Value != null ? pair.Value.length : pair.Key.length; } } } foreach (var clip in animator.runtimeAnimatorController.animationClips) { if (IsAttackAnimation(clip)) { return clip.length; } } return 0f; } private bool IsAttackAnimation(AnimationClip clip) { if (clip == null) { return false; } string clipName = clip.name.ToLowerInvariant(); return clipName.Contains("attack") || clipName.Contains("攻击") || clipName.Contains("普攻"); } private void FinishAttack() { attacking = false; animator.speed = 1f; animator.SetInteger("speed", 0); } private GameObject FindNearestEnemy() { GameObject nearest = null; float nearestDistance = attackRange * attackRange; foreach (GameObject enemy in GameObject.FindGameObjectsWithTag("Enemy")) { if (enemy == null || enemy.transform.root == transform.root) { continue; } UnitBase enemyUnit = enemy.GetComponentInParent(); if (enemyUnit == null) { continue; } float distance = (enemy.transform.position - transform.position).sqrMagnitude; if (distance < nearestDistance) { nearest = enemy; nearestDistance = distance; } } return nearest; } private void FaceTarget(GameObject enemy) { Vector3 direction = enemy.transform.position - transform.position; direction.y = 0f; if (direction.sqrMagnitude > 0.001f) { Quaternion targetRotation = Quaternion.LookRotation(direction, Vector3.up); transform.rotation = Quaternion.Euler( 0f, targetRotation.eulerAngles.y + modelFacingOffset, 0f); } } private void FireProjectile(GameObject enemy) { if (projectilePrefab == null || attackPos == null) { return; } GameObject projectileObject = Instantiate( projectilePrefab, attackPos.position, attackPos.rotation); Projectile projectile = projectileObject.GetComponent(); if (projectile != null) { projectile.Initialize( enemy, attackPower, gameObject, projectileSpeed, projectileLifetime, false); } } }