Skip to content

Instantly share code, notes, and snippets.

@choco798
Last active November 4, 2023 14:01
Show Gist options
  • Save choco798/68fb116d6e115a90c8585cd3cd3ce3e1 to your computer and use it in GitHub Desktop.
Save choco798/68fb116d6e115a90c8585cd3cd3ce3e1 to your computer and use it in GitHub Desktop.
Main.cpp
# include <Siv3D.hpp> // Siv3D v0.6.12
//シーン管理
enum scene { MOVIE, OPENING, GACHA, ZENTAIMAP, TALK_EVENT, MENU, BATTLE, GAMEOVER, EFFECT };
//ボタンの関数
bool Button(const Rect& rect, const Font& font02, const String& text, bool enabled)
{
if (enabled && rect.mouseOver())
{
Cursor::RequestStyle(CursorStyle::Hand);
}
if (enabled) {
rect.draw(ColorF{ Palette::Blue });
font02(text).drawAt(40, (rect.x + rect.w / 2), (rect.y + rect.h / 2));
}
else {
rect.draw(ColorF{ Palette::Gray });
font02(text).drawAt(40, (rect.x + rect.w / 2), (rect.y + rect.h / 2), ColorF{ 0.7 });
}
return (enabled && rect.leftClicked());
}
// Glyph とエフェクトの関数を組み合わせてテキストを描画
void DrawText(const Font& font, double fontSize, const String& text, const Vec2& pos, const ColorF& color, double t,
void f(const Vec2&, double, const Glyph&, const ColorF&, double), double characterPerSec)
{
const double scale = (fontSize / font.fontSize());
Vec2 penPos = pos;
const ScopedCustomShader2D shader{ Font::GetPixelShader(font.method()) };
for (auto&& [i, glyph] : Indexed(font.getGlyphs(text)))
{
if (glyph.codePoint == U'\n')
{
penPos.x = pos.x;
penPos.y += (font.height() * scale);
continue;
}
const double targetTime = (i * characterPerSec);
if (t < targetTime)
{
break;
}
f(penPos, scale, glyph, color, (t - targetTime));
penPos.x += (glyph.xAdvance * scale);
}
}
// 文字が上からゆっくり降ってくる表現
void TextEffect1(const Vec2& penPos, double scale, const Glyph& glyph, const ColorF& color, double t)
{
const double y = EaseInQuad(Saturate(1 - t / 0.3)) * -20.0;
const double a = Min(t / 0.3, 1.0);
glyph.texture.scaled(scale).draw(penPos + glyph.getOffset(scale) + Vec2{ 0, y }, ColorF{ color, a });
}
/// @brief ガチャを行うクラス
class GachaMachine
{
public:
GachaMachine() = default;
/// @brief ガチャを行うクラスを作成します。
/// @param items アイテムとその出現比率のペアの配列
/// @param cooldownTime ガチャのクールダウン時間(デフォルト: 0.5 秒)
explicit GachaMachine(const Array<std::pair<String, double>>& items, const Duration& cooldownTime = 0.5s)
: m_distribution{ items.map([](const auto& item) { return item.second; }) }
, m_probabilities{ m_distribution.probabilities() }
, m_itemNames{ items.map([](const auto& item) { return item.first; }) }
, m_cooldownTimer{ cooldownTime } {}
/// @brief ガチャのアイテム数を返します。
/// @return ガチャのアイテム数
[[nodiscard]]
size_t num_items() const
{
return m_probabilities.size();
}
/// @brief ガチャのアイテムの出現確率を返します。
/// @param index アイテムのインデックス
/// @return アイテムの出現確率
[[nodiscard]]
double getProbability(size_t index) const
{
return m_probabilities[index];
}
/// @brief ガチャのアイテムの出現確率の一覧を返します。
/// @return アイテムの出現確率の一覧
[[nodiscard]]
const Array<double>& getProbabilities() const
{
return m_probabilities;
}
/// @brief ガチャのアイテムの名前を返します。
/// @param index アイテムのインデックス
/// @return アイテムの名前
[[nodiscard]]
const String& getItemName(size_t index) const
{
return m_itemNames[index];
}
/// @brief ガチャのアイテムの名前の一覧を返します。
/// @return アイテムの名前の一覧
[[nodiscard]]
const Array<String>& getItemNames() const
{
return m_itemNames;
}
/// @brief ガチャを行い、結果を返します。
/// @return ガチャの結果
[[nodiscard]]
int32 getResult() const
{
m_cooldownTimer.restart();
return static_cast<int32>(m_distribution(GetDefaultRNG()));
}
/// @brief ガチャを複数回行い、結果を返します。
/// @param n ガチャを行う回数
/// @return ガチャの結果の配列
[[nodiscard]]
Array<int32> getResults(size_t n) const
{
return Array<int32>::Generate(n, [this]() { return getResult(); });
}
/// @brief ガチャがクールダウン中であるかを返します。
/// @return クールダウン中の場合 true, それ以外の場合は false
[[nodiscard]]
bool isCoolingDown() const
{
return m_cooldownTimer.isRunning();
}
/// @brief クールダウンの進捗を [0.0, 1.0] の範囲で返します。
/// @return クールダウンの進捗
[[nodiscard]]
double getCoolDownProgress() const
{
return m_cooldownTimer.progress0_1();
}
private:
mutable DiscreteDistribution m_distribution;
Array<double> m_probabilities;
Array<String> m_itemNames;
mutable Timer m_cooldownTimer;
};
struct SHUJINKO {
int level;
int smart_x;
int smart_y;
int battery;
bool item01;
bool item02;
}shujinko;
void Main()
{
// 背景の色を設定する | Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
//フォントの設定
Font font01{ FontMethod::MSDF, 48 };
const Font font02{ FontMethod::MSDF, 48, Typeface::Bold };
//BGMの設定
const Audio mo_op_aud{ Audio::Stream, U"example/sosweet010.mp3", Loop::Yes };
const Audio ze_te_aud{ Audio::Stream, U"example/sosweet002.mp3", Loop::Yes };
const Audio ba_01_aud{ Audio::Stream, U"example/sosweet009.mp3", Loop::Yes };
const Audio ga_01_aud{ Audio::Stream, U"example/sosweet005.mp3", Loop::Yes };
ba_01_aud.setVolume(0.5);
//ムービーの設定
const VideoTexture videoTexture{ U"example/video/movie.mp4" };
//INIT
//どの場面から始めるか
int scene_no = MOVIE;
//エフェクト時の次のシーンの記憶
int scene_mem = MOVIE;
//タイマー初期化
//timer:600カウントを数え続ける timer_mem:何かを終了したタイミングを記憶 timer_flag:タイマーのフラグ管理
int timer = 0;
int timer_mem = -1;
int timer_flag = 0;
//GACHA_INIT
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
// フォント
const Font font{ FontMethod::MSDF, 48, Typeface::Heavy };
// ガチャのアイテムと出現比率、クールダウン時間を設定する
const GachaMachine gachaMachine{ {
{ U"はずれ", 1000 },
{ U"ブロンズ", 100 },
{ U"シルバー", 10 },
{ U"ゴールド", 1 }},
0.4s };
// アイテムの確率を表示する
for (size_t i = 0; i < gachaMachine.num_items(); ++i)
{
//Print << U"「{}」の確率: {:.2f}%"_fmt(gachaMachine.getItemName(i), (gachaMachine.getProbability(i) * 100));
}
// ダイヤの数
int32 diamonds = 35;
// 直前のガチャの結果を格納する配列
Array<int32> lastResults;
//GAMEPLAY_INIT
//当初のゲームステージ
int game_stage = 1;
//フラグ初期化
//pi1fla:ピプペポ1を倒したか pi2fla:ピプペポ2を倒したか
int pi1fla = 0;
int pi2fla = 0;
//ピプペポ最大数
int pimax = 2;
//ピプペポボス
int piboss = 0;
// 画像ファイルから Image を作成
const Image pi_ima01{ U"example/pipupepo_black.png" };
const Image pi_ima02{ U"example/pipupepo_blue.png" };
const Image pi_ima03{ U"example/pipupepo_red.png" };
// Image から Texture を作成
const Texture pi_te_black{ pi_ima01 };
const Texture pi_te_blue{ pi_ima02 };
const Texture pi_te_red{ pi_ima03 };
const Texture pi_te_black_mini{ pi_ima01.scaled(0.5)};
//EFFECT_INIT
//エフェクトフラグ
bool eff_fla = false;
//const Font font{ FontMethod::MSDF, 48, Typeface::Bold };
const String text = U"Save the\n"
U"Un-Kind Ultra\n"
U"Universe";
Stopwatch stopwatch{ StartImmediately::Yes };
while (System::Update())
{
//オープニングムービー
if (scene_no == MOVIE)
{
// 背景の色を設定する | Set the background color
Scene::SetBackground(ColorF{ Palette::Brown });
//オープニング音楽が流れる
mo_op_aud.play();
//ムービーを流す
videoTexture.advance();
videoTexture
.scaled(1)
.drawAt(400, 300);
//左クリックしたらオープニング画面へ移動
if (MouseL.down() == true) {
videoTexture.reset();
scene_no = OPENING;
}
//ムービーのスキップは左クリックで
ClearPrint();
Print << U"ムービーのスキップは左クリックで";
}
//オープニング画面
if (scene_no == OPENING)
{
//オープニング音楽が流れる
mo_op_aud.play();
//文字消し
ClearPrint();
//オープニング画面読み込み
Scene::SetBackground(ColorF{ Palette::Purple });
font01(U"Save the Un-Kind Ultra Universe").draw(20, 10, Palette::White);
//ボタン表示
Button(Rect{ 280, 260,240, 80 }, font02, U"はじめから", false);
//ボタンが押されたらオープニング音楽停止
//ガチャボタンならエフェクト → ガチャの画面に移動
if (Button(Rect{ 280, 360,240, 80 }, font02, U"ガチャ", true)) {
mo_op_aud.stop(3s);
scene_mem = GACHA;
timer_flag = 1;
scene_no = EFFECT;
}
//体験版ボタンなら全体マップへ移動
if (Button(Rect{ 280, 460,240, 80 }, font02, U"体験版", true)) {
mo_op_aud.stop(3s);
scene_mem = BATTLE;
timer_flag = 1;
scene_no = EFFECT;
}
}
//ガチャ画面
if (scene_no == GACHA)
{
/*//ガチャ画面読み込み
Scene::SetBackground(ColorF{ Palette::White});
Print << U"ガチャ";*/
ga_01_aud.play();
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
if (SimpleGUI::Button(U"\U000F01C8 {}"_fmt(diamonds), Vec2{ 300, 40 }, 100, (not gachaMachine.isCoolingDown())))
{
// ダイヤを購入する処理など
diamonds += 100;
}
if (SimpleGUI::Button(U"1 回ガチャ \U000F01C8 × 1", Vec2{ 300, 80 }, 200, (lastResults.isEmpty() && (1 <= diamonds))))
{
--diamonds;
lastResults = gachaMachine.getResults(1); // 引いた瞬間に結果を取得する
}
if (SimpleGUI::Button(U"5 回ガチャ \U000F01C8 × 5", Vec2{ 520, 80 }, 200, (lastResults.isEmpty() && (5 <= diamonds))))
{
diamonds -= 5;
lastResults = gachaMachine.getResults(5); // 引いた瞬間に結果を取得する
}
if (Button(Rect{ 500, 500,240, 80 }, font02, U"戻る", true)) {
ga_01_aud.stop(3s);
scene_mem = OPENING;
timer_flag = 1;
scene_no = EFFECT;
}
// 各ガチャ結果について
for (int32 i = 0; i < static_cast<int32>(lastResults.size()); ++i)
{
const RectF rect{ 300, (160 + i * 50), (gachaMachine.getCoolDownProgress() * 300), 45 };
// そのアイテムの出現率を取得する
const double probability = gachaMachine.getProbability(lastResults[i]);
// 出現率に応じて背景色を変える
if (probability < 0.1) // 10% 未満
{
rect.draw(ColorF{ 1.0, 0.8, 0.6 });
}
else
{
rect.draw(ColorF{ 0.9 });
}
// クールダウン完了後に
if (not gachaMachine.isCoolingDown())
{
// アイテム名を表示する
font(gachaMachine.getItemName(lastResults[i])).drawAt(30, rect.center(), ColorF{ 0.11 });
}
}
if (lastResults && (not gachaMachine.isCoolingDown()))
{
if (SimpleGUI::Button(U"アイテムを受け取る", Vec2{ 300, 440 }, 300))
{
// ガチャ結果のアイテムを受け取る処理など
lastResults.clear();
shujinko.item01 = true;
}
}
}
//全体マップ画面
if (scene_no == ZENTAIMAP)
{
//全体マップ画面読み込み
Scene::SetBackground(ColorF{ Palette::Green });
Print << U"全体マップ";
//セリフウインドウ作成
//あらすじ表示
//画面移動させるタイミングを設定
//画面移動させるタイミングがきたら移動
}
//会話・イベント画面
if (scene_no == TALK_EVENT)
{
ClearPrint();
if (game_stage == 1) {
Print << U"ステージ1クリアおめでとう";
Print << U"次はステージ2だよ";
}
if (game_stage == 2) {
Print << U"ステージ2クリアおめでとう";
Print << U"次はボスステージだよ がんばろう";
}
if (game_stage == 3) {
Print << U"ゲームクリアおめでとう";
Print << U"右上のバッテンで終わりにしてね";
}
//ピプペポ1,2初期化
pi1fla = 0;
pi2fla = 0;
//画面移行させるタイミングを設定
if (timer_flag == 1) {
timer_mem = timer;
if (timer_mem > 300) {
timer_mem -= 300;
}
else {
timer_mem += 300;
}
timer_flag = 0;
}
//画面移行させるタイミングがきたら移行
if (timer == timer_mem) {
timer_mem = -1;
//次のステージへ
if (game_stage == 2){
game_stage = 3;
}
if (game_stage == 1) {
game_stage = 2;
}
scene_no = BATTLE;
}
//デバッグ用表示
Print << U"";
Print << U"デバッグ用表示";
Print << timer;
Print << timer_mem;
}
//バトル画面
if (scene_no == BATTLE)
{
// 背景の色を設定する | Set the background color
Scene::SetBackground(ColorF{ 0.6, 0.8, 0.7 });
//主人公ステータス設定
shujinko.smart_x = 80;
shujinko.smart_y = 140;
//ガチャアイテム
//shujinko.item01 = true;
if (shujinko.item01 == true) {
shujinko.smart_x = 96;
shujinko.smart_y = 168;
}
ClearPrint();
//ピプペポ残り表示
Print << U"ピプペポ残り";
if ((pimax - pi1fla - pi2fla) > 0) {
Print << (pimax - pi1fla - pi2fla);
}
else {
Print << U"0";
}
//ピプペポが0になったらお知らせ&タイマーフラグ設定
if ((pimax - pi1fla - pi2fla) == 0) {
if (game_stage == 1 || game_stage == 2) {
timer_flag = 1;
Print << U"みんな倒せたよ";
pimax = -1;
}
if (game_stage == 3) {
if (piboss == 1) {
timer_flag = 1;
pimax = -1;
}
}
}
//みんな倒せたよ
if (pimax == -1) {
font01(U"みんな倒せたよ").draw(200, 500, Palette::Black);
}
//画面移行させるタイミングを設定
if (timer_flag == 1) {
timer_mem = timer;
if (timer_mem > 300) {
timer_mem -= 300;
}
else {
timer_mem += 300;
}
timer_flag = 0;
}
//画面移行させるタイミングがきたら移行
if (timer == timer_mem) {
timer_mem = -1;
scene_no = TALK_EVENT;
pimax = 2;
timer_flag = 1;
}
//デバッグ用表示
Print << U"";
Print << U"デバッグ用表示";
Print << timer;
Print << timer_mem;
//ステージ1の設定
if (game_stage == 1) {
//BGMの開始
ba_01_aud.play();
//ピプペポ1の配置&挙動
if (Cursor::Pos().x > (540 - (shujinko.smart_x / 2)) && Cursor::Pos().y > (20 - (shujinko.smart_y / 2))) {
if (Cursor::Pos().x < (620 + (shujinko.smart_x / 2)) && Cursor::Pos().y < (100 + shujinko.smart_y / 2)) {
if (pi1fla == 0) {
pi_te_black.draw(540,20);
}
if (MouseL.down() == false && pi1fla == 0) {
ClearPrint();
Print << U"Rock ON!";
}
else {
ClearPrint();
Print << U"classhhhh!";
pi1fla = 1;
}
}
else {
}
}
else {
}
//ピプペポ2の配置&挙動
if (Cursor::Pos().x > (300 - (shujinko.smart_x / 2)) && Cursor::Pos().y > (500 - shujinko.smart_y / 2)) {
if (Cursor::Pos().x < (380 + (shujinko.smart_x / 2)) && Cursor::Pos().y < (580 + (shujinko.smart_y))) {
if (pi2fla == 0) {
pi_te_black.draw(300, 500);
}
if (MouseL.down() == false && pi2fla == 0) {
ClearPrint();
Print << U"Rock ON!";
}
else {
ClearPrint();
Print << U"classhhhh!";
pi2fla = 1;
}
}
else {
}
}
else {
}
}
//ステージ2の設定(ピプペポ移動&ピプペポ小さい)
if (game_stage == 2) {
//BGMの開始
ba_01_aud.play();
//ピプペポ1の配置&挙動
if (Cursor::Pos().x > (timer - (shujinko.smart_x / 2)) && Cursor::Pos().y > 0) {
if (Cursor::Pos().x < ((timer + 80) + (shujinko.smart_x / 2)) && Cursor::Pos().y < 100 + (shujinko.smart_y / 2)) {
if (pi1fla == 0) {
pi_te_black.draw(timer, 20);
}
if (MouseL.down() == false && pi1fla == 0) {
ClearPrint();
Print << U"Rock ON!";
}
else {
ClearPrint();
Print << U"classhhhh!";
pi1fla = 1;
}
}
else {
}
}
else {
}
//ピプペポ2の配置&挙動
if (Cursor::Pos().x > (300 - (shujinko.smart_x / 2)) && Cursor::Pos().y > (500 - (shujinko.smart_y / 2))) {
if (Cursor::Pos().x < (340 + (shujinko.smart_x / 2)) && Cursor::Pos().y < (540 + (shujinko.smart_y / 2)) ) {
if (pi2fla == 0) {
pi_te_black_mini.draw(300, 500);
}
if (MouseL.down() == false && pi2fla == 0) {
ClearPrint();
Print << U"Rock ON!";
}
else {
ClearPrint();
Print << U"classhhhh!";
pi2fla = 1;
}
}
else {
}
}
else {
}
}
//ステージ3の設定(ボス)
if (game_stage == 3) {
//BGMの開始
ba_01_aud.play();
//デバッグ用表示
Print << U"ボスだお";
Print << piboss;
//ピプペポ1の配置&挙動
if (Cursor::Pos().x > (40 - (shujinko.smart_x / 2)) && Cursor::Pos().y > (190 - (shujinko.smart_y / 2))) {
if (Cursor::Pos().x < (120 + (shujinko.smart_x / 2)) && Cursor::Pos().y < (270 + shujinko.smart_y / 2)) {
if (pi1fla == 0) {
pi_te_blue.draw(40, 190);
}
if (MouseR.down() == false && pi1fla == 0) {
ClearPrint();
Print << U"Rock ON!";
}
else {
ClearPrint();
Print << U"classhhhh!";
pi1fla = 1;
}
}
else {
}
}
else {
}
//ピプペポ2の配置&挙動
if (Cursor::Pos().x > (300 - (shujinko.smart_x / 2)) && Cursor::Pos().y > (timer - shujinko.smart_y / 2)) {
if (Cursor::Pos().x < (380 + (shujinko.smart_x / 2)) && Cursor::Pos().y < ((timer + 80) + (shujinko.smart_y))) {
if (pi2fla == 0) {
pi_te_red.draw(300, timer);
}
if (MouseR.down() == false && pi2fla == 0) {
ClearPrint();
Print << U"Rock ON!";
}
else {
ClearPrint();
Print << U"classhhhh!";
pi2fla = 1;
}
}
else {
}
}
else {
}
//ピプペポボスの挙動
if (Cursor::Pos().x > (500 - (shujinko.smart_x / 2)) && Cursor::Pos().y > (500 - shujinko.smart_y / 2)) {
if (Cursor::Pos().x < (580 + (shujinko.smart_x / 2)) && Cursor::Pos().y < (580 + (shujinko.smart_y))) {
if (piboss == 0) {
pi_te_black.draw(500, 500);
}
if (MouseL.down() == false && piboss == 0) {
ClearPrint();
Print << U"Rock ON!";
}
else {
ClearPrint();
Print << U"classhhhh!";
piboss = 1;
}
}
else {
}
}
else {
}
}
//スマホの画面を描く
Rect{ (Cursor::Pos().x - (shujinko.smart_x / 2)), (Cursor::Pos().y - (shujinko.smart_y / 2)), shujinko.smart_x, shujinko.smart_y }.draw(ColorF{ 1.0, 0.0, 0.0, 0.5 });
}
//エフェクト
if (scene_no == EFFECT)
{
Scene::SetBackground(ColorF{ Palette::Black });
if (eff_fla == false)
{
stopwatch.restart();
eff_fla = true;
}
const double t = stopwatch.sF();
DrawText(font, 40, text, Vec2{ 450, 350 }, Palette::Skyblue, t, TextEffect1, 0.1);
//画面移行させるタイミングを設定
if (timer_flag == 1) {
timer_mem = timer;
if (timer_mem > 300) {
timer_mem -= 300;
}
else {
timer_mem += 300;
}
timer_flag = 0;
}
//画面移行させるタイミングがきたら移行
if (timer == timer_mem) {
timer_mem = -1;
eff_fla = false;
scene_no = scene_mem;
}
}
//タイマー加算
++timer;
//599まできたら0に戻す
if (timer == 600) {
timer = 0;
}
}
}
//スマホから修正できるかtest
//
// - Debug ビルド: プログラムの最適化を減らす代わりに、エラーやクラッシュ時に詳細な情報を得られます。
//
// - Release ビルド: 最大限の最適化でビルドします。
//
// - [デバッグ] メニュー → [デバッグの開始] でプログラムを実行すると、[出力] ウィンドウに詳細なログが表示され、エラーの原因を探せます。
//
// - Visual Studio を更新した直後は、プログラムのリビルド([ビルド]メニュー → [ソリューションのリビルド])が必要な場合があります。
//
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment