Edited at

C++ で楽しく簡単にゲームやアプリを開発できるライブラリ「Siv3D」

More than 1 year has passed since last update.

ゲームエンジン・ライブラリ・ツールの開発 Advent Calendar 4 日目の記事です。


最新の C++ のパワーを手に入れよう


ユーザー定義リテラル

// 四角形を 45° 回転させて描画

rect.rotated(45_deg).draw();


Range-based for ループ

// 写真に含まれる顔をすべて検出し、白く塗りつぶす

for (const auto& face : Imaging::DetectFaces(image))
{
face.overwrite(image, Palette::White);
}


可変引数テンプレート

// 画面に "今日は 12 月 4 日" と表示

Println(L"今日は {} 月 {} 日"_fmt, 12, 4);


ラムダ式

Array<Circle> balls;

// 画面より上に出たボールを削除
Eraese_if(balls, [](const auto& ball){ return ball.y < 0; });

 Siv3D は 2011 年以降に導入された C++ の最新規格 (C++11/14/17) に基づいて設計されています。ライブラリだけでなく、リファレンス・サンプルのすべてが最新の C++ 規格で書かれているので、Siv3D を使うことで新しい C++ の書き方を学習できます。


究極に短いコードでゲームやアプリを開発しよう


図形と文字 (7 行)

# include <Siv3D.hpp>


void Main()
{
const Font font(30);

while (System::Update())
{
Circle(Mouse::Pos(), 100).draw();

font(Mouse::Pos()).draw(50, 200, Palette::Orange);
}
}


ペイント (9 行)

# include <Siv3D.hpp>


void Main()
{
Image image(Window::Size(), Palette::White);

DynamicTexture texture(image);

while (System::Update())
{
if (Input::MouseL.pressed)
{
Line(Mouse::PreviousPos(), Mouse::Pos()).overwrite(image, 8, Palette::Orange);

texture.fill(image);
}

texture.draw();
}
}


電卓 (10 行)

# include <Siv3D.hpp>


void Main()
{
Graphics::SetBackground(Color(240));

const Font font(40);

String expression;

while (System::Update())
{
Input::GetCharsHelper(expression);

font(expression).draw(10, 10, Palette::Black);

if (const auto result = EvaluateOpt(expression))
{
font(result.value()).draw(10, 250, Palette::Black);
}
}
}


Kinect (15 行)

# include <Siv3D.hpp>


void Main()
{
KinectV2::Start();

DynamicTexture depthTexture;

std::array<Optional<KinectV2Body>, 6> bodies;

while (System::Update())
{
if (KinectV2::HasNewDepthFrame())
{
KinectV2::GetDepthFrame(depthTexture);
}

if (KinectV2::HasNewBodyFrame())
{
KinectV2::GetBodyFrame(bodies);
}

depthTexture.draw();

for (const auto& body : bodies)
{
if (body)
{
for (const auto& joint : body->joints)
{
Circle(joint.depthSpacePos, 15).drawFrame(6.0, 0.0, Palette::Red);
}
}
}
}
}


MIDI シーケンサ (21 行)

# include <Siv3D.hpp>


void Main()
{
Window::Resize(480, 480);
Graphics::SetBackground(Palette::White);
Midi::SendMessage(MidiMessage::SetInstrument(0, GMInstrument::MusicBox));

const Array<uint8> midis{ 86, 84, 83, 81, 79, 77, 76, 74, 72, 71, 69, 67, 65, 64, 62, 60 };
const int32 dotSize = 30;

Grid<bool> dots(16, 16);
int32 previousLine = 0;

while (System::Update())
{
const int32 currentLine = Time::MillisecSince1601() / 160 % 16;

if (currentLine != previousLine)
{
for (auto i : step(dots.height))
{
Midi::SendMessage(dots[i][currentLine] ? MidiMessage::NoteOn(0, midis[i]) : MidiMessage::NoteOff(0, midis[i]));
}

previousLine = currentLine;
}

for (auto p : step({ dots.width, dots.height }))
{
Rect rect(p * dotSize, dotSize, dotSize);

if (rect.leftClicked)
{
dots[p.y][p.x] = !dots[p.y][p.x];
}

const Color color = HSV(40, 0.2 + dots[p.y][p.x] * 0.8 - (p.x == currentLine) * 0.2, 0.9 + dots[p.y][p.x] * 0.1);

rect.stretched(-1).draw(color);
}
}
}


ブロックくずし (28 行)

# include <Siv3D.hpp>


void Main()
{
const Size blockSize(40, 20);
const double speed = 8.0;
Rect bar(60, 10);
Circle ball(320, 400, 8);
Vec2 ballSpeed(0, -speed);

Array<Rect> blocks;
for (auto p : step({ Window::Width() / blockSize.x , 5 }))
{
blocks.emplace_back((p*blockSize).moveBy(0, 60), blockSize);
}

while (System::Update())
{
ball.moveBy(ballSpeed);
bar.setCenter(Mouse::Pos().x, 420);

for (auto it = blocks.begin(); it != blocks.end(); ++it)
{
if (it->intersects(ball))
{
(it->bottom.intersects(ball) || it->top.intersects(ball) ? ballSpeed.y : ballSpeed.x) *= -1;

blocks.erase(it);
break;
}
}

for (auto const& block : blocks)
{
block.stretched(-1).draw(HSV(block.y - 40));
}

if (ball.y < 0 && ballSpeed.y < 0)
{
ballSpeed.y *= -1;
}

if ((ball.x < 0 && ballSpeed.x < 0) || (Window::Width() < ball.x && ballSpeed.x > 0))
{
ballSpeed.x *= -1;
}

if (ballSpeed.y > 0 && bar.intersects(ball))
{
ballSpeed = Vec2((ball.x - bar.center.x) / 8, -ballSpeed.y).setLength(speed);
}

ball.draw();
bar.draw();
}
}

 Siv3D は C++ ゲームライブラリの常識を破る豊富な機能と緻密な設計により、驚くほど短いコードでゲームやアプリの本質的な部分のプログラミングに集中できます。公式サンプル集には数行~数十行のコードで動作する様々なプログラムが掲載されています。(シューティングゲーム / ピアノ / Twitter クライアント / 15 パズル など)


独創的なクリエイターになろう

 Siv3D には一見ゲームとは関係ないように思える機能も多く用意されています。なにしろ目標は「コンピュータでできることなら何でも簡単に作れるようにする」だからです。こうしたたくさんの技術とアイデアの組み合わせの自由度の高さが、誰も見たことのないような魅力的なゲームやアプリを生み出すチャンスにつながります。


ゲームのロジックやビジュアル表現


  • Direct3D 11 による 2D / 3D 描画


  • あたり判定 (点 / 線分 / 長方形 / 円 / 三角形 / 四角形 / 多角形どうしの判定が 1 行で書ける)

  • シーン管理

  • 物理演算

  • ベクトルと行列、クォータニオン

  • 極座標系

  • 乱数・ノイズ

  • イージング


  • GUI (ボタン、スライダー、チェックボックス、ラジオボタン、テキストエリア等)

  • CSV, INI, XML, JSON ファイルパーサ

  • ネットワーク

  • ドラッグ & ドロップ

  • クリップボード


音声処理


  • ループ再生

  • 音量、スピードの変更

  • フェードイン / フェードアウト

  • テンポ、ピッチの変更

  • MIDI 再生、MIDI メッセージの送信

  • MIDI 譜面取得

  • 文章の読み上げ

  • 波形編集

  • 録音や再生している音楽を FFT でリアルタイム解析

  • オーディオファイルの読み込みと書き出し (WAVE, MP3, Ogg Vorbis, AAC, WMA, Opus)


画像処理


様々な入力デバイス


  • マウス

  • キーボード

  • ゲームパッド

  • Xbox 360 コントローラ(+振動フィードバック)

  • Web カメラによる撮影

  • マイクによる録音

  • マルチタッチ

  • ペンタブ(+ペンの傾きと筆圧)

  • シリアル通信 (Arduino 等)

  • Kinect v1 / v2

  • Leap Motion による手と指の動きの検出

  • Tobii EyeX による視線追跡

  • Myo


次のステップへ

 Siv3D は全国各地の中高生、高専・専門学校生、大学生、社会人(プロのゲーム開発者を含む)の 気鋭の開発者が利用しGame Jamドキュメントの執筆 など、ユーザコミュニティのイベントも頻繁に実施されています。

 ライブラリは年に数回の頻度で更新がリリースされ、スクリプト対応やクロスプラットフォーム化など、大規模なアップデート計画も進められています。

 使っていて困ったときのサポートは、掲示板SlackTwitter ハッシュタグ を活用しましょう。掲示板にこれまで寄せられた 170 件の質問にはすべて回答がつき、100 人以上が参加する Slack では、開発の相談や将来の Siv3D についての議論が行われています。

 もしこの記事を読んで Siv3D を気に入っていただけたら、ぜひ Siv3D 公式サイト を訪れてください。

 自分がめちゃくちゃ楽しくなるような、友達があっと驚くような、世界中に愛されるようなソフトウェアの開発のお役に立てることを心待ちにしています。