Skip to content

Instantly share code, notes, and snippets.

@sthairno
Last active December 27, 2025 12:37
Show Gist options
  • Select an option

  • Save sthairno/764bc519672ffafbf71da290d62f19b6 to your computer and use it in GitHub Desktop.

Select an option

Save sthairno/764bc519672ffafbf71da290d62f19b6 to your computer and use it in GitHub Desktop.
Siv3D_MessageBusの活用例:マルチプレイヤー対応クッキークリッカー
#include <Siv3D.hpp>
#include <MessageBus/MessageBus.hpp>
// クリック地点に簡単な波紋を出すエフェクト
struct ClickEffect : IEffect
{
Vec2 m_pos;
double m_duration;
explicit ClickEffect(const Vec2& pos)
: m_pos{ pos }
, m_duration{ 0.5 } {
}
bool update(double t) override
{
const double scale = EaseOutCubic(t / m_duration) * 50;
const double alpha = 1.0 - EaseOutSine(t / m_duration);
Circle{ m_pos, scale }.draw(ColorF{ 1, alpha });
// 継続するなら true(Effect が保持する)
return (t < m_duration);
}
};
// IDから識別用の色を作る
ColorF ClientIdToColorF(int64 clientId)
{
return HSV{ clientId * 360 / Largest<int32>, 0.5, 1 };
}
void Main()
{
// このクライアントのランダムなID
const int clientId = RandomClosed(0, Largest<int32>);
// メッセージ送受信
MessageBus::MessageBus bus;
bus.subscribe(U"cursor:move");
bus.subscribe(U"cursor:click");
auto point = bus.variable(U"point", 0);
// 接続UI
bool initialized = false;
TextEditState hostInputState{ U"127.0.0.1" };
// 他クライアントのカーソル位置
HashTable<int, Point> otherCursorPositions;
// 最後に送ったカーソル座標
Point lastSentCursorPos{ -1000, -1000 };
// エフェクト管理
Effect effect;
// 他クライアント表示用のカーソル画像
Image cursorImage{ Icon{Icon::Type::MaterialDesign, 0xF01C0}, 48 };
Texture cursorTexture{ cursorImage };
// 色付きカーソル設定
Image myCursorImage{ cursorImage.size() };
cursorImage.stamp(myCursorImage, 0, 0, ClientIdToColorF(clientId));
Cursor::RegisterCustomCursorStyle(U"my_cursor", myCursorImage);
// クッキークリッカー
Font font{ 30 };
Texture cookieTexture{ U"🍪"_emoji };
while (System::Update())
{
// 事前に作成したカーソルを反映
Cursor::RequestStyle(U"my_cursor");
// MessageBusの更新処理
bus.update();
// 接続 UI の表示
if (not initialized)
{
SimpleGUI::TextBox(hostInputState, Vec2{ 10, 10 }, 180, unspecified);
if (SimpleGUI::Button(U"Connect", Vec2{ 195, 10 }, 100))
{
bus.connect(hostInputState.text, 6379);
initialized = true;
}
}
// 受信:届いたイベントを反映
for (const auto& event : bus.events())
{
if (event.channel == U"cursor:move")
{
const int id = event.value[U"id"].get<int>();
const int32 x = event.value[U"x"].get<int>();
const int32 y = event.value[U"y"].get<int>();
// 自分のイベントは無視
if (id == clientId)
{
continue;
}
Point pos{ x, y };
otherCursorPositions[id] = pos;
Print << U"イベント受信: cursor:move\nid={}, pos={}"_fmt(id, pos);
}
else if (event.channel == U"cursor:click")
{
const int id = event.value[U"id"].get<int>();
const int32 x = event.value[U"x"].get<int>();
const int32 y = event.value[U"y"].get<int>();
// 自分のイベントは無視
if (id == clientId)
{
continue;
}
Point pos{ x, y };
effect.add<ClickEffect>(pos);
Print << U"イベント受信: cursor:click\nid={}, pos={}"_fmt(id, pos);
}
}
// 送信:自分の操作を配信
const Point myCursorPos = Cursor::Pos();
// 送信頻度を下げるため、ウィンドウ内でカーソルが動いたときだけcursor:moveを送る
if (initialized &&
myCursorPos.distanceFrom(lastSentCursorPos) > 20 &&
Scene::Rect().contains(myCursorPos))
{
bus.emit(U"cursor:move", JSON{
{U"id", clientId},
{U"x", myCursorPos.x},
{U"y", myCursorPos.y}
});
Print << U"イベント送信: cursor:move\nid={}, pos={}"_fmt(clientId, myCursorPos);
lastSentCursorPos = myCursorPos;
}
// 左クリック
Circle cookieHitArea{ Scene::CenterF(), 60 };
if (initialized && MouseL.down())
{
// クリックのエフェクト表示
effect.add<ClickEffect>(myCursorPos);
// cursor:clickイベントを送る
bus.emit(U"cursor:click", JSON{
{U"id", clientId},
{U"x", myCursorPos.x},
{U"y", myCursorPos.y}
});
Print << U"イベント送信: cursor:click\nid={}, pos={}"_fmt(clientId, myCursorPos);
// クッキーの当たり判定
if (cookieHitArea.mouseOver())
{
point.set(point.get() + 1);
}
}
// 描画
// クッキー
cookieTexture.drawAt(Scene::CenterF());
// ポイント
font(U"{}"_fmt(point.get()))
.draw(Arg::bottomCenter = (Scene::CenterF() + Vec2{ 0, -60 }));
// クリックエフェクト
effect.update();
// 他クライアントのカーソル
for (const auto& [id, pos] : otherCursorPositions)
{
cursorTexture.draw(pos, ClientIdToColorF(id));
}
}
bus.shutdown();
}
@sthairno
Copy link
Author

Recording 📹

video.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment