策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,把它们封装起来,并使它们可以相互替换,从而使算法的变化独立于使用算法的客户端。简而言之,策略模式提供了一种动态选择算法或行为的方式。

在Unity中,策略模式非常适合以下场景:

  • 游戏角色的多种攻击方式(近战、远程、魔法)。
  • 敌人AI的不同行为(巡逻、追击、逃跑)。
  • 游戏中的运动方式(飞行、游泳、跑步)。

1. 策略模式的基本概念

核心结构

策略模式由以下几个部分组成:

  1. 策略接口(Strategy):定义算法的通用接口。
  2. 具体策略(ConcreteStrategy):实现不同的算法或行为。
  3. 上下文(Context):持有策略的引用,并调用策略的方法。

优点

  1. 算法独立:可以在不修改使用算法的代码的情况下替换算法。
  2. 扩展性强:可以轻松新增策略,而无需修改现有代码。
  3. 简化代码:避免了大量的if-else或switch-case语句。

缺点

  1. 增加类的数量:每个策略需要一个类。
  2. 客户端需要了解策略的细节:客户端需要知道有哪些策略才能选择合适的策略。

2. Unity 实现策略模式

以下通过一个示例,展示如何在Unity中用策略模式实现游戏角色的多种攻击方式。


3. 实现步骤

(1) 定义策略接口

创建一个接口,定义所有攻击方式的通用方法。

// IAttackStrategy.cs
public interface IAttackStrategy
{
    void Attack();
}

(2) 实现具体策略

为每种攻击方式创建一个具体的策略类,实现攻击逻辑。

// MeleeAttack.cs
using UnityEngine;

public class MeleeAttack : IAttackStrategy
{
    public void Attack()
    {
        Debug.Log("Performing a melee attack!");
    }
}

// RangedAttack.cs
using UnityEngine;

public class RangedAttack : IAttackStrategy
{
    public void Attack()
    {
        Debug.Log("Performing a ranged attack!");
    }
}

// MagicAttack.cs
using UnityEngine;

public class MagicAttack : IAttackStrategy
{
    public void Attack()
    {
        Debug.Log("Performing a magic attack!");
    }
}

(3) 创建上下文类

上下文类持有策略的引用,并通过调用策略的方法来执行算法。

// Character.cs
using UnityEngine;

public class Character : MonoBehaviour
{
    private IAttackStrategy _attackStrategy;

    // 设置攻击策略
    public void SetAttackStrategy(IAttackStrategy attackStrategy)
    {
        _attackStrategy = attackStrategy;
    }

    // 执行攻击
    public void PerformAttack()
    {
        if (_attackStrategy != null)
        {
            _attackStrategy.Attack();
        }
        else
        {
            Debug.LogWarning("Attack strategy is not set!");
        }
    }
}

(4) 测试策略模式

创建一个测试脚本,动态切换角色的攻击策略。

// GameManager.cs
using UnityEngine;

public class GameManager : MonoBehaviour
{
    void Start()
    {
        Character character = new GameObject("Player").AddComponent<Character>();

        // 设置近战攻击策略
        character.SetAttackStrategy(new MeleeAttack());
        character.PerformAttack(); // 输出:Performing a melee attack!

        // 切换到远程攻击策略
        character.SetAttackStrategy(new RangedAttack());
        character.PerformAttack(); // 输出:Performing a ranged attack!

        // 切换到魔法攻击策略
        character.SetAttackStrategy(new MagicAttack());
        character.PerformAttack(); // 输出:Performing a magic attack!
    }
}

4. 运行结果

运行游戏后,控制台将输出以下内容:

Performing a melee attack!
Performing a ranged attack!
Performing a magic attack!

5. 扩展与优化

(1) 通过脚本动态切换策略

如果需要根据用户输入动态切换策略,可以监听输入并调用 SetAttackStrategy 方法。例如,使用键盘按键切换攻击方式:

void Update()
{
    if (Input.GetKeyDown(KeyCode.Alpha1))
    {
        character.SetAttackStrategy(new MeleeAttack());
    }
    else if (Input.GetKeyDown(KeyCode.Alpha2))
    {
        character.SetAttackStrategy(new RangedAttack());
    }
    else if (Input.GetKeyDown(KeyCode.Alpha3))
    {
        character.SetAttackStrategy(new MagicAttack());
    }

    if (Input.GetKeyDown(KeyCode.Space))
    {
        character.PerformAttack();
    }
}

(2) 使用ScriptableObject管理策略

在Unity中,可以使用 ScriptableObject 来管理策略。这样可以通过Inspector设置策略,而不是在代码中硬编码具体策略。

创建攻击策略基类:

// AttackStrategyBase.cs
using UnityEngine;

public abstract class AttackStrategyBase : ScriptableObject
{
    public abstract void Attack();
}

具体策略实现:

// MeleeAttackSO.cs
[CreateAssetMenu(fileName = "MeleeAttack", menuName = "Strategies/MeleeAttack")]
public class MeleeAttackSO : AttackStrategyBase
{
    public override void Attack()
    {
        Debug.Log("Performing a melee attack (SO)!");
    }
}

// RangedAttackSO.cs
[CreateAssetMenu(fileName = "RangedAttack", menuName = "Strategies/RangedAttack")]
public class RangedAttackSO : AttackStrategyBase
{
    public override void Attack()
    {
        Debug.Log("Performing a ranged attack (SO)!");
    }
}

修改上下文类:

public class Character : MonoBehaviour
{
    [SerializeField] private AttackStrategyBase attackStrategy;

    public void PerformAttack()
    {
        if (attackStrategy != null)
        {
            attackStrategy.Attack();
        }
        else
        {
            Debug.LogWarning("Attack strategy is not set!");
        }
    }

    public void SetAttackStrategy(AttackStrategyBase newStrategy)
    {
        attackStrategy = newStrategy;
    }
}

通过这种方式,可以在Inspector中直接拖拽不同的ScriptableObject来切换策略。


(3) 使用对象池优化策略实例

如果策略对象的创建和销毁较为频繁,可以考虑结合 对象池 来优化内存管理。


6. 优化与注意事项

(1) 避免策略对象的耦合

每个策略类应该是独立的,不应该依赖其他策略类或上下文类。

(2) 注意性能开销

虽然策略模式可以动态切换算法,但频繁的切换可能会导致性能开销,尤其是在复杂场景中。

(3) 简化客户端使用

通过组合模式或工厂模式,可以简化客户端对策略类的使用。例如,通过一个工厂类动态创建所需的策略。


7. 总结

策略模式的优缺点
优点缺点
提高代码的灵活性,可以动态替换行为。增加了类的数量,每个策略需要一个具体类。
避免了大量的if-else或switch-case语句。如果策略过多,可能会增加代码复杂度。
易于扩展:新增策略时无需修改已有代码。客户端需要知道可用的策略,以便切换策略。

适用场景
  1. 游戏角色需要支持多种可切换的行为(如攻击方式、移动方式等)。
  2. 需要避免复杂的条件分支语句。
  3. 不同场景需要动态选择算法或行为。

通过策略模式,Unity中的角色行为或算法逻辑可以更加灵活和模块化,同时也为扩展提供了良好的支持。

更多推荐