Created
September 22, 2020 14:02
-
-
Save azaika/0166f727a527c0923a27eebf37b465b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Siv3D.hpp> | |
void Main() { | |
Graphics::SetTargetFrameRateHz(60); | |
Window::SetTitle(U"五月祭 TSGLIVE 5! day-1 ライブゲームプログラミング"); | |
Scene::SetBackground(Palette::Lightblue); | |
Window::Resize({ 500, 800 }); | |
constexpr double timeDelta = 1 / 60.0; | |
constexpr double mag = 1 / 20.0; | |
Camera2D camera(Vec2::Zero(), 1/mag, Camera2DParameters::NoControl()); | |
P2World world(9.8); | |
P2Body ceiling = world.createStaticRect(Vec2{ 0.0, 10.0 }, SizeF{ 25, 1.5 }); | |
Array<P2Body> wall = { | |
world.createStaticRect( | |
Vec2{-(Window::ClientWidth() - 1.5) / 2 * mag, 0.0 }, | |
SizeF{ 1.5, 100 } | |
), | |
world.createStaticRect( | |
Vec2{ (Window::ClientWidth() - 1.5) / 2.0 * mag, 0.0 }, | |
SizeF{ 1.5, 100 } | |
) | |
}; | |
const Polygon arrow({ | |
{ 0.0, 1.0 }, | |
{ 0.3, 0.7 }, | |
{ 0.1, 0.8 }, | |
{ 0.0, 0.0 }, | |
{-0.1, 0.8 }, | |
{-0.3, 0.7 }, | |
}); | |
double outer = 0.75; | |
Array<P2Body> playerBodies; | |
for (auto i : step(8)) | |
playerBodies << world.createCircle( | |
Circular(outer / Sin(Math::Pi / 8), i * Math::TwoPi / 8), | |
outer | |
); | |
Array<P2PivotJoint> playerPivots; | |
auto resetPivots = [&](Vec2 center) { | |
for (auto i : step(playerBodies.size())) | |
playerPivots << world.createPivotJoint( | |
playerBodies[i], | |
playerBodies[(i + 1) % playerBodies.size()], | |
center + Circular( | |
outer / Tan(Math::Pi / playerBodies.size()), | |
(i + 0.5) * Math::TwoPi / playerBodies.size() | |
) | |
).setLimits(-40.0_deg, 40.0_deg).setLimitEnabled(true); | |
}; | |
resetPivots(Vec2::Zero()); | |
Optional<Vec2> grabOrigin; | |
Font scoreFont(60); | |
Font highScoreFont(30); | |
double highScore = 0.0; | |
Optional<double> hitInterval = none; | |
Array<P2Body> objects; | |
std::unordered_set<int> objectIds; | |
bool isGameover = false; | |
while (System::Update()) { | |
Cursor::RequestStyle(CursorStyle::Hand); | |
camera.update(); | |
const Vec2 playerCenter = playerBodies.reduce( | |
[](auto&& acc, auto&& body) { | |
return acc + body.getPos(); | |
}, Vec2::Zero() | |
) / static_cast<double>(playerBodies.size()); | |
if (!hitInterval) { | |
for (auto&& [pair, col] : world.getCollisions()) { | |
if (objectIds.contains(pair.a) || objectIds.contains(pair.b)) { | |
playerPivots.clear(); | |
if (playerBodies.size() <= 3) { | |
for (auto&& [i, b] : IndexedRef(playerBodies)) | |
b.applyLinearImpulse(Circular(5.0, Random(Math::TwoPi))); | |
isGameover = true; | |
break; | |
} | |
playerBodies.pop_back(); | |
for (auto&& [i, b] : IndexedRef(playerBodies)) | |
b.setPos(playerCenter + Circular(outer / Sin(Math::Pi / playerBodies.size()), i * Math::TwoPi / playerBodies.size())); | |
resetPivots(playerCenter); | |
hitInterval = 0.0; | |
break; | |
} | |
} | |
} | |
else { | |
*hitInterval += 1.0; | |
if (*hitInterval >= 200) | |
hitInterval = none; | |
} | |
const Vec2 rawCursor = Cursor::PosRaw(); | |
const double gazeY = Min(0.0, playerCenter.y); | |
if (!isGameover) { | |
camera.setTargetCenter({ 0.0, gazeY }); | |
wall[0].setPos({ -11.75, gazeY }); | |
wall[1].setPos({ 11.75, gazeY }); | |
if (MouseL.down()) | |
grabOrigin = rawCursor; | |
if (grabOrigin && MouseL.up()) { | |
for (auto&& body : playerBodies) | |
body.applyLinearImpulse((*grabOrigin - rawCursor) / 5.0); | |
grabOrigin = none; | |
} | |
highScore = Max(highScore, -gazeY); | |
} | |
constexpr double interval = 10.0; | |
const Quad tekiQuad({ 1.5, 0.0 }, { 0.0, 1.5 }, { -1.5, 0.0 }, { 0.0, -1.5 }); | |
while (highScore + 5 * interval >= static_cast<double>(objects.size()) * interval) { | |
objects << world.createStaticQuad( | |
{ | |
Random(-9.0, 9.0), | |
-static_cast<double>(objects.size() + 1) * interval | |
}, | |
tekiQuad | |
); | |
objectIds.insert(objects.back().id()); | |
} | |
world.update(); | |
// 描画 | |
{ | |
const auto transformer = camera.createTransformer(); | |
ceiling.draw(); | |
wall.each([](auto&& w) { w.draw(); }); | |
objects.each([](auto&& o) { o.draw(); }); | |
playerBodies.each([&](auto&& b) { b.draw(hitInterval ? Palette::Blue.lerp(Palette::Red, *hitInterval / 250) : Palette::Red); }); | |
} | |
if (isGameover) { | |
Rect(Window::ClientSize()).draw(ColorF(Palette::Black, 0.5)); | |
scoreFont(U"ハイスコア").drawAt(Window::ClientCenter().moveBy(0, -30)); | |
scoreFont(U"{:.1f}m"_fmt(highScore)).drawAt(Window::ClientCenter().moveBy(0, 30)); | |
continue; | |
} | |
if (grabOrigin && MouseL.pressed()) { | |
Vec2 v = rawCursor - *grabOrigin; | |
auto _ = Transformer2D( | |
Mat3x2::Rotate(-v.getAngle({ 0.0, -1.0 })) | |
* Mat3x2::Scale(v.length()) | |
* Mat3x2::Translate(*grabOrigin) | |
); | |
arrow.draw(); | |
} | |
scoreFont(U"{:.1f}m"_fmt(Max(0.0, -gazeY))).drawAt({ Window::ClientWidth() / 2, 50 }, Palette::Black); | |
highScoreFont(U"ハイスコア: {:.1f}m"_fmt(highScore)).drawAt({ Window::ClientWidth() / 2, 100 }, Palette::Black); | |
} | |
playerPivots.clear(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment