Unity3D ACT手游战斗系统深度设计与C#实现

一、ACT战斗系统核心特性

  1. 实时精确的碰撞检测(HitBox/HurtBox)
  2. 多段连击与技能取消机制
  3. 硬直/浮空/受击反馈状态系统
  4. 动态镜头震动与打击特效
  5. 帧精确的动画状态控制

二、战斗系统架构设计

1. 基于ECS的实体组件架构

// 实体组件定义
public struct HitBoxComponent : IComponentData {
    public float3 Center;
    public float Radius;
    public Entity Owner;
    public int DamageValue;
}

public struct HurtBoxComponent : IComponentData {
    public float3 Center;
    public float Radius;
    public Entity Owner;
}

// 碰撞检测系统
public class HitDetectionSystem : SystemBase {
    protected override void OnUpdate() {
        Entities.WithAll<HitBoxComponent>().ForEach((Entity hitEntity, ref HitBoxComponent hitBox) => {
            Entities.WithAll<HurtBoxComponent>().ForEach((Entity hurtEntity, ref HurtBoxComponent hurtBox) => {
                if (hitBox.Owner != hurtBox.Owner && 
                    math.distance(hitBox.Center, hurtBox.Center) < hitBox.Radius + hurtBox.Radius) {
                    var damageEvent = new DamageEvent {
                        Attacker = hitBox.Owner,
                        Target = hurtBox.Owner,
                        Damage = hitBox.DamageValue
                    };
                    EntityManager.AddComponentData(hurtEntity, damageEvent);
                }
            }).Run();
        }).Run();
    }
}

2. 动态骨骼动画控制

public class WeaponTrailController : MonoBehaviour {
    private List<Vector3> _trailPoints = new List<Vector3>();
    private LineRenderer _lineRenderer;
    private Transform[] _boneChain;

    void Update() {
        RecordTrailPoints();
        UpdateTrailRenderer();
    }

    private void RecordTrailPoints() {
        if (_boneChain == null) return;
        
        _trailPoints.Clear();
        foreach (var bone in _boneChain) {
            _trailPoints.Add(bone.position);
        }
    }

    private void UpdateTrailRenderer() {
        _lineRenderer.positionCount = _trailPoints.Count;
        _lineRenderer.SetPositions(_trailPoints.ToArray());
    }

    public void Initialize(Transform rootBone, int chainLength) {
        _boneChain = new Transform[chainLength];
        Transform current = rootBone;
        for (int i = 0; i < chainLength; i++) {
            _boneChain[i] = current;
            current = current.parent;
        }
    }
}

三、连击系统实现

1. 连击状态机

public class ComboSystem : MonoBehaviour {
    private int _currentComboStep;
    private float _comboResetTimer;
    private bool _canAcceptNextInput;

    [System.Serializable]
    public class ComboSequence {
        public string AnimationState;
        public float InputWindowStart;
        public float InputWindowDuration;
        public AttackType[] CancelableAttacks;
    }

    public ComboSequence[] comboSequence;

    void Update() {
        if (_currentComboStep > 0) {
            _comboResetTimer -= Time.deltaTime;
            if (_comboResetTimer <= 0) {
                ResetCombo();
            }
        }
    }

    public void TryNextAttack(AttackType inputType) {
        if (!_canAcceptNextInput) return;

        var currentStep = comboSequence[_currentComboStep];
        if (Array.Exists(currentStep.CancelableAttacks, t => t == inputType)) {
            ExecuteAttack(inputType);
        }
    }

    private void ExecuteAttack(AttackType type) {
        _currentComboStep = (_currentComboStep + 1) % comboSequence.Length;
        _comboResetTimer = comboSequence[_currentComboStep].InputWindowDuration;
        _canAcceptNextInput = false;
        
        StartCoroutine(OpenInputWindow(
            comboSequence[_currentComboStep].InputWindowStart,
            comboSequence[_currentComboStep].InputWindowDuration
        ));
    }

    private IEnumerator OpenInputWindow(float startTime, float duration) {
        yield return new WaitForSeconds(startTime);
        _canAcceptNextInput = true;
        yield return new WaitForSeconds(duration);
        _canAcceptNextInput = false;
    }
}

2. 技能取消机制

public class AttackCancelController : MonoBehaviour {
    private Dictionary<AttackState, AttackCancelRule> _cancelRules = 
        new Dictionary<AttackState, AttackCancelRule>();

    public void RegisterCancelRule(AttackState fromState, 
        AttackType cancelType, AttackState toState) {
        if (!_cancelRules.ContainsKey(fromState)) {
            _cancelRules[fromState] = new AttackCancelRule();
        }
        _cancelRules[fromState].AddCancelOption(cancelType, toState);
    }

    public bool TryGetCancelState(AttackState currentState, 
        AttackType inputType, out AttackState nextState) {
        if (_cancelRules.TryGetValue(currentState, out var rule)) {
            return rule.TryGetCancelState(inputType, out nextState);
        }
        nextState = AttackState.None;
        return false;
    }
}

public class AttackCancelRule {
    private Dictionary<AttackType, AttackState> _cancelMap = 
        new Dictionary<AttackType, AttackState>();

    public void AddCancelOption(AttackType type, AttackState state) {
        _cancelMap[type] = state;
    }

    public bool TryGetCancelState(AttackType type, out AttackState state) {
        return _cancelMap.TryGetValue(type, out state);
    }
}

四、打击反馈系统

1. 受击反应控制器

public class HitReactionController : MonoBehaviour {
    private Animator _animator;
    private Rigidbody _rigidbody;
    private Coroutine _currentReaction;

    [Header("Hit Reactions")]
    public float hitStunDuration = 0.3f;
    public float launchForce = 5f;
    public AnimationCurve hitSlowCurve;

    public void ProcessHit(HitDirection direction, HitIntensity intensity) {
        if (_currentReaction != null) {
            StopCoroutine(_currentReaction);
        }
        _currentReaction = StartCoroutine(HitReactionRoutine(direction, intensity));
    }

    private IEnumerator HitReactionRoutine(HitDirection dir, HitIntensity intensity) {
        // 动画触发
        _animator.SetTrigger(GetHitTriggerName(dir, intensity));
        
        // 物理反馈
        Vector3 force = GetForceDirection(dir) * intensity.Power;
        _rigidbody.AddForce(force, ForceMode.VelocityChange);
        
        // 时间扭曲
        Time.timeScale = 0.2f;
        yield return new WaitForSecondsRealtime(0.1f);
        
        float timer = 0f;
        while (timer < hitStunDuration) {
            Time.timeScale = Mathf.Lerp(0.2f, 1f, hitSlowCurve.Evaluate(timer / hitStunDuration));
            timer += Time.unscaledDeltaTime;
            yield return null;
        }
        Time.timeScale = 1f;
    }

    private string GetHitTriggerName(HitDirection dir, HitIntensity intensity) {
        return $"Hit_{dir.ToString()}_{intensity.ToString()}";
    }
}

2. 动态镜头控制

public class CombatCameraController : MonoBehaviour {
    private CinemachineVirtualCamera _virtualCam;
    private CinemachineBasicMultiChannelPerlin _noiseModule;
    private Coroutine _currentShake;

    void Awake() {
        _virtualCam = GetComponent<CinemachineVirtualCamera>();
        _noiseModule = _virtualCam.GetCinemachineComponent<CinemachineBasicMultiChannelPerlin>();
    }

    public void TriggerCameraShake(ShakePreset preset) {
        if (_currentShake != null) StopCoroutine(_currentShake);
        _currentShake = StartCoroutine(ShakeRoutine(preset));
    }

    private IEnumerator ShakeRoutine(ShakePreset preset) {
        _noiseModule.m_AmplitudeGain = preset.amplitude;
        _noiseModule.m_FrequencyGain = preset.frequency;
        
        float decayRate = preset.amplitude / preset.duration;
        float remaining = preset.duration;
        
        while (remaining > 0) {
            _noiseModule.m_AmplitudeGain = Mathf.Lerp(0, preset.amplitude, remaining / preset.duration);
            remaining -= Time.deltaTime;
            yield return null;
        }
        
        _noiseModule.m_AmplitudeGain = 0;
        _noiseModule.m_FrequencyGain = 0;
    }
}

五、性能优化策略

1. 基于Job System的碰撞检测

[BurstCompile]
public struct HitDetectionJob : IJobParallelFor {
    [ReadOnly] public NativeArray<HitBoxData> hitBoxes;
    [ReadOnly] public NativeArray<HurtBoxData> hurtBoxes;
    public NativeQueue<DamageEvent>.ParallelWriter damageEvents;

    public void Execute(int index) {
        var hitBox = hitBoxes[index];
        foreach (var hurtBox in hurtBoxes) {
            if (hitBox.owner != hurtBox.owner && 
                math.distance(hitBox.position, hurtBox.position) < hitBox.radius + hurtBox.radius) {
                damageEvents.Enqueue(new DamageEvent {
                    attacker = hitBox.owner,
                    target = hurtBox.owner,
                    damage = hitBox.damage
                });
            }
        }
    }
}

2. 动画事件优化

public class AnimationEventOptimizer : MonoBehaviour {
    private Dictionary<string, AnimationEvent[]> _cachedEvents = 
        new Dictionary<string, AnimationEvent[]>();

    public void PrecacheAnimationEvents(Animator animator) {
        foreach (var clip in animator.runtimeAnimatorController.animationClips) {
            _cachedEvents[clip.name] = clip.events;
            clip.events = OptimizeEvents(clip.events);
        }
    }

    private AnimationEvent[] OptimizeEvents(AnimationEvent[] original) {
        List<AnimationEvent> optimized = new List<AnimationEvent>();
        foreach (var evt in original) {
            if (evt.functionName.StartsWith("Combat/")) {
                optimized.Add(evt);
            }
        }
        return optimized.ToArray();
    }

    public void DispatchAnimationEvent(string eventPath) {
        var parts = eventPath.Split('/');
        string systemName = parts[1];
        string methodName = parts[2];
        
        switch (systemName) {
            case "HitBox":
                CombatSystem.Instance.HitBoxSystem.Invoke(methodName, 0);
                break;
            case "Camera":
                CameraSystem.Instance.Invoke(methodName, 0);
                break;
        }
    }
}

六、进阶扩展方向

  1. 精确的输入缓冲系统
public class InputBufferSystem : MonoBehaviour {
    private Queue<BufferedInput> _inputQueue = new Queue<BufferedInput>();
    private float _bufferWindow = 0.15f;

    void Update() {
        ProcessExpiredInputs();
    }

    public void BufferInput(InputType type) {
        _inputQueue.Enqueue(new BufferedInput {
            type = type,
            expireTime = Time.time + _bufferWindow
        });
    }

    public bool TryConsumeInput(InputType type) {
        foreach (var input in _inputQueue) {
            if (input.type == type && Time.time <= input.expireTime) {
                _inputQueue.Clear();
                return true;
            }
        }
        return false;
    }

    private void ProcessExpiredInputs() {
        while (_inputQueue.Count > 0 && 
               _inputQueue.Peek().expireTime < Time.time) {
            _inputQueue.Dequeue();
        }
    }
}
  1. 动态物理材质控制
public class PhysicsMaterialController : MonoBehaviour {
    private Dictionary<Collider, PhysicMaterial> _originalMaterials = 
        new Dictionary<Collider, PhysicMaterial>();

    public void ApplyTemporaryMaterial(Collider collider, 
        PhysicMaterial material, float duration) {
        StartCoroutine(MaterialRoutine(collider, material, duration));
    }

    private IEnumerator MaterialRoutine(Collider collider, 
        PhysicMaterial material, float duration) {
        if (!_originalMaterials.ContainsKey(collider)) {
            _originalMaterials[collider] = collider.material;
        }
        
        collider.material = material;
        yield return new WaitForSeconds(duration);
        collider.material = _originalMaterials[collider];
    }
}

七、总结与展望

本战斗系统实现方案具有以下核心优势:

  1. 基于ECS架构实现高性能碰撞检测
  2. 使用动画事件驱动的精确战斗节奏控制
  3. 模块化的技能取消和连击系统设计
  4. 多层次的打击反馈体系(动画/物理/镜头/音效)

未来优化方向:

  1. 引入机器学习实现智能敌人AI
  2. 实现基于物理的布料和毛发模拟
  3. 开发可视化连招编辑工具
  4. 添加网络同步的预测回滚机制
  5. 集成基于Wwise的3D空间音效系统

建议开发者根据具体项目需求,结合DOTS技术栈优化大规模战斗场景,通过Shader Graph实现高级视觉效果,最终打造出具备主机级品质的移动端ACT战斗体验。

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐