コード:
#include <DxLib.h>
#include <vector>
#include <functional>
////
// std::function<bool ()> をタスクとし、戻り値は、タスクが継続中かどうかを示す真偽値である
// タスクは、falseを返した後でも、繰り返し呼び出し可能でなければならない
////
// タスクfをn回繰り返すタスク
class limited_loop_coroutine
{
public:
limited_loop_coroutine(int n, std::function<bool ()> f)
: m_count(0)
, m_limit(n)
, m_task(f)
{
if (n <= 0) {
throw std::invalid_argument("nは正の整数である必要があります");
}
if (!f) {
throw std::invalid_argument("タスクが空です");
}
}
// 繰り返し回数に達したら、falseを返す(カウントは0にリセットされる)
bool operator()()
{
if (!m_task()) {
m_count = (m_count + 1) % m_limit;
return m_count != 0;
}
return true;
}
private:
int m_count;
int m_limit;
std::function<bool ()> m_task;
};
// タスク列f[i]を順番に実行するタスク
class sequential_coroutine
{
public:
sequential_coroutine(std::vector<std::function<bool ()>> s)
: m_index(0)
, m_sequence(s)
{
if (s.empty()) {
throw std::invalid_argument("シーケンスが空です");
}
}
// f[i]をすべて実行したら、falseを返す(添字は0にリセットされる)
bool operator()()
{
if (!m_sequence[m_index]()) {
m_index = (m_index + 1) % m_sequence.size();
return m_index != 0;
}
return true;
}
private:
int m_index;
std::vector<std::function<bool ()>> m_sequence;
};
////
// タスクを作成するヘルパー関数
////
// タスクfをずっと繰り返すタスク
std::function<bool ()> loop(std::function<bool ()> f)
{
return [=]() -> bool {
f();
return true;
};
}
// タスクfをずっと繰り返すタスク(戻り値なしの関数からでも作れるように)
std::function<bool ()> loop(std::function<void ()> f)
{
return [=]() -> bool {
f();
return true;
};
}
// fを一回だけ実行するタスク
std::function<bool ()> one_time(std::function<void ()> f)
{
return [=]() -> bool {
f();
return false;
};
}
// タスクfをn回繰り返すタスク
std::function<bool ()> repeat(int n, std::function<bool ()> f)
{
return limited_loop_coroutine(n, f);
}
// タスクシーケンスを生成する
std::function<bool ()> sequence(std::vector<std::function<bool ()>> s)
{
return sequential_coroutine(s);
}
std::function<bool ()> sequence(std::function<bool ()> f1)
{
std::vector<std::function<bool ()>> s;
s.push_back(f1);
return sequence(s);
}
std::function<bool ()> sequence(std::function<bool ()> f1, std::function<bool ()> f2)
{
std::vector<std::function<bool ()>> s;
s.push_back(f1);
s.push_back(f2);
return sequence(s);
}
std::function<bool ()> sequence(std::function<bool ()> f1, std::function<bool ()> f2, std::function<bool ()> f3)
{
std::vector<std::function<bool ()>> s;
s.push_back(f1);
s.push_back(f2);
s.push_back(f3);
return sequence(s);
}
std::function<bool ()> sequence(std::function<bool ()> f1, std::function<bool ()> f2, std::function<bool ()> f3, std::function<bool ()> f4)
{
std::vector<std::function<bool ()>> s;
s.push_back(f1);
s.push_back(f2);
s.push_back(f3);
s.push_back(f4);
return sequence(s);
}
////
// 必要な数だけ、オーバーロードする
////
// テスト用クラス
class test_object
{
public:
test_object(int x, int y, int color)
: m_x(x)
, m_y(y)
, m_color(color)
{
}
void move(int vx, int vy)
{
m_x += vx;
m_y += vy;
}
void set_task(std::function<bool ()> task)
{
m_task = task;
}
void update()
{
// 設定されたタスクを実行する
// タスクがfalseを返したら、空のタスクに設定する
if (m_task && !m_task()) {
m_task = std::function<bool ()>();
}
}
void draw() const
{
DrawCircle(m_x, m_y, 20, m_color);
}
private:
int m_x;
int m_y;
int m_color;
std::function<bool ()> m_task;
};
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int)
{
ChangeWindowMode(TRUE);
if (DxLib_Init() != 0) {
return -1;
}
SetDrawScreen(DX_SCREEN_BACK);
test_object t[2] = {
test_object(200, 200, GetColor(255, 0, 0)),
test_object(300, 300, GetColor(0, 255, 255))
};
//「→5x20, ↓5x20, ←5x20, ↑5x20」を繰り返す
t[0].set_task(
loop(
sequence(
repeat(20, one_time([&]{t[0].move(5, 0);})),
repeat(20, one_time([&]{t[0].move(0, 5);})),
repeat(20, one_time([&]{t[0].move(-5, 0);})),
repeat(20, one_time([&]{t[0].move(0, -5);}))
)
)
);
// 左右に振動しつつ、徐々に左に移動
t[1].set_task(
loop(
sequence(
repeat(5, one_time([&]{t[1].move(10, 0);})),
repeat(5, one_time([&]{t[1].move(-11, 0);}))
)
)
);
while (ProcessMessage() == 0) {
if (CheckHitKey(KEY_INPUT_ESCAPE)) {
break;
}
ClearDrawScreen();
for (int i = 0; i < 2; ++i) {
t[i].update();
t[i].draw();
}
ScreenFlip();
}
DxLib_End();
return 0;
}