Last active
August 29, 2023 03:34
-
-
Save voidproc/f712cd9487e973d3db6a8841aaaee4e0 to your computer and use it in GitHub Desktop.
時間を止める能力を使い "3秒以内" にターゲットを破壊するゲーム(試作)- OpenSiv3D Discord #game-idea のお題「1 ゲーム 3 秒」より
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> // OpenSiv3D v0.6.11 | |
// 【ルールと設定】 | |
// 主人公は、仮想空間に侵入してから 3 秒以内にターゲット(PC)をすべて破壊しなければならない。 | |
// 3 秒経過後、侵入者は防衛システムにより消去され、システムは初期状態にリセットされ、 | |
// 主人公の別個体が初めからミッションをやり直すことになる。 | |
// 主人公は「強い衝撃」を受けることにより *時間を少しだけ止められる* 能力があり、 | |
// 時間に制約がある本ミッションでは大いに役立つと思われるが、体力に限りがあるため無限にこの能力を使うことはできない。 | |
constexpr int StageCount = 3; | |
const std::array<String, StageCount> StageData = { | |
U"................" | |
U"................" | |
U"..I.....h....T.." | |
U"................" | |
U"......####......" | |
U"..P...####......" | |
U"......####......" | |
U"................" | |
U"..T....H........" | |
U"................" | |
U"................" | |
U"................" | |
, | |
U"................" | |
U"................" | |
U"..T..##..##..T.." | |
U".....##..##....." | |
U"..H..........h.." | |
U".##...V..v...##." | |
U"..H..........h.." | |
U".....##..##....." | |
U"..P..##..##..T.." | |
U"................" | |
U"................" | |
U"................" | |
, | |
U"................" | |
U"................" | |
U".#...#...#...#.." | |
U".#...#...#...#.." | |
U".#.T.#.T.#.T.#.." | |
U".#.V.#.v.#.V.#.." | |
U".#...#...#...#.." | |
U".#...#...#...#.." | |
U".......P........" | |
U".I.....I.....I.." | |
U"................" | |
, | |
}; | |
// ゲーム全体にわたって共有されるデータ | |
struct GameData | |
{ | |
int stage = 0; | |
VariableSpeedStopwatch clock{ StartImmediately::Yes }; | |
Array<Optional<double>> result; | |
}; | |
// App | |
using App = SceneManager<String, GameData>; | |
// タイトルシーン | |
class TitleScene : public App::Scene | |
{ | |
public: | |
TitleScene(const InitData& init) | |
: IScene{ init } | |
{ | |
} | |
void update() override | |
{ | |
if (KeyEnter.down() && not timeEnter_.isRunning()) | |
{ | |
timeEnter_.start(); | |
} | |
if (timeEnter_.isRunning() && timeEnter_.sF() > 1.0) | |
{ | |
changeScene(U"MainScene", 0ms); | |
} | |
} | |
void draw() const override | |
{ | |
FontAsset(U"title")(U"MISSION IN 3SEC").drawAt(64, Scene::CenterF()); | |
const double alpha = timeEnter_.isRunning() ? Periodic::Pulse0_1(0.15s, 0.65) : 1.0; | |
FontAsset(U"title")(U"Press Enter Key").drawAt(32, Scene::CenterF().movedBy(0, 64), ColorF{ 1.0, alpha }); | |
} | |
private: | |
Stopwatch timeEnter_; | |
}; | |
// 壁 | |
class Wall | |
{ | |
public: | |
Wall(const RectF& rect) | |
: rect_{ rect } | |
{ | |
} | |
void draw() const | |
{ | |
rect_ | |
.rounded(8) | |
.draw(Palette::Gray) | |
.drawFrame(3.0, Palette::Silver); | |
} | |
const RectF collision() const | |
{ | |
return rect_; | |
} | |
private: | |
RectF rect_; | |
}; | |
enum class Direction | |
{ | |
Left, | |
Right, | |
Front, | |
Back, | |
}; | |
// プレイヤー | |
class Enemy; | |
class Target; | |
class Item; | |
class Player | |
{ | |
public: | |
Player(const Vec2& defaultPos) | |
: pos_{ defaultPos } | |
{ | |
} | |
void update(const Array<Wall>& walls) | |
{ | |
if (not alive_) return; | |
constexpr double MaxSpeed = 200.0; | |
const double DeltaSpeed = 500.0 * Scene::DeltaTime(); | |
if (KeyLeft.pressed()) | |
{ | |
dir_ = Direction::Left; | |
speed_.x -= DeltaSpeed; | |
} | |
else if (KeyRight.pressed()) | |
{ | |
dir_ = Direction::Right; | |
speed_.x += DeltaSpeed; | |
} | |
if (KeyUp.pressed()) | |
{ | |
speed_.y -= DeltaSpeed; | |
} | |
else if (KeyDown.pressed()) | |
{ | |
speed_.y += DeltaSpeed; | |
} | |
// 減速 | |
speed_.setLength(Max(speed_.length() - speed_.length() * 2 * Scene::DeltaTime(), 0.0)); | |
speed_.limitLengthSelf(MaxSpeed); | |
pos_ += speed_ * Scene::DeltaTime(); | |
for (const auto& wall : walls) | |
{ | |
if (collision().intersects(wall.collision())) | |
{ | |
pos_ -= speed_ * Scene::DeltaTime() * 2; | |
speed_ = -speed_; | |
break; | |
} | |
} | |
} | |
template <class Obj> | |
void onCollide(Obj& obj) | |
{ | |
if constexpr (std::is_same_v<Obj, Enemy>) | |
{ | |
// 無敵になる | |
timerInvincible_.restart(1.5s); | |
// 体力が減る | |
life_ = Clamp(life_ - 34.0, 0.0, MaxLife); | |
if (life_ <= 1e-3) | |
{ | |
kill(); | |
} | |
// スピードが0になる | |
speed_ = Vec2::Zero(); | |
} | |
else if constexpr (std::is_same_v<Obj, Target>) | |
{ | |
} | |
else if constexpr (std::is_same_v<Obj, Item>) | |
{ | |
// 体力が回復する | |
life_ = Clamp(life_ + 20.0, 0.0, MaxLife); | |
} | |
} | |
void draw() const | |
{ | |
if (timeKilled_.sF() < 1.0) | |
{ | |
const double alphaKilled = timeKilled_.isRunning() ? Periodic::Square0_1(0.1s) : 1.0; | |
const double alpha = timerInvincible_.isRunning() ? 0.4 + 0.5 * Periodic::Square0_1(0.1s) : 1; | |
// プレイヤーのダメージエフェクト | |
if (timerInvincible_.isRunning() && timerInvincible_.sF() > 1.2) | |
{ | |
Shape2D::NStar(16, 56, 36, pos_).draw(ColorF{ Palette::Whitesmoke, (0.8 * Periodic::Square0_1(0.1s)) }); | |
} | |
// ダメージ時画面赤フラッシュ | |
if (timerInvincible_.isRunning() && timerInvincible_.sF() > 0) | |
{ | |
const double t = (timerInvincible_.sF() - 0) / 1.5; | |
Scene::Rect().draw(ColorF{ Palette::Red, 0.5 * EaseInExpo(t) }); | |
} | |
TextureAsset(U"player") | |
.mirrored(dir_ == Direction::Right) | |
.resized(72) | |
.rotated(20_deg * Sin(Scene::Time() * 8.0)) | |
.drawAt(pos_, ColorF{ 1, alpha * alphaKilled }); | |
} | |
// 体力ゲージ | |
const auto gaugeRect = RectF{ Arg::center = Scene::Rect().bottomCenter().movedBy(0, -48), SizeF{ 600, 32 } }; | |
gaugeRect | |
.rounded(32) | |
.draw(Palette::Red); | |
if (life_ > 0.1) | |
gaugeRect | |
.rounded(32) | |
.setSize(600 * life_ / 100, 32) | |
.draw(Palette::White); | |
} | |
Circle collision() const | |
{ | |
return Circle{ pos_, 56 / 2 }; | |
} | |
template <class Obj> | |
bool isCollidedTo(const Obj& obj) const | |
{ | |
if (not alive_) return false; | |
if constexpr (std::is_same_v<Obj, Enemy>) | |
{ | |
if (timerInvincible_.isRunning()) return false; | |
} | |
return collision().intersects(obj.collision()); | |
} | |
const Vec2& pos() const | |
{ | |
return pos_; | |
} | |
const double life() const | |
{ | |
return life_; | |
} | |
void kill() | |
{ | |
alive_ = false; | |
timeKilled_.start(); | |
} | |
bool alive() const | |
{ | |
return alive_; | |
} | |
private: | |
// 位置 | |
Vec2 pos_{}; | |
// 向き | |
Direction dir_ = Direction::Right; | |
// 速度 | |
Vec2 speed_{}; | |
// 無敵 | |
Timer timerInvincible_{}; | |
// 生きてるか(衝突判定をするか) | |
bool alive_ = true; | |
// 破壊された | |
Stopwatch timeKilled_; | |
// 体力 | |
inline static constexpr double MaxLife = 100.0; | |
double life_ = MaxLife; | |
}; | |
// 敵 | |
enum class EnemyType | |
{ | |
BeeH, | |
BeeH2, | |
BeeV, | |
BeeV2, | |
}; | |
class Enemy | |
{ | |
public: | |
Enemy(const GameData& gameData, EnemyType type, const Vec2& defaultPos) | |
: gameData_{ gameData }, type_{ type }, defaultPos_ { defaultPos }, pos_{ defaultPos } | |
{ | |
} | |
void update() | |
{ | |
Vec2 motion{}; | |
switch (type_) | |
{ | |
case EnemyType::BeeH: | |
motion = Vec2{ 100.0, 0.0 }; | |
break; | |
case EnemyType::BeeH2: | |
motion = Vec2{ -100.0, 0.0 }; | |
break; | |
case EnemyType::BeeV: | |
motion = Vec2{ 0.0, 85.0 }; | |
break; | |
case EnemyType::BeeV2: | |
motion = Vec2{ 0.0, -85.0 }; | |
break; | |
} | |
const auto pos = defaultPos_ + motion * Periodic::Sine1_1(2.0s, gameData_.clock.sF()); | |
speed_ = pos - pos_; | |
pos_ = pos; | |
} | |
void draw() const | |
{ | |
TextureAsset(U"enemy") | |
.mirrored(speed_.x > 0) | |
.resized(64) | |
.drawAt(pos_ + Vec2{ 0, 16.0 } * Periodic::Sine1_1(0.7s, gameData_.clock.sF())); | |
} | |
Circle collision() const | |
{ | |
return Circle{ pos_, 56 / 2 }; | |
} | |
private: | |
const GameData& gameData_; | |
// 種類 | |
EnemyType type_; | |
// 位置 | |
Vec2 defaultPos_{}; | |
Vec2 pos_{}; | |
// 速度 | |
Vec2 speed_{}; | |
}; | |
// 目標物 | |
class Target | |
{ | |
public: | |
Target(const Vec2& defaultPos) | |
: pos_{ defaultPos } | |
{ | |
} | |
void update() | |
{ | |
} | |
void draw() const | |
{ | |
if (timeKilled_.sF() > 1.0) return; | |
const double alpha = timeKilled_.isRunning() ? Periodic::Square0_1(0.1s) : 1.0; | |
TextureAsset(U"target") | |
.resized(64) | |
.drawAt(pos_, ColorF{ 1.0, alpha }); | |
} | |
Circle collision() const | |
{ | |
return Circle{ pos_, 56 / 2 }; | |
} | |
void kill() | |
{ | |
alive_ = false; | |
timeKilled_.start(); | |
} | |
bool alive() const | |
{ | |
return alive_; | |
} | |
private: | |
// 位置 | |
Vec2 pos_{}; | |
// 生きてるか(衝突判定をするか) | |
bool alive_ = true; | |
// 破壊された | |
Stopwatch timeKilled_; | |
}; | |
// アイテム | |
class Item | |
{ | |
public: | |
Item(const Vec2& defaultPos) | |
: defaultPos_{ defaultPos }, pos_{ defaultPos } | |
{ | |
} | |
void update() | |
{ | |
} | |
void draw() const | |
{ | |
if (timeKilled_.sF() > 1.0) return; | |
const double alpha = timeKilled_.isRunning() ? Periodic::Square0_1(0.1s) : 1.0; | |
TextureAsset(U"item") | |
.resized(64) | |
.drawAt(pos_ + Vec2{ 0.0, 4.0 } * Periodic::Sine1_1(1.1s), ColorF{ 1, alpha }); | |
} | |
Circle collision() const | |
{ | |
return Circle{ pos_, 56 / 2 }; | |
} | |
void kill() | |
{ | |
alive_ = false; | |
timeKilled_.start(); | |
} | |
bool alive() const | |
{ | |
return alive_; | |
} | |
private: | |
// 位置 | |
Vec2 defaultPos_{}; | |
Vec2 pos_{}; | |
// 生きてるか(衝突判定をするか) | |
bool alive_ = true; | |
// 破壊された | |
Stopwatch timeKilled_; | |
}; | |
// メインシーン | |
class MainScene : public App::Scene | |
{ | |
public: | |
MainScene(const InitData& init) | |
: IScene{ init } | |
{ | |
if (getData().stage >= StageData.size()) | |
{ | |
getData().stage = 0; | |
} | |
if (getData().stage == 0) | |
{ | |
getData().result.clear(); | |
for (int iStage : step(StageCount)) | |
{ | |
getData().result.push_back(none); | |
} | |
} | |
for (auto [index, c] : Indexed(StageData[getData().stage])) | |
{ | |
const Vec2 objPos{ (index % 16) * 50.0 + 25.0, (index / 16) * 50.0 + 25.0 }; | |
if (c == U'H') | |
{ | |
enemies_.emplace_back(getData(), EnemyType::BeeH, objPos); | |
} | |
else if (c == U'h') | |
{ | |
enemies_.emplace_back(getData(), EnemyType::BeeH2, objPos); | |
} | |
else if (c == U'V') | |
{ | |
enemies_.emplace_back(getData(), EnemyType::BeeV, objPos); | |
} | |
else if (c == U'v') | |
{ | |
enemies_.emplace_back(getData(), EnemyType::BeeV2, objPos); | |
} | |
else if (c == U'T') | |
{ | |
targets_.emplace_back(objPos); | |
} | |
else if (c == U'I') | |
{ | |
items_.emplace_back(objPos); | |
} | |
else if (c == U'#') | |
{ | |
walls_.emplace_back(RectF{ objPos - Vec2{ 25.0, 25.0 }, 50.0 }); | |
} | |
else if (c == U'P') | |
{ | |
player_ = Player{ objPos }; | |
} | |
} | |
} | |
void update() override | |
{ | |
// ゲーム開始のカウントダウン | |
if (not time_.isStarted() && timerCountdown_.reachedZero()) | |
{ | |
timerCountdown_.reset(); | |
time_.start(); | |
getData().clock.restart(); | |
} | |
// ゲーム中 | |
if (time_.isRunning()) | |
{ | |
// キャラクターの更新 | |
player_.update(walls_); | |
for (auto& enemy : enemies_) | |
{ | |
enemy.update(); | |
} | |
for (auto& target : targets_) | |
{ | |
target.update(); | |
} | |
for (auto& item : items_) | |
{ | |
item.update(); | |
} | |
// 衝突判定 | |
for (auto& enemy : enemies_) | |
{ | |
if (player_.isCollidedTo(enemy)) | |
{ | |
player_.onCollide(enemy); | |
// 画面シェイク | |
quakeAmount_.y = 20; | |
// 6秒間スローになる | |
timerSlow_.restart(6s); | |
break; | |
} | |
} | |
for (auto& target : targets_) | |
{ | |
if (player_.isCollidedTo(target) && target.alive()) | |
{ | |
player_.onCollide(target); | |
target.kill(); | |
// 画面シェイク | |
quakeAmount_.y = 10; | |
// 最後の 1 個を kill したらタイマーストップ | |
// 次のステージへ(遷移するためのタイマーをセット) | |
if (targets_.count_if([](const auto& t) { return t.alive(); }) == 0) | |
{ | |
time_.pause(); | |
timerSlow_.reset(); | |
timeAllTargetDestroyed_.start(); | |
getData().clock.setSpeed(1); | |
} | |
} | |
} | |
for (auto& item : items_) | |
{ | |
if (player_.isCollidedTo(item) && item.alive()) | |
{ | |
player_.onCollide(item); | |
item.kill(); | |
} | |
} | |
// タイムアップ | |
if (timeRemain_() <= 1e-3) | |
{ | |
player_.kill(); | |
} | |
// プレイヤー生きてるか? | |
// しんでたらゲームオーバーへ | |
if (not player_.alive() && not timePlayerKilled_.isRunning()) | |
{ | |
timePlayerKilled_.start(); | |
timerSlow_.reset(); | |
getData().clock.setSpeed(1); | |
} | |
} | |
// スロー | |
if (timerSlow_.isRunning()) | |
{ | |
const double spd = EaseInExpo(timerSlow_.progress0_1()); | |
time_.setSpeed(spd); | |
getData().clock.setSpeed(spd); | |
} | |
// 画面シェイク | |
quakeAmount_.x -= 20.0 * Scene::DeltaTime() / 0.3; | |
quakeAmount_.x = Max(quakeAmount_.x, 0.0); | |
quakeAmount_.y -= 20.0 * Scene::DeltaTime() / 0.3; | |
quakeAmount_.y = Max(quakeAmount_.y, 0.0); | |
// ステージクリアしたのでキー入力待ち | |
if (timeAllTargetDestroyed_.sF() > 1.5) | |
{ | |
if (KeyEnter.down()) | |
{ | |
// 次のステージへ | |
getData().result[getData().stage] = timeRemain_(); | |
getData().stage += 1; | |
if (getData().stage >= StageCount) | |
changeScene(U"ResultScene", 0ms); | |
else | |
changeScene(U"MainScene", 0ms); | |
} | |
} | |
// ゲームオーバー | |
// 入力待ち | |
if (timePlayerKilled_.sF() > 3.0) | |
{ | |
if (KeyEnter.down()) | |
{ | |
getData().stage = 0; | |
changeScene(U"ResultScene", 0ms); | |
} | |
} | |
} | |
void draw() const override | |
{ | |
// 画面シェイク | |
const Transformer2D transformerQuake(Mat3x2::Translate(quakeAmount_.x * Periodic::Sine1_1(0.1s), quakeAmount_.y * Periodic::Sine1_1(0.07s))); | |
// 背景 | |
for (int iy : step(600 / 50 + 4)) | |
{ | |
for (int ix : step(800 / 50 + 4)) | |
{ | |
RectF{ (ix - 2) * 50, (iy - 2) * 50, 50 }.draw(ColorF{ 0.18, 0.18, 0.39 }).drawFrame(1.0, 0, ColorF{ Palette::Slateblue, 0.5 }); | |
} | |
} | |
// プレイヤー | |
player_.draw(); | |
// 壁 | |
for (const auto& wall : walls_) | |
{ | |
wall.draw(); | |
} | |
// 敵 | |
for (const auto& enemy : enemies_) | |
{ | |
enemy.draw(); | |
} | |
// ターゲット | |
for (const auto& target : targets_) | |
{ | |
target.draw(); | |
} | |
// アイテム | |
for (const auto& item : items_) | |
{ | |
item.draw(); | |
} | |
// ゲーム開始のカウントダウン表示 | |
if (timerCountdown_.isRunning()) | |
{ | |
Scene::Rect().draw(ColorF{ 0, 0.8 }); | |
const auto countdownText = timerCountdown_.s() < 1 ? U"GO!" : U"Ready"; | |
FontAsset(U"countdown")(countdownText).drawAt(168, Scene::CenterF(), Palette::White); | |
} | |
// スロー | |
if (timerSlow_.isRunning()) | |
{ | |
const double r = 1000 * EaseOutExpo(timerSlow_.progress1_0()); | |
const double alpha = timerSlow_.progress1_0(); | |
Circle{ player_.pos(), r } | |
.draw(ColorF{ Palette::Lime, 0.1 + 0.2 * EaseInSine(alpha) }); | |
} | |
// 残り時間 | |
if (time_.isStarted()) | |
{ | |
const ColorF clearTimeColor = ColorF{ Palette::Yellow, 0.7 + 0.3 * Periodic::Pulse0_1(0.3s, 0.8) }; | |
const ColorF timeRemainColor = timerSlow_.isRunning() ? Palette::Lime.lerp(Palette::White, timerSlow_.progress0_1()) : Palette::White; | |
const double alpha = timerSlow_.isRunning() ? 0.3 + 0.7 * Periodic::Square0_1(0.2s) : 1.0; | |
const bool cleared = time_.isPaused(); | |
FontAsset(U"time_remain")(U"{}{:.2f}{}"_fmt(cleared ? U"Clear! " : U"", timeRemain_(), cleared ? U"s" : U"")) | |
.drawAt(80, Scene::Rect().topCenter().movedBy(0, 56), cleared ? clearTimeColor : ColorF{ timeRemainColor, alpha }); | |
} | |
// ステージ名とか | |
const auto stageText = FontAsset(U"stage")(U"Stage {}"_fmt(getData().stage + 1)); | |
const auto stageTextRegion = stageText.region(24); | |
stageText.draw(24, Scene::Rect().br().movedBy(-stageTextRegion.size).movedBy(-8, 0), Palette::White); | |
// ステージクリアしたのでキー入力待ち | |
if (timeAllTargetDestroyed_.sF() > 1.5) | |
{ | |
Scene::Rect().draw(ColorF{ 0, 0.5 }); | |
FontAsset(U"title")(U"Press Enter Key").drawAt(Scene::CenterF(), ColorF{ 1, 0.7 + 0.3 * Periodic::Square0_1(0.3s)}); | |
} | |
// ゲームオーバー | |
// 入力待ち | |
if (timePlayerKilled_.sF() > 3.0) | |
{ | |
Scene::Rect().draw(ColorF{ Palette::Red, 0.5 }); | |
FontAsset(U"title")(U"GAME OVER").drawAt(96, Scene::CenterF(), ColorF{ 1 }); | |
} | |
} | |
private: | |
// メインシーンの経過時間 | |
VariableSpeedStopwatch time_; | |
// メインシーンの残り時間 | |
double timeRemain_() const | |
{ | |
return Max(0.0, 3.0 - time_.sF()); | |
} | |
// カウントダウン | |
Timer timerCountdown_{ 2s, StartImmediately::Yes }; | |
// プレイヤー | |
Player player_{ Vec2{ 200, 300 } }; | |
// 敵 | |
Array<Enemy> enemies_{}; | |
// ターゲット | |
Array<Target> targets_{}; | |
// アイテム | |
Array<Item> items_{}; | |
// 壁 | |
Array<Wall> walls_{}; | |
// 画面シェイク | |
Vec2 quakeAmount_{}; | |
// スロー | |
Timer timerSlow_; | |
// ステージクリアした | |
Stopwatch timeAllTargetDestroyed_; | |
// プレイヤーがしんだ | |
Stopwatch timePlayerKilled_; | |
}; | |
// リザルトシーン | |
class ResultScene : public App::Scene | |
{ | |
public: | |
ResultScene(const InitData& init) | |
: IScene{ init } | |
{ | |
} | |
void update() override | |
{ | |
if (timeEnter_.sF() > 2.0 && KeyEnter.down()) | |
{ | |
changeScene(U"MainScene", 0ms); | |
} | |
} | |
void draw() const override | |
{ | |
// 背景 | |
for (int iy : step(600 / 50 + 4)) | |
{ | |
for (int ix : step(800 / 50 + 4)) | |
{ | |
RectF{ (ix - 2) * 50, (iy - 2) * 50, 50 }.draw(ColorF{ 0.10, 0.10, 0.25 }).drawFrame(1.0, 0, ColorF{ Palette::Slateblue, 0.3 }); | |
} | |
} | |
constexpr double LineHeight = 72; | |
constexpr double h = LineHeight * StageCount; | |
for (int iStage : step(StageCount)) | |
{ | |
FontAsset(U"stage")(U"Stage {} : {}"_fmt(iStage + 1, getData().result[iStage].has_value() ? U"{:.2f} sec"_fmt(getData().result[iStage].value()) : U"No data")) | |
.drawAt(56, Scene::CenterF().movedBy(0, iStage * LineHeight - h / 2 + LineHeight / 2)); | |
} | |
} | |
private: | |
Stopwatch timeEnter_{ StartImmediately::Yes }; | |
}; | |
void Main() | |
{ | |
Scene::SetBackground(Palette::Mediumorchid); | |
// アセット | |
TextureAsset::Register(U"player", U"🏃♂️"_emoji); | |
TextureAsset::Register(U"enemy", U"🐝"_emoji); | |
TextureAsset::Register(U"target", U"🖥️"_emoji); | |
TextureAsset::Register(U"item", U"🍬"_emoji); | |
FontAsset::Register(U"title", FontMethod::MSDF, 48, Typeface::Black); | |
FontAsset::Register(U"countdown", FontMethod::MSDF, 48, Typeface::Black); | |
FontAsset::Register(U"time_remain", FontMethod::MSDF, 48, Typeface::Heavy, FontStyle::Italic); | |
FontAsset::Register(U"stage", FontMethod::MSDF, 48, Typeface::Heavy); | |
// シーン登録 | |
App app; | |
//app.add<TitleScene>(U"TitleScene"); | |
app.add<MainScene>(U"MainScene"); | |
app.add<ResultScene>(U"ResultScene"); | |
app.setFadeColor(Palette::Plum); | |
app.changeScene(U"MainScene", 0ms); | |
while (System::Update()) | |
{ | |
if (not app.update()) | |
{ | |
break; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
demo.mp4