OpenSiv3D

OpenSiv3D is a C++17/C++20 framework for creative coding.
Installation Guide & Documentation
- English: https://siv3d.github.io/
- 日本語: https://siv3d.github.io/ja-jp/
SDK Downloads
| Platform | Version | Date | Requirements |
|---|---|---|---|
| Windows | 0.4.3 | 11 April 2020 | - Windows 7 SP1 / 8.1 / 10 (64-bit) - Microsoft Visual C++ 2019 16.4 - Windows 10 SDK |
| macOS | 0.4.3 | 11 April 2020 | - macOS Mojave v10.14 or newer - Xcode 11.3 or newer - OpenGL 4.1 compatible graphics card |
| Linux | 0.4.3* | 11 April 2020 | - Tested compilers: Clang 8.0.1 / GCC 9.1.0 - OpenGL 4.1 compatible graphics card |
| iOS | TBA |
* Some functionality may be missing or limited
Examples
Hello, Siv3D!
# include <Siv3D.hpp>
void Main()
{
// Set background color to sky blue
Scene::SetBackground(ColorF(0.8, 0.9, 1.0));
// Create a new font
const Font font(60);
// Create a new texture that contains a cat emoji
const Texture cat(Emoji(U"�?�"));
// Coordinates of the cat
Vec2 catPos(640, 450);
while (System::Update())
{
// Put a message in the middle of the screen
font(U"Hello, Siv3D!�?�").drawAt(Scene::Center(), Palette::Black);
// Display the texture with animated size
cat.resized(100 + Periodic::Sine0_1(1s) * 20).drawAt(catPos);
// Draw a translucent red circle that follows the mouse cursor
Circle(Cursor::Pos(), 40).draw(ColorF(1, 0, 0, 0.5));
// When [A] key is down
if (KeyA.down())
{
// Print `Hello!`
Print << U"Hello!";
}
// When [Move the cat] button is pushed
if (SimpleGUI::Button(U"Move the cat", Vec2(600, 20)))
{
// Move the cat's coordinates to a random position in the screen
catPos = RandomVec2(Scene::Rect());
}
}
}Breakout
# include <Siv3D.hpp>
void Main()
{
// ブãƒãƒƒã‚¯ï¿½?�サイズ
constexpr Size blockSize(40, 20);
// ブãƒãƒƒã‚¯ï¿½?��?列
Array<Rect> blocks;
// 横 (Scene::Width() / blockSize.x) 個�?縦 5 個�?�ブãƒãƒƒã‚¯ã‚’�?列�?ï¿½è¿½åŠ ï¿½?�る
for (auto p : step(Size((Scene::Width() / blockSize.x), 5)))
{
blocks << Rect(p.x * blockSize.x, 60 + p.y * blockSize.y, blockSize);
}
// ボール�?�速�?�
constexpr double speed = 480.0;
// ボール�?�速度
Vec2 ballVelocity(0, -speed);
// ボール
Circle ball(400, 400, 8);
while (System::Update())
{
// パドル
const Rect paddle(Arg::center(Cursor::Pos().x, 500), 60, 10);
// ボールを移動
ball.moveBy(ballVelocity * Scene::DeltaTime());
// ブãƒãƒƒã‚¯ã‚’é †ï¿½?��?ェック
for (auto it = blocks.begin(); it != blocks.end(); ++it)
{
// ボール�?�ブãƒãƒƒã‚¯ï¿½?�交差�?��?��?��?�ら
if (it->intersects(ball))
{
// ボール�?��?��??を�??転�?�る
(it->bottom().intersects(ball) || it->top().intersects(ball) ? ballVelocity.y : ballVelocity.x) *= -1;
// ブãƒãƒƒã‚¯ã‚’�?列�?�ら削除(イテレータ�?�無効�?��?�る�?��?�注�?)
blocks.erase(it);
// �?�れ以上�?ェック�?��?��?�
break;
}
}
// 天井�?��?��?��?��?��?�ら�?��?�返る
if (ball.y < 0 && ballVelocity.y < 0)
{
ballVelocity.y *= -1;
}
// 左�?��?��?�?��?��?��?��?��?�ら�?��?�返る
if ((ball.x < 0 && ballVelocity.x < 0) || (Scene::Width() < ball.x && ballVelocity.x > 0))
{
ballVelocity.x *= -1;
}
// パドル�?��?��?��?��?�ら�?��?�返る
if (ballVelocity.y > 0 && paddle.intersects(ball))
{
// パドル�?�ä¸å¿ƒï¿½?�ら�?��?離�?�応�?��?��?��?�返る�?��??を変�?�る
ballVelocity = Vec2((ball.x - paddle.center().x) * 10, -ballVelocity.y).setLength(speed);
}
// �?��?��?��?�ブãƒãƒƒã‚¯ã‚’�??画�?�る
for (const auto& block : blocks)
{
block.stretched(-1).draw(HSV(block.y - 40));
}
// ボールを�??�??
ball.draw();
// パドルを�??�??
paddle.draw();
}
}Kaleidoscope sketch
# include <Siv3D.hpp>
void Main()
{
// ã‚ャン�?ス�?�サイズ
constexpr Size canvasSize(600, 600);
// 分割数
constexpr int32 N = 12;
// 背景色
constexpr Color backgroundColor(20, 40, 60);
// ウィンドウをã‚ャン�?ス�?�サイズ�?�
Window::Resize(canvasSize);
// 書�??込�?�用�?�画�?
Image image(canvasSize, backgroundColor);
// 画�?を表示�?�る�?��?�?�動的テクス�?ャ
DynamicTexture texture(image);
while (System::Update())
{
if (MouseL.pressed())
{
// 画�?��?�ä¸å¿ƒï¿½?� (0, 0) �?��?�るよ�?��?�マウスカーソル�?�座標を移動
const Vec2 begin = (MouseL.down() ? Cursor::PosF() : Cursor::PreviousPosF()) - Scene::Center();
const Vec2 end = Cursor::PosF() - Scene::Center();
for (auto i : step(N))
{
// 円座標�?�変�?�
std::array<Circular, 2> cs = { begin, end };
for (auto& c : cs)
{
// 角度を�?�ら�?�
c.theta = IsEven(i) ? (-c.theta - 2_pi / N * (i - 1)) : (c.theta + 2_pi / N * i);
}
// �?�ら�?��?��?置をも�?��?��?画�?�?�線を書�??込む
Line(cs[0], cs[1]).moveBy(Scene::Center())
.paint(image, 2, HSV(Scene::Time() * 60.0, 0.5, 1.0));
}
// 書�??込ん�?�画�?�?�テクス�?ャを更新
texture.fillIfNotBusy(image);
}
else if (MouseR.down()) // �?�クリック�?�れ�?�ら
{
// 画�?を塗り�?��?��?�
image.fill(backgroundColor);
// 塗り�?��?��?��?�画�?�?�テクス�?ャを更新
texture.fill(image);
}
// テクス�?ャを�??�??
texture.draw();
}
}Piano
# include <Siv3D.hpp>
void Main()
{
// 白�?��?�大�??�?�
constexpr Size keySize(55, 400);
// 楽器�?�種類
constexpr GMInstrument instrument = GMInstrument::Piano1;
// ウインドウをリサイズ
Window::Resize(12 * keySize.x, keySize.y);
// �?�盤�?�数
constexpr int32 NumKeys = 20;
// 音を作�?
std::array<Audio, NumKeys> sounds;
for (auto i : step(NumKeys))
{
sounds[i] = Audio(Wave(instrument, static_cast<uint8>(PianoKey::A3 + i), 0.5s));
}
// 対応�?�るã‚ー
constexpr std::array<Key, NumKeys> keys =
{
KeyTab, Key1, KeyQ,
KeyW, Key3, KeyE, Key4, KeyR, KeyT, Key6, KeyY, Key7, KeyU, Key8, KeyI,
KeyO, Key0, KeyP, KeyMinus, KeyGraveAccent,
};
// �??画�?置計算用�?�オフセット値
constexpr std::array<int32, NumKeys> keyPositions =
{
0, 1, 2, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22
};
while (System::Update())
{
// ã‚ー�?�押�?�れ�?�ら対応�?�る音を�?生
for (auto i : step(NumKeys))
{
if (keys[i].down())
{
sounds[i].playOneShot(0.5);
}
}
// 白�?�を�??画
for (auto i : step(NumKeys))
{
// オフセット値�?��?�数
if (IsEven(keyPositions[i]))
{
RectF(keyPositions[i] / 2 * keySize.x, 0, keySize.x, keySize.y)
.stretched(-1).draw(keys[i].pressed() ? Palette::Pink : Palette::White);
}
}
// 黒�?�を�??画
for (auto i : step(NumKeys))
{
// オフセット値�?�奇数
if (IsOdd(keyPositions[i]))
{
RectF(keySize.x * 0.68 + keyPositions[i] / 2 * keySize.x, 0, keySize.x * 0.58, keySize.y * 0.62)
.draw(keys[i].pressed() ? Palette::Pink : Color(24));
}
}
}
}Pinball
# include <Siv3D.hpp>
// 外周�?ï¿½æž ï¿½?ï¿½é ‚ç‚¹ãƒªã‚¹ãƒˆã‚’ä½œï¿½?
Array<Vec2> CreateFrame(const Vec2& leftAnchor, const Vec2& rightAnchor)
{
Array<Vec2> points = { leftAnchor, Vec2(-7, -2) };
for (auto i : Range(-30, 30))
{
points << OffsetCircular(Vec2(0.0, -12.0), 7, i * 3_deg);
}
return points << Vec2(7, -2) << rightAnchor;
}
// 接触�?��?��?�る�?��?�応�?��?�色を決定
ColorF GetColor(const P2Body& body, const Array<P2BodyID>& list)
{
return list.includes(body.id()) ? Palette::White : Palette::Orange;
}
void Main()
{
// フレームレートを 60 �?�固定
Graphics::SetTargetFrameRateHz(60);
// フレームレート�?��?å˜ï¿½?��?��?��?物�?�シミュレーション�?�更新
constexpr double timeDelta = 1.0 / 60.0;
// 背景色をè¨å®š
Scene::SetBackground(ColorF(0.2, 0.3, 0.4));
// 物�?�演算用�?�ワールド
P2World world(6.0);
// 左�?�フリッパー�?�軸�?�座標
constexpr Vec2 leftFlipperAnchor(-2.5, 1), rightFlipperAnchor(2.5, 1);
// 固定�?�æž
Array<P2Body> frames;
// 外周
frames << world.createStaticLineString(Vec2(0, 0), LineString(CreateFrame(leftFlipperAnchor, rightFlipperAnchor)));
// 左上�?� (
frames << world.createStaticLineString(Vec2(0, 0), LineString(Range(-25, -10).map([=](int32 i) { return OffsetCircular(Vec2(0.0, -12.0), 5.5, i * 3_deg).toVec2(); })));
// �?�上�?� )
frames << world.createStaticLineString(Vec2(0, 0), LineString(Range(10, 25).map([=](int32 i) { return OffsetCircular(Vec2(0.0, -12.0), 5.5, i * 3_deg).toVec2(); })));
// �?ンパー
Array<P2Body> bumpers;
// �? x3
bumpers << world.createStaticCircle(Vec2(0, -17), 0.5, P2Material(1.0, 1.0));
bumpers << world.createStaticCircle(Vec2(-2, -15), 0.5, P2Material(1.0, 1.0));
bumpers << world.createStaticCircle(Vec2(2, -15), 0.5, P2Material(1.0, 1.0));
// â–² x2
bumpers << world.createStaticTriangle(Vec2(0, 0), Triangle(-6, -5, -4, -1.5, -6, -3), P2Material(1.0, 0.8));
bumpers << world.createStaticTriangle(Vec2(0, 0), Triangle(6, -5, 6, -3, 4, -1.5), P2Material(1.0, 0.8));
// 左フリッパー
P2Body leftFlipper = world.createDynamicRect(leftFlipperAnchor, RectF(0.0, 0.04, 2.1, 0.45), P2Material(0.1, 0.0));
// 左フリッパー�?�ジョイント
const P2PivotJoint leftJoint = world.createPivotJoint(frames[0], leftFlipper, leftFlipperAnchor).setLimits(-20_deg, 25_deg).setLimitEnabled(true);
// �?�フリッパー
P2Body rightFlipper = world.createDynamicRect(rightFlipperAnchor, RectF(-2.1, 0.04, 2.1, 0.45), P2Material(0.1, 0.0));
// �?�フリッパー�?�ジョイント
const P2PivotJoint rightJoint = world.createPivotJoint(frames[0], rightFlipper, rightFlipperAnchor).setLimits(-25_deg, 20_deg).setLimitEnabled(true);
// スピナー +
const P2Body spinner = world.createDynamicRect(Vec2(-5.8, -12), SizeF(2.0, 0.1), P2Material(0.1, 0.0)).addRect(SizeF(0.1, 2.0), P2Material(0.01, 0.0));
// スピナー�?�ジョイント
P2PivotJoint spinnerJoint = world.createPivotJoint(frames[0], spinner, Vec2(-5.8, -12)).setMaxMotorTorque(0.05).setMotorSpeed(0).setMotorEnabled(true);
// 風車�?� |
frames << world.createStaticLine(Vec2(0, 0), Line(-4, -6, -4, -4));
// 風車�?�羽 �?
const P2Body windmillWing = world.createDynamicRect(Vec2(-4, -6), SizeF(3.0, 0.2), P2Material(0.1, 0.8));
// 風車�?�ジョイント
const P2PivotJoint windmillJoint = world.createPivotJoint(frames.back(), windmillWing, Vec2(-4, -6)).setMotorSpeed(240_deg).setMaxMotorTorque(10000.0).setMotorEnabled(true);
// 振り�?�?�軸
const P2Body pendulumbase = world.createStaticDummy(Vec2(0, -19));
// 振り�? �?
P2Body pendulum = world.createDynamicCircle(Vec2(0, -12), 0.4, P2Material(0.1, 1.0));
// 振り�?�?�ジョイント
const P2DistanceJoint pendulumJoint = world.createDistanceJoint(pendulumbase, Vec2(0, -19), pendulum, Vec2(0, -12), 7);
// エレベーター�?�上部 �?
const P2Body elevatorA = world.createStaticCircle(Vec2(4, -10), 0.3);
// エレベーター�?�床 �?
const P2Body elevatorB = world.createRect(Vec2(4, -10), SizeF(2.0, 0.2));
// エレベーター�?�ジョイント
P2SliderJoint elevatorSliderJoint = world.createSliderJoint(elevatorA, elevatorB, Vec2(4, -10), Vec2(0, 1)).setLimits(0.5, 5.0).setLimitEnabled(true).setMaxMotorForce(10000).setMotorSpeed(-10);
// ボール 〇
const P2Body ball = world.createDynamicCircle(Vec2(-4, -12), 0.4, P2Material(0.05, 0.0));
const P2BodyID ballID = ball.id();
// エレベーター�?�アニメーション用ストップウォッ�?
Stopwatch sliderStopwatch(true);
// 2D カメラ
const Camera2D camera(Vec2(0, -8), 24.0);
while (System::Update())
{
/////////////////////////////////////////
//
// æ›´æ–°
//
// 振り�?�?�抵抗
pendulum.applyForce(Vec2(pendulum.getVelocity().x < 0.0 ? 0.01 : -0.01, 0.0));
if (sliderStopwatch > 4s)
{
// エレベーター�?�巻�??上�?�を�?�æ¢
elevatorSliderJoint.setMotorEnabled(false);
sliderStopwatch.restart();
}
else if (sliderStopwatch > 2s)
{
// エレベーター�?�巻�??上�?�
elevatorSliderJoint.setMotorEnabled(true);
}
// 左フリッパー�?��?作
leftFlipper.applyTorque(KeyLeft.pressed() ? -80 : 40);
// �?�フリッパー�?��?作
rightFlipper.applyTorque(KeyRight.pressed() ? 80 : -40);
// 物�?�演算ワールド�?�更新
world.update(timeDelta, 24, 12);
// ボール�?�接触�?��?��?�るボディ�?� ID を�?�得
Array<P2BodyID> collidedIDs;
for (auto [pair, collision] : world.getCollisions())
{
if (pair.a == ballID)
{
collidedIDs << pair.b;
}
else if (pair.b == ballID)
{
collidedIDs << pair.a;
}
}
/////////////////////////////////////////
//
// �??画
//
// �??画用�?� Transformer2D
const auto transformer = camera.createTransformer();
// æž ï¿½?��??ç”»
for (const auto& frame : frames)
{
frame.draw(Palette::Skyblue);
}
// スピナー�?��??画
spinner.draw(GetColor(spinner, collidedIDs));
// �?ンパー�?��??画
for (const auto& bumper : bumpers)
{
bumper.draw(GetColor(bumper, collidedIDs));
}
// 風車�?��??画
windmillWing.draw(GetColor(windmillWing, collidedIDs));
// 振り�?�?��??画
pendulum.draw(GetColor(pendulum, collidedIDs));
// エレベーター�?��??画
elevatorA.draw(GetColor(elevatorA, collidedIDs));
elevatorB.draw(GetColor(elevatorB, collidedIDs));
// ボール�?��??画
ball.draw(Palette::White);
// フリッパー�?��??画
leftFlipper.draw(Palette::Orange);
rightFlipper.draw(Palette::Orange);
// ジョイント�?��?�視化
leftJoint.draw(Palette::Red);
rightJoint.draw(Palette::Red);
spinnerJoint.draw(Palette::Red);
windmillJoint.draw(Palette::Red);
pendulumJoint.draw(Palette::Red);
elevatorSliderJoint.draw(Palette::Red);
}
}Game of life
# include <Siv3D.hpp>
// 1 セル�?� 1 �?イト�?��?�るよ�?��?ビットフィールドを使用
struct Cell
{
bool previous : 1;
bool current : 1;
};
// フィールドをランダム�?�セル値�?�埋�?る関数
void RandomFill(Grid<Cell>& grid)
{
grid.fill({ 0,0 });
// 境界�?�セルを除�?��?�更新
for (auto y : Range(1, grid.height() - 2))
{
for (auto x : Range(1, grid.width() - 2))
{
grid[y][x] = { 0, RandomBool(0.5) };
}
}
}
// フィールド�?�状態を更新�?�る関数
void Update(Grid<Cell>& grid)
{
for (auto& cell : grid)
{
cell.previous = cell.current;
}
// 境界�?�セルを除�?��?�更新
for (auto y : Range(1, grid.height() - 2))
{
for (auto x : Range(1, grid.width() - 2))
{
const int32 c = grid[y][x].previous;
int32 n = 0;
n += grid[y - 1][x - 1].previous;
n += grid[y - 1][x].previous;
n += grid[y - 1][x + 1].previous;
n += grid[y][x - 1].previous;
n += grid[y][x + 1].previous;
n += grid[y + 1][x - 1].previous;
n += grid[y + 1][x].previous;
n += grid[y + 1][x + 1].previous;
// セル�?�状態�?�更新
grid[y][x].current = (c == 0 && n == 3) || (c == 1 && (n == 2 || n == 3));
}
}
}
// フィールド�?�状態を画�?化�?�る関数
void CopyToImage(const Grid<Cell>& grid, Image& image)
{
for (auto y : step(image.height()))
{
for (auto x : step(image.width()))
{
image[y][x] = grid[y + 1][x + 1].current
? Color(0, 255, 0) : Palette::Black;
}
}
}
void Main()
{
// フィールド�?�セル�?�数(横)
constexpr int32 width = 60;
// フィールド�?�セル�?�数(縦)
constexpr int32 height = 60;
// 計算を�?��?��?�境界部分も�?��?�?�サイズ�?�二次元�?列を確�?
Grid<Cell> grid(width + 2, height + 2, { 0,0 });
// フィールド�?�状態を�?�視化�?�る�?��?�?�画�?
Image image(width, height, Palette::Black);
// 動的テクス�?ャ
DynamicTexture texture(image);
Stopwatch s(true);
// 自動�?生
bool autoStep = false;
// æ›´æ–°é »åº¦
double speed = 0.5;
// グリッド�?�表示
bool showGrid = true;
// 画�?�?�更新�?�必�?�?��?�る�?�
bool updated = false;
while (System::Update())
{
// フィールドをランダム�?�値�?�埋�?るボタン
if (SimpleGUI::ButtonAt(U"Random", Vec2(700, 40), 170))
{
RandomFill(grid);
updated = true;
}
// フィールド�?�セルを�?��?��?�ゼãƒï¿½?��?�るボタン
if (SimpleGUI::ButtonAt(U"Clear", Vec2(700, 80), 170))
{
grid.fill({ 0, 0 });
updated = true;
}
// 一時�?ï¿½æ¢ / �?生ボタン
if (SimpleGUI::ButtonAt(autoStep ? U"Pause" : U"Run â–¶", Vec2(700, 160), 170))
{
autoStep = !autoStep;
}
// æ›´æ–°é »åº¦å¤‰æ›´ã‚¹ãƒ©ã‚¤ãƒ€ãƒ¼
SimpleGUI::SliderAt(U"Speed", speed, 1.0, 0.1, Vec2(700, 200), 70, 100);
// 1 ステップ進�?るボタン�?�?��?��?�更新タイミング�?�確�?
if (SimpleGUI::ButtonAt(U"Step", Vec2(700, 240), 170)
|| (autoStep && s.sF() >= (speed * speed)))
{
Update(grid);
updated = true;
s.restart();
}
// グリッド表示�?�有無を指定�?�る�?ェックボックス
SimpleGUI::CheckBoxAt(showGrid, U"Grid", Vec2(700, 320), 170);
// フィールド上�?��?�セル�?�編集
if (Rect(0, 0, 599).mouseOver())
{
const Point target = Cursor::Pos() / 10 + Point(1, 1);
if (MouseL.pressed())
{
grid[target].current = true;
updated = true;
}
else if (MouseR.pressed())
{
grid[target].current = false;
updated = true;
}
}
// 画�?�?�更新
if (updated)
{
CopyToImage(grid, image);
texture.fill(image);
updated = false;
}
// 画�?をフィルタ�?��?��?�拡大�?��?�表示
{
ScopedRenderStates2D sampler(SamplerState::ClampNearest);
texture.scaled(10).draw();
}
// グリッド�?�表示
if (showGrid)
{
for (auto i : step(61))
{
Rect(0, i * 10, 600, 1).draw(ColorF(0.4));
Rect(i * 10, 0, 1, 600).draw(ColorF(0.4));
}
}
if (Rect(0, 0, 599).mouseOver())
{
Cursor::RequestStyle(CursorStyle::Hidden);
Rect(Cursor::Pos() / 10 * 10, 10).draw(Palette::Orange);
}
}
}




