Skip to content

Instantly share code, notes, and snippets.

@sthairno
Last active October 20, 2024 09:21
Show Gist options
  • Save sthairno/0719d46db2c5ee9af13841e0904cdd7a to your computer and use it in GitHub Desktop.
Save sthairno/0719d46db2c5ee9af13841e0904cdd7a to your computer and use it in GitHub Desktop.
OpenSiv3Dで某アクションゲームのメニュー画面を再現したデモ

OpenSiv3Dで某アクションゲームのメニュー画面を再現したデモ

スクリーンショット

image

実行方法

  1. READMEの「導入方法」に従いFlexLayoutをインストール
    https://github.com/sthairno/FlexLayout/blob/main/README.md
  2. Menu.xmlを.exeファイルがあるディレクトリ(App/ディレクトリ内)にコピー
  3. ビルドして実行 🏃
#include <Siv3D.hpp>
#include <FlexLayout.hpp>
struct Item
{
String emoji;
int level;
};
void Main()
{
constexpr double InspectorColorAlpha = 0.7;
Window::Resize(1280, 720);
// 画面表示用のアセット
const static Array<StringView> foodEmojis = {
U"🍇",U"🍈",U"🍉",U"🍊",U"🍋",U"🍌",U"🍍",U"🍎",U"🍏",U"🍐",U"🍑",U"🍒",U"🍓",U"🥝",U"🍅",U"🥥",U"🥑",U"🍆",U"🥔",U"🥕",U"🌽",U"🌶",U"🥒",U"🥦",U"🍄",U"🥜",U"🌰",U"🍞",U"🥐",U"🥖",U"🥨",U"🥞",U"🧀",U"🍖",U"🍗",U"🥩",U"🥓",U"🍔",U"🍟",U"🍕",U"🌭",U"🥪",U"🌮",U"🌯",U"🍳",U"🍲",U"🥣",U"🥗",U"🍿",U"🥫",U"🍱",U"🍘",U"🍙",U"🍚",U"🍛",U"🍜",U"🍝",U"🍠",U"🍢",U"🍣",U"🍤",U"🍥",U"🍡",U"🥟",U"🥠",U"🥡",U"🍦",U"🍧",U"🍨",U"🍩",U"🍪",U"🎂",U"🍰",U"🥧",U"🍫",U"🍬",U"🍭",U"🍮",U"🍯",U"🍼",U"🥛",U"☕",U"🍵",U"🍶",U"🍾",U"🍷",U"🍸",U"🍹",U"🍺"
};
const Texture backgroundSrc{ U"example/bay.jpg" };
const RenderTexture internalTexture{ backgroundSrc.size() };
const RenderTexture backgroundTexture{ backgroundSrc.size() };
Shader::GaussianBlur(backgroundSrc, internalTexture, backgroundTexture);
Texture playerTexture{ U"example/siv3d-kun.png" };
FontAsset::Register(U"default", FontMethod::MSDF, 20, Typeface::Regular);
FontAsset::Register(U"mono-emoji", FontMethod::Bitmap, 30, Typeface::MonochromeEmoji);
FontAsset::Register(U"color-emoji", FontMethod::Bitmap, 70, Typeface::ColorEmoji);
Array<Item> items =
foodEmojis.choice(RandomClosed(10, 25))
.map([](const StringView emoji) {
return Item{
.emoji = String{ emoji },
.level = RandomClosed(1, 29)
};
});
// テンプレート
const static auto CreateItemView = [](FlexLayout::Box& root, const Item& item) -> FlexLayout::Box {
auto view = root.getElementById(U"item-template")->cloneNode(true);
view.removeAttribute(U"id");
auto emojiLabel = *view.getElementById(U"emoji-label")->asLabel();
emojiLabel.setText(item.emoji);
auto levelLabel = *view.getElementById(U"level-label")->asLabel();
levelLabel.setText(Format(item.level));
return view;
};
// レイアウト
Optional<FlexLayout::Box> main;
Optional<FlexLayout::Box> leftPanelHeader;
Array<FlexLayout::Label> leftPanelHeaderLabels;
Optional<FlexLayout::Box> leftPanelContent;
Array<FlexLayout::Box> itemViews;
Optional<FlexLayout::Label> heartLabel;
Optional<FlexLayout::Box> playerView;
Optional<FlexLayout::Box> footer;
Array<FlexLayout::Label> footerLabels;
// ホットリロードや初回ロードなど、ファイルを読み込んだあとに行う処理
FlexLayout::Layout::OnLoadCallback onLoad = [&](FlexLayout::Layout&, FlexLayout::Box& root) {
// IDから要素を(再)取得
main = root.getElementById(U"main");
leftPanelHeader = root.getElementById(U"left-panel-header");
leftPanelHeaderLabels = leftPanelHeader
->getElementsByClassName(U"header-item")
.map([](const auto& box) { return *box.asLabel(); });
leftPanelContent = root.getElementById(U"left-panel-content");
heartLabel = root.getElementById(U"heart-label")->asLabel();
playerView = root.getElementById(U"player-view");
footer = root.getElementById(U"footer");
footerLabels = footer
->getElementsByClassName(U"footer-item")
.map([](const auto& box) { return *box.asLabel(); });
// アイテムのビューを生成
itemViews = items
.map([&](const Item& item) {
return CreateItemView(root, item);
});
leftPanelContent->replaceChildren(itemViews);
// テクスチャのサイズを反映
if (playerView)
{
playerView->setStyle(U"width", playerTexture.width());
playerView->setStyle(U"height", playerTexture.height());
}
};
FlexLayout::Layout layout{ U"Menu.xml", FlexLayout::EnableHotReload::Yes, onLoad };
while (System::Update())
{
// 40秒周期で体力が増えたり減ったりするSiv3Dくん
size_t heartCount = static_cast<size_t>(Math::Round(Periodic::Triangle0_1(40s) * 20));
heartLabel->setText(String(heartCount, U'❤'));
// レイアウトの更新
layout.update(Scene::Rect());
// ---背景---
// ぼかし入り背景画像
backgroundTexture.drawClipped({ 0, 0 }, *main->rect(), ColorF{ 0.5, 0.5, 0.5 });
// ---左側パネル---
// 角丸ヘッダー
leftPanelHeader->rect()
->rounded(10, 10, 0, 0)
.draw(Palette::Lemonchiffon);
// ヘッダーのアイコン
for (auto& label : leftPanelHeaderLabels)
{
// XMLでactive属性が指定されている場合は色を濃くする
label.draw(label.hasAttribute(U"active") ? ColorF{ 0.1 } : ColorF{ 0.6 });
}
// 半透明の黒背景
leftPanelContent->rect()
->draw(ColorF{ 0.0, 0.0, 0.0, 0.5 });
// アイテム
for (auto& itemView : itemViews)
{
// 背景
itemView.drawFrame(ColorF{ 0.4 });
itemView.paddingAreaRect()->draw(ColorF{ 0.2 });
// 絵文字
auto emojiLabel = *itemView.getElementById(U"emoji-label")->asLabel();
emojiLabel.draw();
// レベル
auto levelLabel = *itemView.getElementById(U"level-label")->asLabel();
levelLabel.drawFrame(ColorF{ 0.4 });
levelLabel.paddingAreaRect()->draw(ColorF{ 0.1 });
levelLabel.draw();
}
// ---右側パネル---
// ハート
heartLabel->draw();
// プレイヤー (Siv3Dくん)
playerTexture.draw(playerView->rect()->pos);
// ---フッター---
// 背景
footer->rect()->draw(Palette::Lemonchiffon);
// ラベル
for (auto& label : footerLabels)
{
label.draw(Palette::Black);
}
// ---デバッグ---
if (auto hoveredBox = FlexLayout::Debugger::GetHoveredBox(*layout.document()))
{
// カーソルを合わせているボックスのレイアウトを描画
FlexLayout::Debugger::DrawLayout(*hoveredBox);
// 左クリックしたらConsoleにツリーを出力
if (MouseL.down())
{
Console << U"--- Tree ---";
Console << FlexLayout::Debugger::DumpTree(*hoveredBox);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Layout>
<!-- siv3d-font="default"でFontAsset{ U"default" }を読み込む -->
<Box siv3d-font="default" style="flex-direction: column">
<Box id="main" style="flex-grow: 1; padding: 20px 26px 0; align-items: stretch;">
<Box id="left-panel" style="flex: 0 0 600px; margin: 0 40px; flex-direction: column;">
<Box id="left-panel-header" siv3d-font="mono-emoji" style="flex: 0 0 60px; align-items: center; justify-content: space-between; padding: 0 70px; font-size: 30px;">
<Label class="header-item" style="width: 40px; height: 40px;">👕</Label>
<Label class="header-item" style="width: 40px; height: 40px;">🏹</Label>
<Label class="header-item" style="width: 40px; height: 40px;">🛡️</Label>
<Label class="header-item" style="width: 40px; height: 40px;">⚔️</Label>
<Label class="header-item" style="width: 40px; height: 40px;" active="">🍎</Label>
<Label class="header-item" style="width: 40px; height: 40px;">🍲</Label>
<Label class="header-item" style="width: 40px; height: 40px;">🔵</Label>
<Label class="header-item" style="width: 40px; height: 40px;">⭐</Label>
<Label class="header-item" style="width: 40px; height: 40px;">⚙</Label>
</Box>
<Box id="left-panel-content" style="flex: 1 1; flex-direction: row; padding: 40px; gap: 16px; flex-wrap: wrap; align-content: start;">
</Box>
</Box>
<Box id="right-panel" style="flex: 1 1; flex-direction: column;">
<Box style="flex-direction: row;">
<Label id="heart-label" siv3d-font="color-emoji" style="font-size: 20px; max-width: 10.1lh; line-height: 1;">❤❤❤❤❤</Label>
</Box>
<Box style="flex: 1 1; justify-content: center; align-items: flex-end;">
<Box id="player-view" style="align-items: stretch;"/>
</Box>
</Box>
</Box>
<Box id="footer" style="flex: 0 0 40px; flex-direction: row; align-items: center; justify-content: end; gap: 50px; padding: 0 60px">
<Label class="footer-item">並べ替える</Label>
<Label class="footer-item">食べる</Label>
<Label class="footer-item">戻る</Label>
</Box>
<Box id="templates" style="display: none;">
<!--
テンプレートの使い方
1. IDセレクタでテンプレートの要素を取得
auto item = *root.getElementById("some-template")
2. cloneNodeでテンプレートの要素を複製
auto clone = *item.cloneNode(true)
3. 複製したノードのIDを削除
clone.removeAttribute(U"id")
4. 追加したい場所の要素にappendChild
auto target = *root.getElementById("target");
target.appendChild(clone)
参考:https://developer.mozilla.org/ja/docs/Web/API/Node/cloneNode
-->
<Box
id="item-template"
class="item"
style="width: 90px;
height: 90px;
border-width: 1;
justify-content: center;">
<Label
id="emoji-label"
siv3d-font="color-emoji"
style="font-size: 70px;">
🍎
</Label>
<Label
id="level-label"
style="position: absolute;
right: 0px;
bottom: 0px;
padding: 0.1em;
min-width: 2em;
border-top-width: 1px;
border-left-width: 1px;
text-align: center;
font-size: 18px;">
0
</Label>
</Box>
</Box>
</Box>
</Layout>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment