实现一款商业化级FPS第一人称游戏PEV刷怪流程框架

下面为你设计一套商业化级FPS第一人称游戏PVE刷怪流程框架(PVE Enemy Spawning System Framework),适用于Unity(C#),也可迁移到其他引擎。该框架强调高性能、高配置化、高可用、易扩展、热更友好、支持复杂关卡与波次、支持AI与多样怪物类型,适合商业项目。


1. 框架目标与核心原则

  • 高性能:对象池、分帧生成、异步加载、内存优化
  • 高配置化:怪物、波次、关卡、出生点、AI等全部配置表驱动,支持热更
  • 高可用:流程解耦、易维护、易扩展、支持多种刷怪模式
  • 商业级特性:多样怪物、精英/首领、AI行为、动态难度、事件驱动、统计与掉落、网络同步等

2. 框架模块结构

2.1 主要模块

  • SpawnManager:刷怪总控,管理关卡、波次、怪物生成、流程控制
  • WaveConfig:波次配置表,支持热更
  • MonsterConfig:怪物配置表,支持热更
  • SpawnPoint:刷怪点管理
  • MonsterPool:怪物对象池
  • MonsterBase:怪物基类,支持多态
  • AIController:AI行为控制
  • SpawnEventBus:事件系统
  • DropManager:掉落与奖励
  • DifficultyManager:动态难度调整
  • SpawnStatistics:刷怪统计与分析

3. 代码结构与核心实现

3.1 WaveConfig(波次配置)

[Serializable]
public class WaveConfigItem
{
    public int WaveId;
    public float StartDelay; // 波次开始延迟
    public List<SpawnInfo> Spawns; // 本波次刷怪信息
}

[Serializable]
public class SpawnInfo
{
    public string MonsterId;
    public int Count;
    public float Interval; // 间隔
    public List<int> SpawnPointIds; // 可用刷怪点
}

public class WaveConfig
{
    public List<WaveConfigItem> Waves;

    public static WaveConfig Load()
    {
        var json = Resources.Load<TextAsset>("WaveConfig").text;
        return JsonUtility.FromJson<WaveConfig>(json);
    }
}

3.2 MonsterConfig(怪物配置)

[Serializable]
public class MonsterConfigItem
{
    public string MonsterId;
    public string Name;
    public string PrefabPath;
    public float HP;
    public float MoveSpeed;
    public float Attack;
    public float AttackInterval;
    public string AIType;
    public string DropTableId;
    // ...更多属性
}

public class MonsterConfig
{
    public List<MonsterConfigItem> Monsters;

    public static MonsterConfig Load()
    {
        var json = Resources.Load<TextAsset>("MonsterConfig").text;
        return JsonUtility.FromJson<MonsterConfig>(json);
    }

    public MonsterConfigItem GetConfig(string monsterId)
    {
        return Monsters.Find(m => m.MonsterId == monsterId);
    }
}

3.3 SpawnPoint(刷怪点)

public class SpawnPoint : MonoBehaviour
{
    public int PointId;
    public bool IsOccupied;
}

3.4 MonsterPool(对象池)

public class MonsterPool
{
    public static MonsterPool Instance { get; } = new MonsterPool();
    private Dictionary<string, Queue<MonsterBase>> pool = new Dictionary<string, Queue<MonsterBase>>();

    public MonsterBase GetMonster(string monsterId)
    {
        if (!pool.ContainsKey(monsterId)) pool[monsterId] = new Queue<MonsterBase>();
        if (pool[monsterId].Count > 0)
            return pool[monsterId].Dequeue();

        var config = MonsterConfig.Load().GetConfig(monsterId);
        var prefab = Resources.Load<GameObject>(config.PrefabPath);
        var go = GameObject.Instantiate(prefab);
        var monster = go.GetComponent<MonsterBase>();
        monster.Init(config);
        return monster;
    }

    public void RecycleMonster(MonsterBase monster)
    {
        monster.gameObject.SetActive(false);
        pool[monster.Config.MonsterId].Enqueue(monster);
    }
}

3.5 MonsterBase(怪物基类)

public abstract class MonsterBase : MonoBehaviour
{
    public MonsterConfigItem Config { get; private set; }
    public AIController AI { get; private set; }
    public int CurrentHP { get; private set; }

    public virtual void Init(MonsterConfigItem config)
    {
        Config = config;
        CurrentHP = (int)config.HP;
        AI = AIControllerFactory.Create(config.AIType, this);
    }

    public virtual void OnSpawn(Vector3 pos)
    {
        transform.position = pos;
        gameObject.SetActive(true);
        AI?.OnSpawn();
    }

    public virtual void OnDead()
    {
        AI?.OnDead();
        DropManager.Instance.HandleDrop(Config.DropTableId, transform.position);
        MonsterPool.Instance.RecycleMonster(this);
        SpawnEventBus.Send("OnMonsterDead", this);
    }

    public virtual void TakeDamage(int damage)
    {
        CurrentHP -= damage;
        if (CurrentHP <= 0) OnDead();
    }
}

3.6 AIController(AI行为)

public abstract class AIController
{
    protected MonsterBase monster;
    public AIController(MonsterBase monster) { this.monster = monster; }
    public virtual void OnSpawn() { }
    public virtual void OnDead() { }
    public abstract void Update();
}

public class SimpleChaseAI : AIController
{
    public SimpleChaseAI(MonsterBase monster) : base(monster) { }
    public override void Update()
    {
        // 追击玩家逻辑
    }
}

public static class AIControllerFactory
{
    public static AIController Create(string aiType, MonsterBase monster)
    {
        switch (aiType)
        {
            case "SimpleChase": return new SimpleChaseAI(monster);
            // ...更多AI类型
            default: return null;
        }
    }
}

3.7 SpawnManager(刷怪总控)

public class SpawnManager : MonoBehaviour
{
    public static SpawnManager Instance { get; private set; }
    public List<SpawnPoint> spawnPoints;
    private WaveConfig waveConfig;
    private int currentWave = 0;
    private int aliveMonsters = 0;
    private bool isSpawning = false;

    void Awake() { Instance = this; }

    public void Init()
    {
        waveConfig = WaveConfig.Load();
        currentWave = 0;
        StartCoroutine(SpawnWaveRoutine());
    }

    private IEnumerator SpawnWaveRoutine()
    {
        while (currentWave < waveConfig.Waves.Count)
        {
            var wave = waveConfig.Waves[currentWave];
            yield return new WaitForSeconds(wave.StartDelay);

            foreach (var spawn in wave.Spawns)
            {
                StartCoroutine(SpawnMonsters(spawn));
            }

            isSpawning = true;
            while (aliveMonsters > 0 || isSpawning)
                yield return null;

            currentWave++;
            SpawnEventBus.Send("OnWaveEnd", currentWave);
        }
        SpawnEventBus.Send("OnAllWavesEnd", null);
    }

    private IEnumerator SpawnMonsters(SpawnInfo spawn)
    {
        isSpawning = true;
        for (int i = 0; i < spawn.Count; i++)
        {
            var point = GetAvailableSpawnPoint(spawn.SpawnPointIds);
            if (point == null) yield break;

            var monster = MonsterPool.Instance.GetMonster(spawn.MonsterId);
            monster.OnSpawn(point.transform.position);
            aliveMonsters++;
            point.IsOccupied = true;

            monster.AI = AIControllerFactory.Create(monster.Config.AIType, monster);

            monster.gameObject.SetActive(true);
            monster.AI?.OnSpawn();

            monster.OnDead += () =>
            {
                aliveMonsters--;
                point.IsOccupied = false;
            };

            yield return new WaitForSeconds(spawn.Interval);
        }
        isSpawning = false;
    }

    private SpawnPoint GetAvailableSpawnPoint(List<int> ids)
    {
        foreach (var id in ids)
        {
            var point = spawnPoints.Find(p => p.PointId == id && !p.IsOccupied);
            if (point != null) return point;
        }
        return null;
    }
}

3.8 SpawnEventBus(事件系统)

public static class SpawnEventBus
{
    private static Dictionary<string, Action<object>> eventTable = new Dictionary<string, Action<object>>();

    public static void Register(string eventName, Action<object> callback)
    {
        if (!eventTable.ContainsKey(eventName))
            eventTable[eventName] = delegate { };
        eventTable[eventName] += callback;
    }

    public static void Unregister(string eventName, Action<object> callback)
    {
        if (eventTable.ContainsKey(eventName))
       eventTable[eventName] -= callback;   
     }
 }      

继续为你完善商业化级FPS第一人称游戏PVE刷怪流程框架,补充掉落、动态难度、统计、热更、性能优化、流程控制等高级特性,并给出完整的流程示例。


3.9 DropManager(掉落与奖励)

public class DropManager : MonoBehaviour
{
    public static DropManager Instance { get; private set; }
    private Dictionary<string, DropTable> dropTables;

    void Awake()
    {
        Instance = this;
        LoadDropTables();
    }

    void LoadDropTables()
    {
        // 假设本地或服务器热更加载
        var json = Resources.Load<TextAsset>("DropTableConfig").text;
        dropTables = JsonUtility.FromJson<DropTableList>(json).ToDictionary();
    }

    public void HandleDrop(string dropTableId, Vector3 pos)
    {
        if (!dropTables.ContainsKey(dropTableId)) return;
        var table = dropTables[dropTableId];
        var dropItem = table.GetRandomDrop();
        if (dropItem != null)
        {
            // 生成掉落物
            var prefab = Resources.Load<GameObject>(dropItem.PrefabPath);
            GameObject.Instantiate(prefab, pos, Quaternion.identity);
        }
    }
}

[Serializable]
public class DropTable
{
    public string DropTableId;
    public List<DropItem> Items;

    public DropItem GetRandomDrop()
    {
        float totalWeight = Items.Sum(i => i.Weight);
        float rand = UnityEngine.Random.Range(0, totalWeight);
        float acc = 0;
        foreach (var item in Items)
        {
            acc += item.Weight;
            if (rand <= acc) return item;
        }
        return null;
    }
}

[Serializable]
public class DropItem
{
    public string ItemId;
    public string PrefabPath;
    public float Weight;
}

[Serializable]
public class DropTableList
{
    public List<DropTable> Tables;
    public Dictionary<string, DropTable> ToDictionary()
    {
        return Tables.ToDictionary(t => t.DropTableId, t => t);
    }
}

3.10 DifficultyManager(动态难度调整)

public class DifficultyManager : MonoBehaviour
{
    public static DifficultyManager Instance { get; private set; }
    public float DifficultyMultiplier { get; private set; } = 1f;

    void Awake() { Instance = this; }

    public void AdjustDifficulty(int wave, float playerPerformance)
    {
        // 例如:根据波次和玩家表现动态调整
        DifficultyMultiplier = 1f + wave * 0.1f + playerPerformance * 0.2f;
    }

    public float ApplyDifficulty(float baseValue)
    {
        return baseValue * DifficultyMultiplier;
    }
}

MonsterBase.Init等处可用DifficultyManager.Instance.ApplyDifficulty(config.HP)等方式动态调整怪物属性。


3.11 SpawnStatistics(刷怪统计与分析)

public class SpawnStatistics
{
    public int TotalSpawned { get; private set; }
    public int TotalKilled { get; private set; }
    public Dictionary<string, int> MonsterKillCount = new Dictionary<string, int>();

    public void OnMonsterSpawn(string monsterId)
    {
        TotalSpawned++;
    }

    public void OnMonsterDead(string monsterId)
    {
        TotalKilled++;
        if (!MonsterKillCount.ContainsKey(monsterId))
            MonsterKillCount[monsterId] = 0;
        MonsterKillCount[monsterId]++;
    }
}

SpawnManagerMonsterBase.OnDead中调用统计接口。


3.12 热更与配置化

  • 配置表(波次、怪物、掉落、AI等)全部支持本地/服务器热更,支持JSON、ScriptableObject等格式。
  • AI行为掉落表刷怪点等均可通过配置表扩展,无需改动代码。
  • 关卡流程可通过配置表描述(如关卡脚本、事件触发等)。

3.13 性能优化建议

  • 对象池:怪物、掉落物、特效等全部池化,避免频繁销毁/创建。
  • 分帧生成:大波次怪物分帧生成,避免卡顿。
  • 异步加载:怪物、掉落物等资源异步加载,支持预加载。
  • 刷怪点复用:刷怪点可动态分配,避免同一位置堆叠。
  • AI分帧/分组更新:大量怪物AI可分帧或分组更新,降低CPU压力。

3.14 流程控制与事件驱动

  • 事件总线(SpawnEventBus)用于解耦流程,如波次开始/结束、怪物死亡、关卡胜利/失败等。
  • 流程控制:支持暂停、加速、跳波、重开等功能,便于调试和运营活动。

3.15 典型刷怪流程示例

// 1. 关卡开始
SpawnManager.Instance.Init();

// 2. 进入波次循环
//    - 延迟后开始波次
//    - 读取波次配置,分帧生成怪物
//    - 怪物生成后,AI启动,统计+难度调整

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值