第 6 回 | テーマ「99 行でゲームを作る」 参加作品
はむくん
- Zキーで弾を発射
- 十字キーで移動
# include <Siv3D.hpp> | |
struct Object{ | |
Vec2 pos, vel; | |
double value; | |
int num, hp, type; | |
bool isDestroyed; | |
}; | |
void Main(){ | |
Graphics::SetBackground(Palette::Black); | |
const Font font(30); | |
Stopwatch stopWatch(true); | |
const String stageData = L"003333001313001111001212002222004000000005"; | |
std::array<Array<Object>,6> data; | |
Window::Resize(Size(480, 640)); | |
const Vec2 windowSize = Window::Size(); | |
Vec2 player{ windowSize.x / 2.0, windowSize.y * 0.9 }; | |
size_t currentStageIndex, remainingLives, currentScore, sceneIndex = 0; | |
const Array<Color> colors{ ColorF(0.5), ColorF(0.7), Palette::Skyblue, Palette::Blue, Palette::Red, Palette::White}; | |
const auto init = [&](){ | |
stopWatch.restart(); | |
remainingLives = 3; | |
currentStageIndex = currentScore = 0; | |
for (auto& objects : data) | |
objects.clear(); | |
sceneIndex = 1; | |
}; | |
const auto drawBullets = [&](const Array<Object>& objects, double radius){ | |
for (auto& object : objects){ | |
Circle(object.pos, radius).draw(colors[object.num * 2]); | |
Circle(object.pos, radius * 0.7).draw(colors[object.num * 2 + 1]); | |
} | |
}; | |
const auto drawEnemies = [&](const Array<Object>& objects){ | |
for (auto& object : objects) | |
Circle(object.pos, 20.0).drawFrame(3.0, 0.0, Palette::Red); | |
}; | |
const auto drawEffect = [&](const Array<Object>& objects){ | |
for (auto& object : objects) | |
RectF(100.0).setCenter(object.pos).draw(HSV(90.0, 0.5, Exp(- object.num * 0.1)).toColorF(Exp(-object.num * 0.1))); | |
}; | |
const auto shoot = [&](Object& object, size_t bulletNum, const Vec2& direction, double range, int shootInterval, int longPeriod, int shortPeriod, double speed, double value, int num) { | |
if (object.num % longPeriod <= shortPeriod && object.num % shootInterval == 0) | |
for (size_t i = 0; i < bulletNum; ++i) | |
data[0].push_back(Object{ object.pos, direction.rotated(-range + 2.0 * range / static_cast<double>(bulletNum) / 2.0 + 2.0 * range / static_cast<double>(bulletNum) * i) * speed, value, num, 5, 0, false }); | |
}; | |
const auto updateEnemy = [&](Object& object) { | |
++object.num; | |
shoot(object, 1, (player - object.pos).normalized(), Pi, 5, 60, 30, 5.0, 0.0, 2); | |
shoot(object, (object.type != 1) ? 5 : 21, Vec2::Down.rotated((Pi / 4.0) * Sin(object.num * 0.1)), (object.type != 1) ? Pi / 4.0 : Pi, (object.type != 1) ? 20 : 5, 120, 60, 5.0, 0.0, 1); | |
}; | |
const auto drawBackground = [&](const Array<Object>& objects){ | |
for (auto& object : objects) | |
RectF(object.vel.length() *100.0).setCenter((object.pos - Window::Center()) * 1.2 + Window::Center() - player * (0.1)).draw(HSV(200, 0.5, 0.1 * Min(object.vel.length() / 1.0, 1.0))); | |
}; | |
std::array<std::function<void(const Array<Object>&)>, 6> drawFunctions{ [&](const Array<Object>& objects) { drawBullets(objects, 8.0); }, drawEnemies, [&](const Array<Object>& objects) { drawBullets(objects, 15.0); }, drawEffect, [&](const Array<Object>& objects) {}, drawBackground }; | |
std::array<std::function<void(Object&)>, 6> updateFunctions{ [](Object&) {} , updateEnemy, [](Object&) {},[&](Object& object) {object.isDestroyed = (60 < ++object.num);}, [](Object&) {}, [](Object&) {} }; | |
Array<std::function<void()>> enemyFactories{ [&]() {data[1].push_back(Object{ Vec2(Random(0.0, windowSize.y), 0), Vec2::Down.rotated(Random(-Pi / 8.0, Pi / 8.0)) * Random(5.0, 20.0), 5.0, Random(0, 60), 5,0, false}); }, [&]() {data[1].push_back(Object{ Vec2(Random(0.0, windowSize.x), 0), Vec2::Down.rotated(Random(-Pi / 8.0, Pi / 8.0)) * 1.0, 0.0, Random(0, 60), 5,0, false}); }, [&]() {data[1].push_back(Object{ Vec2(windowSize.x * 0.5, 0), Vec2::Down * 10.0, 5.0, 0, 500,1, false }); } }; | |
const auto generateEnemies = [&](int enemyFactoriesIndex, int num = 0){ | |
for (int i = 0; i < num; ++i) | |
enemyFactories[enemyFactoriesIndex](); | |
}; | |
Array<std::function<void()>> enemyAppearancePattern{ []() {} , [&]() {generateEnemies(0, Random(1, 5)); }, [&]() {generateEnemies(0,Random(5,15)); },[&]() {generateEnemies(1,3); },[&]() {generateEnemies(2,1); },[&]() {sceneIndex = 3; } }; | |
const auto mainScene = [&](){ | |
if (stopWatch.elapsed().count() / 3000 != currentStageIndex) { | |
currentStageIndex = stopWatch.elapsed().count() / 3000; | |
enemyAppearancePattern[Parse<int>(Format(stageData[currentStageIndex]))](); | |
} | |
Vec2 dir(Vec2(Input::KeyRight.pressed - Input::KeyLeft.pressed, Input::KeyDown.pressed - Input::KeyUp.pressed)); | |
player += (!dir.isZero()) ? dir.setLength(Input::KeyZ.pressed ? 4.0 : 6.0) : Vec2::Zero; | |
player = Vec2(Clamp(player.x, 0.0, windowSize.x), Clamp(player.y, 0.0, windowSize.y)); | |
for (size_t i = 0; i < (Input::KeyZ.pressed) * 3; ++i) | |
data[2].push_back(Object{ player, (Vec2::Up * 10.0 + Vec2::Left + Vec2::Right * i).normalized() * 30.0, 0.0, 0, 5,0, false }); | |
if (System::FrameCount() % 10 == 0) | |
data[5].push_back(Object{ Vec2(Random(0.0, windowSize.x), 0.0), Vec2::Down * Random(0.0, 1.0), 0.0, 0, 5,0, false }); | |
for (size_t i = 0; i < data.size(); ++i) | |
for (size_t j = 0; j < data[i].size(); ++j) { | |
data[i][j].vel += -data[i][j].vel * data[i][j].value / 60.0; | |
data[i][j].pos += data[i][j].vel; | |
updateFunctions[i](data[i][j]); | |
data[i][j].isDestroyed |= !Window::ClientRect().intersects(data[i][j].pos); | |
} | |
for (auto& bullet : data[2]) | |
for (auto& enemy : data[1]) | |
if ((enemy.pos - bullet.pos).length() < 20) { | |
bullet.isDestroyed = true; | |
if (--enemy.hp <= 0) { | |
enemy.isDestroyed = true; | |
currentScore += 100; | |
for (int i = 0; i < 20; ++i) | |
data[3].push_back(Object{ enemy.pos, RandomVec2(Circle(1.0)) * 30.0 + Vec2::Up * 10.0, 10.0, 0, 5,0, false }); | |
} | |
} | |
for (auto& enemyBullet : data[0]) | |
if ((enemyBullet.pos - player).length() < 5.0) { | |
for (int i = 0; i < 100; ++i) | |
data[3].push_back(Object{ player, RandomVec2(Circle(1.0)) * 50.0 + Vec2::Up * 10.0, 5.0, 0, 5,0, false }); | |
player = Vec2(windowSize.x / 2.0, windowSize.y * 0.9); | |
for (auto& bullet : data[0]) | |
bullet.isDestroyed = true; | |
sceneIndex = (--remainingLives <= 0) ? 2 : 1; | |
} | |
for (auto& objects : data) | |
Erase_if(objects, [&](const Object& object) {return object.isDestroyed; }); | |
for (size_t i = 0; i < data.size(); ++i) { | |
const int j = data.size() - i - 1; | |
drawFunctions[j](data[j]); | |
} | |
Circle(player, 10.0).draw(); | |
font(L"SCORE: ",currentScore, L" LIFE: ", remainingLives).draw(); | |
}; | |
const auto simpleScene = [&](const String& string, size_t currentScene, size_t nextScene){ | |
font(string).drawCenter(windowSize / 2.0); | |
sceneIndex = (Input::AnyKeyClicked()) ? nextScene : currentScene; | |
}; | |
Array<std::function<void()>> scenes{ [&]() {simpleScene(L"SIMPLE STG\n\nZ KEY: SHOOT\nARROW: MOVE", 0, 4); } ,mainScene,[&]() {simpleScene(Format(L"GAME OVER\nSCORE: ", currentScore), 2, 4); },[&]() {simpleScene(Format(L"GAME CLEAR!\nSCORE: ", currentScore), 3, 4); }, init }; | |
while (System::Update()) | |
scenes[sceneIndex](); | |
} |