使用工厂方法模式来生成敌人。
一:创建工厂的接口 + 工厂抽象类
(1)在工厂中配有创建敌人的方法,在具体的工厂中会保存有敌人的预制体引用。在创建的函数实现中,还会调用敌人类中的初始化方法。
(2)创建工厂的接口:让具体的敌人来创建,更加方便(可以利用多态):通过此接口我们可以实现提供一系列的敌人Prefab,然后全部自动创建工厂。
public interface ICreateFactory
{
public EnemyFactory CreateFactory(EnemyController enemyPrefab);
}
public abstract class EnemyFactory
{
public abstract EnemyController CreateEnemy();
}
public class MetalonFactory : EnemyFactory
{
private readonly MetalonController metalonPrefab;
public MetalonFactory(MetalonController metalonPrefab)
{
this.metalonPrefab = metalonPrefab;
}
public override EnemyController CreateEnemy()
{
var metalon = Object.Instantiate(metalonPrefab);
metalon.InitAfterGenerate();
return metalon;
}
}
public class GolemFactory : EnemyFactory
{
private readonly GolemController golemPrefab;
public GolemFactory(GolemController golemPrefab)
{
this.golemPrefab = golemPrefab;
}
public override EnemyController CreateEnemy()
{
var golem = Object.Instantiate(golemPrefab);
golem.InitAfterGenerate();
return golem;
}
}
二:敌人生成器
(1)在编译器窗口中,我们只需要把所有的敌人预制体放入即可自动创建所有的工厂。
(2)为所有敌人增加了阶级属性(普通敌人,精英敌人,BOSS敌人),并且分别创建了对应的工厂列表,这样可以方便地随机生成想要的阶级的敌人。同时建立了一个<敌人名称,敌人工厂>的字典,可以供我们生成具体想要的敌人类型(在每个Enemy中有静态字段表示名称)。
(3)设置一个简单的基于波数的战斗规则(规则可自行定义)。分为普通波和BOSS波,每一波后有短暂的间隔,BOSS战后有较长的休息时间,同时回家的传送门会打开,无限循环即可。
public class EnemyGenerator : MonoBehaviour
{
[Header("敌人预制体")]
[SerializeField] private List<EnemyController> enemyPrefabs;
[Header("敌人生成设置")]
[SerializeField] private float generateTime; // 每波的冷却时间(杀死所有敌人后)
[SerializeField] private int generateMinEnemyCount; // 每波生成的最少敌人
[SerializeField] private int generateMaxEnemyCount; // 每波生成的最大敌人
[SerializeField] private float generateEliteProbability; // 生成精英敌人的概率
[SerializeField] private float restTimeAfterBossFight; // boss战后的调整时间
[Header("引用")]
[SerializeField] private GameObject transitionPatrol; // 战斗场景的传送门:规则打完boss之后会打开一段时间
private int generateTimes; // 当前的波数:每10波生成一个boss
private bool isBossFightOver; // 是否是在Boss波结束后
private bool isFightOver; // 是否是在一波战斗结束后
// 不同阶级敌人的生成工厂:用于在某个阶级上随机生成不同种类敌人
private readonly List<EnemyFactory> normalEnemyFactories = new();
private readonly List<EnemyFactory> eliteEnemyFactories = new();
private readonly List<EnemyFactory> bossEnemyFactories = new();
// 字典工厂:可以指定生成某种类的敌人
private readonly Dictionary<string, EnemyFactory> enemyNameToFactories = new();
private float generateTimeCounter; // 生成冷却时间的计时器
public float GenerateTimeCounter
{
get => generateTimeCounter;
set
{
generateTimeCounter = value;
if (value <= 0)
{
StartCoroutine(GenerateEnemy());
}
}
}
private void Awake()
{
foreach (var enemyPrefab in enemyPrefabs)
{
var factory = enemyPrefab.CreateFactory(enemyPrefab);
switch (enemyPrefab.EnemyLevel)
{
case EnemyLevelType.Normal:
normalEnemyFactories.Add(factory);
break;
case EnemyLevelType.Elite:
eliteEnemyFactories.Add(factory);
break;
case EnemyLevelType.Boss:
bossEnemyFactories.Add(factory);
break;
}
enemyNameToFactories.Add(enemyPrefab.EnemyName, factory);
}
}
private void Start()
{
generateTimes = 0;
GenerateTimeCounter = generateTime;
}
private void OnEnable()
{
GlobalEvent.enemyDeathEvent += CheckFightOver;
}
private void OnDisable()
{
GlobalEvent.enemyDeathEvent -= CheckFightOver;
}
private void CheckFightOver(EnemyController enemyController)
{
isFightOver = GameManager.Instance.enemies.Count == 0;
isBossFightOver = isFightOver & (generateTimes % 10 == 0);
if (isBossFightOver)
{
//激活传送门
transitionPatrol.gameObject.SetActive(true);
generateTimeCounter = restTimeAfterBossFight;
}
}
private void Update()
{
// 计时器计时的规则:1:当前是战斗场景 2:敌人数量为0 3:当前不是正在加载场景
if (SceneLoader.Instance.GetCurrentSceneType == SceneType.FightScene && GameManager.Instance.enemies.Count == 0&& !SceneLoader.Instance.IsLoading)
GenerateTimeCounter -= Time.deltaTime;
// 如果在boss波结束后按下E键 则立刻进入下一波
if (isBossFightOver&&Input.GetKeyDown(KeyCode.E))
{
GenerateTimeCounter = 0;
}
}
public IEnumerator GenerateEnemy()
{
++generateTimes;
transitionPatrol.gameObject.SetActive(false);
// 生成boss类敌人
if (generateTimes % 10 == 0)
{
CreateRandomBossEnemy();
yield break;
}
// 生成普通/精英敌人
int generateEnemyCount = Random.Range(generateMinEnemyCount, generateMaxEnemyCount);
for (int i = 0; i < generateEnemyCount; i++)
{
float random = Random.value;
if (random < generateEliteProbability)
{
CreateRandomEliteEnemy();
}
else
{
CreateRandomNormalEnemy();
}
yield return new WaitForSeconds(1f);
}
}
private void CreateRandomNormalEnemy()
{
var enemy = normalEnemyFactories[Random.Range(0, normalEnemyFactories.Count)].CreateEnemy();
}
private void CreateRandomEliteEnemy()
{
var enemy = eliteEnemyFactories[Random.Range(0, eliteEnemyFactories.Count)].CreateEnemy();
}
private void CreateRandomBossEnemy()
{
// TODO:暂时没有设计boss
var enemy = eliteEnemyFactories[Random.Range(0, eliteEnemyFactories.Count)].CreateEnemy();
// var enemy = bossEnemyFactories[Random.Range(0, bossEnemyFactories.Count)].CreateEnemy();
}
private void CreateEnemyByName(string enemyName)
{
if (enemyNameToFactories.TryGetValue(enemyName, out var factory))
{
var enemy = factory.CreateEnemy();
}
}
}