使用命令模式实现《植物大战僵尸》兵营生产系统

使用命令模式实现《植物大战僵尸》兵营生产系统

在游戏开发中,命令模式(Command Pattern) 是一种非常实用的设计模式,尤其适用于需要将请求封装为对象、支持撤销操作、或实现请求队列的场景。本文将以经典游戏《植物大战僵尸》中的"兵营生产植物战士"机制为例,使用 命令模式 实现一个简洁而可扩展的生产系统。


🌱 场景需求

  • 玩家点击界面上的"图片按钮",向兵营发出生产命令。
  • 兵营接收到命令后,每生产一个植物战士需冷却一段时间(如 200ms 或 500ms)。
  • 本关只需实现 一种植物战士(豌豆射手) 的生产逻辑。
  • 要求使用 命令模式 来解耦"界面按钮(发送者)"、"兵营(调用者)"和"植物生产(接收者)"。

🔧 命令模式核心角色

命令模式包含以下关键组件:

角色 说明
Command(命令接口) 声明执行操作的接口(如 Execute()
ConcreteCommand(具体命令) 实现 Execute(),持有对 Receiver 的引用
Invoker(调用者) 持有命令对象,负责触发命令执行(如兵营)
Receiver(接收者) 执行实际业务逻辑的对象(如 Peashooter
Client(客户端) 创建命令、接收者和调用者,并建立关联

注意:在本题中,由于 Peashooter 的构造函数直接输出日志,因此 具体命令类无需显式持有接收者引用,但结构上仍符合命令模式。


💻 代码实现解析

1. 抽象命令类 Commands.java

java 复制代码
public abstract class Commands {
    public abstract void Execute();
}

定义统一的执行接口。


2. 具体命令类 ProduceCommand.java

java 复制代码
public class ProduceCommand extends Commands {
    @Override
    public void Execute() {
        new Peashooter(); // 生产豌豆射手
    }
}
  • 每次执行 Execute() 就会创建一个 Peashooter 对象。
  • 构造函数中自动打印 "豌豆射手生产出来了"

3. 调用者类 CampInvokers.java

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class CampInvokers {
    private final List<Commands> commands = new ArrayList<>();
    private final long TrainTimer; // 冷却时间
    private long timer; // 上次生产完成的时间戳

    public CampInvokers(long trainTime) {
        TrainTimer = trainTime;
        timer = 0;
    }

    public void Train() {
        commands.add(new ProduceCommand()); // 添加生产命令到队列
    }

    public void ExecuteCommand() {
        if (commands.isEmpty()) return;
        // 检查是否过了冷却时间
        if (timer + TrainTimer <= System.currentTimeMillis()) {
            commands.get(0).Execute(); // 执行队首命令
            commands.remove(0);        // 移除已执行命令
            timer = System.currentTimeMillis(); // 更新冷却计时起点
        }
    }

    public void CancelTrainCommand() {
        if (!commands.isEmpty()) {
            commands.remove(commands.size() - 1);
            if (commands.isEmpty()) {
                timer = System.currentTimeMillis();
            }
        }
    }
}
  • 使用 命令队列 缓存玩家多次点击。
  • ExecuteCommand() 在冷却结束后才执行队列中的命令。
  • 完美模拟了"点击快但生产慢"的游戏体验。

4. 接收者:Peashooter.java

java 复制代码
public class Peashooter implements IBotany {
    public Peashooter() {
        System.out.println("豌豆射手生产出来了");
    }
}
  • 构造即代表"生产完成"。
  • 符合游戏设计中"实例化即部署"的常见逻辑。

5. 客户端 Client.java

java 复制代码
public class Client {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int number = scanner.nextInt();      // 点击次数
        long traintime = scanner.nextLong(); // 冷却时间(ms)

        long currtime = System.currentTimeMillis();
        CampInvokers camp = new CampInvokers(traintime);

        // 模拟玩家快速点击 N 次
        for (int i = 0; i < number; i++) {
            camp.Train();
        }

        // 在 1000ms 内持续尝试执行命令(受冷却限制)
        while (true) {
            camp.ExecuteCommand();
            if (currtime + 1000 < System.currentTimeMillis())
                break;
        }
    }
}
  • 玩家在瞬间发出 6 次命令。
  • 系统在 1 秒内尽可能多地执行(受限于冷却时间)。

🧪 测试示例

输入:6 200

  • 冷却 200ms → 1000ms 内最多生产 floor(1000/200) = 5
  • 输出 5 行 "豌豆射手生产出来了"

输入:6 500

  • 冷却 500ms → 最多生产 2 个
  • 输出 2 行

注意:首次生产从 timer=0 开始,所以第一次立即执行,之后每次间隔 traintime


✅ 命令模式的优势体现

  1. 解耦:界面按钮(发送者)无需知道兵营如何生产,只需发出命令。
  2. 可扩展 :未来可轻松添加 SunflowerCommandWallnutCommand 等。
  3. 支持队列与延迟执行 :命令可排队、取消(已有 CancelTrainCommand)。
  4. 易于测试与维护:每个组件职责单一。

📌 总结

通过命令模式,我们将"玩家点击"这一行为抽象为可存储、可调度的命令对象,使兵营的生产逻辑更加灵活、健壮。这种设计不仅适用于游戏开发,在 GUI 应用、事务处理、宏命令等场景中也极为常见。

相关推荐
stevenzqzq3 天前
Compose重组的概念1
命令模式·compose
老朱佩琪!3 天前
Unity命令模式
unity·游戏引擎·命令模式
阿拉斯攀登3 天前
设计模式:Spring MVC 中命令模式的核心映射与设计逻辑
spring·设计模式·mvc·命令模式
阿拉斯攀登6 天前
设计模式:命令模式
设计模式·命令模式
阿拉斯攀登6 天前
设计模式:命令模式(Spring MVC中的实践)
设计模式·springmvc·命令模式
syt_10137 天前
设计模式之-命令模式
设计模式·命令模式
Poetinthedusk9 天前
设计模式-命令模式
windows·设计模式·c#·wpf·命令模式
刺客xs10 天前
Qt------信号槽,属性,对象树
开发语言·qt·命令模式
我有一棵树10 天前
基于 Vue3 动态组件的弹框流程管理:命令模式事件
命令模式