游戏战斗系统编码思路

By pocaster

游戏战斗系统编码思路

1. 战斗系统架构设计

1.1 核心组件分层

graph TD A[输入层] --> B[逻辑层] B --> C[表现层] D[数据层] --> B E[网络层] --> B

1.2 状态驱动设计

\(\text{战斗实体状态} = \begin{cases} \text{Idle} & \text{待机} \\ \text{Attack} & \text{攻击} \\ \text{Hit} & \text{受击} \\ \text{Skill} & \text{技能} \\ \text{Dead} & \text{死亡} \end{cases}\)

2. 伤害计算系统

2.1 基础公式

\(\text{最终伤害} = (\text{基础攻击} \times \text{暴击系数} - \text{防御减免}) \times \text{元素反应倍率}\)

2.2 属性克制关系

flowchart LR 火 --> 冰[+30%伤害] 冰 --> 雷[+25%伤害] 雷 --> 水[+20%伤害] 水 --> 火[+15%伤害]

3. 技能系统实现

3.1 技能数据结构

graph TD A[技能数据层] --> B[技能逻辑层] B --> C[技能表现层]
{
  "skill_id": 1001,
  "cooldown": 5.0,
  "hit_count": 3,
  "damage_ratio": 0.8,
  "effect_prefab": "Assets/Effects/fireball.prefab",
  "hit_sound": "Audio/SFX/fire_explosion.wav"
}

3.2 技能状态机

public abstract class SkillState : IState
{
    public virtual void OnEnter() {}
    public virtual void OnUpdate() {}
    public virtual void OnExit() {}
  
    protected void PlayVFX(GameObject prefab) 
    {
        // 粒子特效播放逻辑
    }
}

4. 连招系统设计

4.1 输入缓冲机制

class InputBuffer:
    def __init__(self):
        self.buffer = []
        self.buffer_time = 0.3  # 秒
      
    def add_input(self, input_type):
        self.buffer.append({
            'type': input_type,
            'time': Time.time
        })
      
    def get_valid_input(self):
        current_time = Time.time
        return [i for i in self.buffer 
                if current_time - i['time'] <= self.buffer_time]

4.2 连招判定树

stateDiagram-v2 [*] --> Idle Idle --> Attack1: 轻攻击 Attack1 --> Attack2: 0.5秒内轻攻击 Attack2 --> Attack3: 0.5秒内重攻击 Attack3 --> Special: 成功触发终结技

5. 网络同步方案

5.1 帧同步与状态同步对比

方案 带宽消耗 实现复杂度 适用场景
帧同步 格斗/MOBA
状态同步 MMO/RPG

5.2 伤害同步逻辑

[Command]
void CmdApplyDamage(int targetId, int skillId)
{
    // 服务器验证
    if(IsValidAttack(targetId, skillId)) 
    {
        int damage = CalculateDamage(skillId);
        TargetRpcTakeDamage(targetId, damage);
    }
}

6. 性能优化策略

6.1 碰撞检测优化

  • 使用空间分区树(QuadTree/Octree)
  • 伤害判定采用球体/胶囊体简化碰撞体
  • 非活跃实体跳过检测

6.2 动画事件优化

void OnAttackFrameEvent(int frameIndex)
{
    if(frameIndex == 12) // 关键帧触发伤害
    {
        ApplyHitBox();
    }
}

7. 现代战斗系统趋势

7.1 物理引擎融合

  • 布娃娃系统受击反应
  • 基于力的击飞计算
  • 可破坏环境交互

7.2 AI增强战斗

pie title AI行为权重 "攻击倾向" : 45 "防御倾向" : 30 "走位策略" : 25

高级战斗系统实现专题

1. 《鬼泣》风格华丽连招系统实现

1.1 连招架构设计

classDiagram class ComboSystem{ +InputBuffer buffer +ComboTree comboTree +EvaluateCombo() Skill } class ComboNode{ +string animation +float cancelWindow +ComboLink[] links } class ComboLink{ +InputType input +ComboNode nextNode } ComboSystem --> ComboNode ComboNode --> ComboLink

1.2 关键技术实现

1.2.1 动画取消窗口

// Unity示例:动画状态机行为脚本
public class ComboCancelState : StateMachineBehaviour
{
    [Range(0,1)] public float cancelStart = 0.3f;
    [Range(0,1)] public float cancelEnd = 0.7f;
  
    override public void OnStateUpdate(Animator animator, 
                                    AnimatorStateInfo stateInfo,
                                    int layerIndex)
    {
        float normalizedTime = stateInfo.normalizedTime % 1;
        bool inCancelWindow = normalizedTime > cancelStart && 
                            normalizedTime < cancelEnd;
        animator.SetBool("CanCancel", inCancelWindow);
    }
}

1.2.2 风格评分系统

class StyleMeter:
    def __init__(self):
        self.points = 0
        self.multiplier = 1
        self.decay_rate = 0.5  # 点数每秒衰减率
      
    def add_action(self, action_type):
        base_points = {
            'basic_attack': 50,
            'aerial_attack': 80,
            'dodge': 30,
            'taunt': 100
        }
        self.points += base_points[action_type] * self.multiplier
        self.multiplier = min(5, self.multiplier + 0.2)
      
    def update(self, delta_time):
        self.points = max(0, self.points - self.decay_rate * delta_time)
        if self.points <= 0:
            self.multiplier = 1

2. 《黑暗之魂》类锁定机制实现方案

2.1 锁定系统架构

sequenceDiagram Player->>LockSystem: 按下锁定键 LockSystem->>TargetManager: 获取可锁定目标列表 TargetManager->>SpatialHash: 查询10m内敌人 SpatialHash-->>TargetManager: 返回候选目标 TargetManager->>LockSystem: 按优先级排序 LockSystem->>CameraController: 设置注视目标

2.2 关键技术实现

2.2.1 目标选择算法

public class LockOnTargetSelector : MonoBehaviour
{
    [SerializeField] float maxDistance = 10f;
    [SerializeField] float angleThreshold = 45f;
  
    public Transform FindBestTarget(Vector3 playerPosition, 
                                  Vector3 playerForward)
    {
        Collider[] candidates = Physics.OverlapSphere(
            playerPosition, maxDistance, LayerMask.GetMask("Enemy"));
          
        Transform bestTarget = null;
        float bestScore = float.MinValue;
      
        foreach (var col in candidates)
        {
            Vector3 dirToTarget = (col.transform.position - playerPosition).normalized;
            float angle = Vector3.Angle(playerForward, dirToTarget);
          
            if (angle <= angleThreshold)
            {
                float distanceScore = 1 - Vector3.Distance(
                    playerPosition, col.transform.position) / maxDistance;
                float angleScore = 1 - angle / angleThreshold;
                float totalScore = distanceScore * 0.4f + angleScore * 0.6f;
              
                if (totalScore > bestScore)
                {
                    bestScore = totalScore;
                    bestTarget = col.transform;
                }
            }
        }
        return bestTarget;
    }
}

2.2.2 相机混合控制

public class LockCamera : MonoBehaviour
{
    public Transform target;
    public float rotationSpeed = 5f;
    public float positionLerpSpeed = 3f;
  
    void LateUpdate()
    {
        if (target != null)
        {
            // 平滑旋转
            Quaternion targetRotation = Quaternion.LookRotation(
                target.position - transform.position);
            transform.rotation = Quaternion.Slerp(
                transform.rotation, targetRotation, 
                rotationSpeed * Time.deltaTime);
              
            // 位置跟随
            Vector3 idealPosition = CalculateIdealPosition();
            transform.position = Vector3.Lerp(
                transform.position, idealPosition,
                positionLerpSpeed * Time.deltaTime);
        }
    }
  
    Vector3 CalculateIdealPosition()
    {
        // 基于角色背后偏移量计算
        return player.position - player.forward * 3f + Vector3.up * 1.5f;
    }
}

3. 大规模战斗场景优化技巧

3.1 实体管理优化

3.1.1 分帧更新系统

public class BatchUpdateSystem : MonoBehaviour
{
    private List<Enemy> activeEnemies = new List<Enemy>();
    private int updatesPerFrame = 10;
  
    void Update()
    {
        int count = Mathf.Min(updatesPerFrame, activeEnemies.Count);
        for (int i = 0; i < count; i++)
        {
            int index = (Time.frameCount + i) % activeEnemies.Count;
            activeEnemies[index].UpdateLogic();
        }
    }
}

3.1.2 LOD分级策略

graph TD A[战斗实体] --> B{距离玩家} B -->|<=5m| C[高精度模型] B -->|5-15m| D[简化骨骼动画] B -->|>15m| E[代理表示]

3.2 渲染优化方案

3.2.1 GPU Instancing材质

// Shader示例
UNITY_INSTANCING_BUFFER_START(Props)
    UNITY_DEFINE_INSTANCED_PROP(float4, _ColorArray)
    UNITY_DEFINE_INSTANCED_PROP(float, _HealthRatio)
UNITY_INSTANCING_BUFFER_END(Props)

void surf(Input IN, inout SurfaceOutputStandard o)
{
    o.Albedo = UNITY_ACCESS_INSTANCED_PROP(Props, _ColorArray).rgb;
    o.Emission = lerp(float3(1,0,0), float3(0,1,0), 
                     UNITY_ACCESS_INSTANCED_PROP(Props, _HealthRatio));
}

3.2.2 可见性剔除优化

public class OcclusionCullingManager : MonoBehaviour
{
    public Camera mainCamera;
    public float checkInterval = 0.2f;
  
    IEnumerator Start()
    {
        while (true)
        {
            Plane[] planes = GeometryUtility.CalculateFrustumPlanes(mainCamera);
            foreach (var enemy in EnemyManager.Instance.Enemies)
            {
                bool visible = GeometryUtility.TestPlanesAABB(
                    planes, enemy.GetComponent<Renderer>().bounds);
                enemy.SetLODLevel(visible ? LODLevel.High : LODLevel.Low);
            }
            yield return new WaitForSeconds(checkInterval);
        }
    }
}

3.3 网络同步优化

3.3.1 兴趣管理系统(AOI)

class AreaOfInterest:
    def __init__(self, cell_size=10):
        self.cells = defaultdict(set)
        self.cell_size = cell_size
  
    def update_entity(self, entity_id, position):
        cell_x = int(position.x / self.cell_size)
        cell_z = int(position.z / self.cell_size)
        self.cells[(cell_x, cell_z)].add(entity_id)
  
    def get_nearby_entities(self, position, radius):
        result = set()
        cells_in_radius = radius // self.cell_size
        center_x = int(position.x / self.cell_size)
        center_z = int(position.z / self.cell_size)
      
        for x in range(center_x - cells_in_radius, 
                       center_x + cells_in_radius + 1):
            for z in range(center_z - cells_in_radius,
                          center_z + cells_in_radius + 1):
                result.update(self.cells.get((x,z), set()))
        return result

3.3.2 状态压缩同步

public class NetworkEnemy : NetworkBehaviour
{
    [SyncVar(hook = nameof(OnHealthChanged))]
    private ushort compressedHealth;
  
    private void OnHealthChanged(ushort newValue)
    {
        float realHealth = newValue / 65535f * maxHealth;
        UpdateHealthDisplay(realHealth);
    }
  
    [Command]
    public void CmdTakeDamage(float damage)
    {
        currentHealth -= damage;
        compressedHealth = (ushort)(currentHealth / maxHealth * 65535);
    }
}
Tags: Gamedev Public