You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C++协程实现游戏敌人脚本:消除co_await重复编写

解决C++协程行为脚本中重复co_await的方案

不用宏的话,最优的实现思路是将行为命令(command_*())设计为协程promise类型的成员函数,让这些函数内部自动处理协程挂起逻辑,无需在脚本中显式编写co_await。核心是利用promise可以访问当前协程句柄的特性,在命令函数内部完成awaiter的创建、挂起判断和协程暂停操作。

具体实现步骤

1. 定义基础awaiter类型

先实现对应行为逻辑的awaiter,这里以等待时长、移动目标为例:

#include <coroutine>
#include <chrono>
#include <vector>
#include <cstdio>
#include <thread>

// 全局模拟游戏的协程恢复队列
inline std::vector<std::pair<std::chrono::steady_clock::time_point, std::coroutine_handle<>>> g_resume_queue;

// 游戏帧更新:处理到点需要恢复的协程
void game_update() {
    auto now = std::chrono::steady_clock::now();
    for (auto it = g_resume_queue.begin(); it != g_resume_queue.end();) {
        if (it->first <= now) {
            it->second.resume();
            it = g_resume_queue.erase(it);
        } else {
            ++it;
        }
    }
}

// 等待时长的awaiter
struct WaitAwaiter {
    float duration;
    std::chrono::steady_clock::time_point resume_time;

    bool await_ready() const {
        return duration <= 0.f;
    }

    void await_suspend(std::coroutine_handle<> handle) {
        g_resume_queue.emplace_back(resume_time, handle);
    }

    void await_resume() {}

    WaitAwaiter(float sec) : duration(sec), resume_time(std::chrono::steady_clock::now() + std::chrono::duration<float>(sec)) {}
};

// 移动目标的awaiter(简化为等待1秒模拟移动耗时)
struct MoveAwaiter {
    float target_x, target_y;

    bool await_ready() const { return false; }

    void await_suspend(std::coroutine_handle<> handle) {
        // 实际项目中这里会注册移动逻辑到游戏更新系统,完成后恢复协程
        g_resume_queue.emplace_back(std::chrono::steady_clock::now() + std::chrono::seconds(1), handle);
    }

    void await_resume() {
        printf("已移动到目标位置(%.1f, %.1f)\n", target_x, target_y);
    }

    MoveAwaiter(float x, float y) : target_x(x), target_y(y) {}
};

2. 设计协程返回类型与Promise

将行为命令作为Promise的成员函数,内部封装挂起逻辑:

// 协程返回类型:管理敌人行为脚本的生命周期
struct EnemyBehaviorScript {
    struct promise_type;
    using Handle = std::coroutine_handle<promise_type>;

    Handle coro_handle;

    EnemyBehaviorScript(Handle h) : coro_handle(h) {}
    ~EnemyBehaviorScript() { if (coro_handle) coro_handle.destroy(); }

    // 禁止拷贝,支持移动
    EnemyBehaviorScript(const EnemyBehaviorScript&) = delete;
    EnemyBehaviorScript& operator=(const EnemyBehaviorScript&) = delete;
    EnemyBehaviorScript(EnemyBehaviorScript&& other) : coro_handle(other.coro_handle) { other.coro_handle = nullptr; }
    EnemyBehaviorScript& operator=(EnemyBehaviorScript&& other) {
        if (this != &other) {
            if (coro_handle) coro_handle.destroy();
            coro_handle = other.coro_handle;
            other.coro_handle = nullptr;
        }
        return *this;
    }

    struct promise_type {
        // 获取协程返回对象
        EnemyBehaviorScript get_return_object() {
            return EnemyBehaviorScript(Handle::from_promise(*this));
        }

        // 初始不挂起,直接执行协程逻辑
        std::suspend_never initial_suspend() { return {}; }
        // 结束不挂起,直接销毁协程
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }

        // 获取当前promise引用,方便协程内部调用命令
        promise_type& self() { return *this; }

        // 行为命令:等待指定时间
        void command_wait(float seconds) {
            WaitAwaiter awaiter{seconds};
            if (!awaiter.await_ready()) {
                auto handle = Handle::from_promise(*this);
                awaiter.await_suspend(handle);
            }
        }

        // 行为命令:移动到目标位置
        void command_move_to(float x, float y) {
            MoveAwaiter awaiter{x, y};
            auto handle = Handle::from_promise(*this);
            awaiter.await_suspend(handle);
        }
    };
};

3. 编写简洁的行为脚本

在协程内部直接调用命令函数,无需co_await:

EnemyBehaviorScript enemy_ai() {
    // 获取当前promise的引用,用于调用命令
    auto& ai = EnemyBehaviorScript::Handle::from_promise(*this).promise();

    ai.command_move_to(10.0f, 0.0f);
    ai.command_wait(2.0f);
    ai.command_move_to(0.0f, 0.0f);
    ai.command_wait(1.0f);
    printf("敌人回到初始位置\n");
}

4. 模拟游戏运行

int main() {
    auto script = enemy_ai();

    // 模拟20帧游戏更新,每帧间隔0.5秒
    for (int i = 0; i < 20; ++i) {
        game_update();
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
    }

    return 0;
}

方案优势

  • 完全消除重复的co_await语句,脚本逻辑更简洁直观
  • 挂起逻辑封装在Promise内部,命令函数与协程状态解耦
  • 类型安全,相比宏定义更易维护、调试和扩展

内容的提问来源于stack exchange,提问作者nj16

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 00:23:10