策略模式(Strategy Pattern),解决的是“行为和算法的多样性”问题。

在 Unity 开发中,策略模式是用来消灭长篇大论的 if-elseswitch 语句的神兵利器。

💡 为什么要用策略模式?

假设你正在做一款射击游戏,玩家可以切换不同的武器(手枪、散弹枪、激光枪)。

新手的灾难写法:

public class PlayerWeapon : MonoBehaviour
{
    public enum WeaponType { Pistol, Shotgun, Laser }
    public WeaponType currentWeapon;

    public void Fire()
    {
        switch (currentWeapon)
        {
            case WeaponType.Pistol:
                Debug.Log("发射单发子弹!");
                break;
            case WeaponType.Shotgun:
                Debug.Log("发射扇形散弹!");
                break;
            case WeaponType.Laser:
                Debug.Log("发射持续激光!");
                break;
        }
    }
}

这种写法的缺点很明显:如果你要加 10 种新武器,这个 Fire 方法会变得极其臃肿。更糟糕的是,如果你想给散弹枪增加“击退效果”,你得在这个庞大的类里面修改,很容易把其他武器的代码弄坏(违背了开闭原则)。

策略模式的优雅解法:
把每一种“开火行为(策略)”单独封装成一个类。玩家(PlayerWeapon)不需要知道具体是怎么开火的,它手里只要拿着一个“开火策略”,调用开火就行了。随时可以把手里的策略替换掉!


一、 经典的纯 C# 接口写法

这是最标准的面向对象策略模式写法,通过 C# 的 interface 来实现。

1. 定义策略接口 (定义规则)

// 所有的开火行为,都必须实现这个接口
public interface IShootStrategy
{
    void Shoot(Transform firePoint);
}

2. 实现具体的策略 (把具体算法封装起来)

using UnityEngine;

// 策略 A:手枪单发
public class PistolStrategy : IShootStrategy
{
    public void Shoot(Transform firePoint)
    {
        Debug.Log("手枪开火:在 " + firePoint.position + " 生成了一发子弹");
        // 这里写具体的 Instantiate 逻辑
    }
}

// 策略 B:散弹枪
public class ShotgunStrategy : IShootStrategy
{
    public void Shoot(Transform firePoint)
    {
        Debug.Log("散弹开火:在 " + firePoint.position + " 生成了 5 发扇形子弹");
    }
}

3. 使用策略的上下文 (玩家武器控制器)

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public Transform firePoint;
    
    // 玩家当前持有的策略
    private IShootStrategy _currentShootStrategy;

    void Start()
    {
        // 默认装备手枪
        EquipWeapon(new PistolStrategy());
    }

    void Update()
    {
        // 随时可以在运行时切换策略!
        if (Input.GetKeyDown(KeyCode.Alpha1)) EquipWeapon(new PistolStrategy());
        if (Input.GetKeyDown(KeyCode.Alpha2)) EquipWeapon(new ShotgunStrategy());

        // 执行策略
        if (Input.GetKeyDown(KeyCode.Space))
        {
            // 玩家不需要管具体是怎么开火的,让策略自己去执行
            _currentShootStrategy?.Shoot(firePoint);
        }
    }

    // 切换策略的方法
    public void EquipWeapon(IShootStrategy newStrategy)
    {
        _currentShootStrategy = newStrategy;
        Debug.Log("切换了新武器!");
    }
}


二、 🌟 Unity 开发者特供版:ScriptableObject 策略 (强烈推荐)

上面的纯接口写法有一个问题:C# 的 interface 在 Unity 的 Inspector 面板里是无法直接拖拽赋值的! 这让策划和美术很难受。

在 Unity 中,实现策略模式最强大、最原生的方式是使用 ScriptableObject。它不仅能完美实现策略模式,还能让策划直接在面板上像搭积木一样配置技能和武器!

1. 定义策略基类 (继承 ScriptableObject)

using UnityEngine;

// 注意这里不再是 interface,而是继承自 ScriptableObject 的抽象类
public abstract class ShootStrategySO : ScriptableObject
{
    // 可以配置一些通用参数,比如攻击力、冷却时间
    public int damage;
    public float fireRate;

    // 抽象的开火方法,子类必须实现
    public abstract void Shoot(Transform firePoint);
}

2. 实现具体的策略资产

using UnityEngine;

// 添加这个标签,你就可以在 Project 右键菜单里创建这个策略的资产文件了!
[CreateAssetMenu(fileName = "New Pistol Strategy", menuName = "Weapons/Pistol Strategy")]
public class PistolStrategySO : ShootStrategySO
{
    public GameObject bulletPrefab; // 直接把预制体拖给这个策略

    public override void Shoot(Transform firePoint)
    {
        Debug.Log($"手枪开火,造成 {damage} 点伤害");
        Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
    }
}

[CreateAssetMenu(fileName = "New Shotgun Strategy", menuName = "Weapons/Shotgun Strategy")]
public class ShotgunStrategySO : ShootStrategySO
{
    public GameObject pelletPrefab;
    public int pelletCount = 5; // 散弹枪特有的参数

    public override void Shoot(Transform firePoint)
    {
        Debug.Log($"散弹枪开火,发射 {pelletCount} 发弹丸");
        // 具体的扇形发射逻辑...
    }
}

3. 在玩家代码中使用 (策划狂喜)

using UnityEngine;

public class PlayerShooter : MonoBehaviour
{
    public Transform firePoint;
    
    // 现在这个策略可以在 Inspector 面板里直接拖拽了!
    public ShootStrategySO currentWeaponStrategy;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && currentWeaponStrategy != null)
        {
            // 直接调用当前 SO 资产里写好的逻辑
            currentWeaponStrategy.Shoot(firePoint);
        }
    }
}

这种 Unity 特供版写法的爽点在哪?

  1. 策划可以在项目里右键创建出各种各样的武器策略文件(资产)。
  2. 想让哪个怪物或者玩家用哪把武器?直接把对应的策略资产拖拽到它身上的 currentWeaponStrategy 槽位里就行了,一行代码都不用改!
  3. 完美实现了代码和数据的分离。

💡 总结

  • 核心思想: 将变化的行为(怎么移动、怎么开火、怎么算分数)提取出来,封装成独立的类,互相替换。

  • 适用场景: * 敌人的 AI 状态(巡逻策略、追击策略、逃跑策略)。

  • 技能释放逻辑(火球术、冰冻术)。

  • 伤害计算公式(普通攻击计算、暴击计算、护甲穿透计算)。

  • 避坑指南: 如果你的行为只有两种(比如简单的开门和关门),直接用 if-else 就行了,不要强行写一堆策略类,避免过度设计导致文件数量爆炸。只有当行为种类繁多,且未来极大概率会继续增加时,策略模式才是你的救星。