短い時間で Siv3D プログラミングの要点を学習するコースです。
1. 基本の構造
1.1 インクルードするヘッダ
Siv3D のプログラムを書くときは、いつも <Siv3D.hpp>
ヘッダをインクルードします。
# include <Siv3D.hpp>
これだけで、Siv3D のすべての機能を使ってプログラムを書けるようになります。
C++ の経験者であれば、ほかにも <iostream>
や <vector>
などの C++ 標準ライブラリヘッダをインクルードしたくなるかもしれませんが、その必要はありません。すでに <Siv3D.hpp>
の中で、Siv3D のプログラミングでよく使われる主要な C++ 標準ライブラリヘッダがインクルードされているためです。また、標準ライブラリの機能の多くが、より便利な Siv3D の関数やクラスで再実装されているため、Siv3D の学習の序盤で C++ 標準ライブラリの関数を使うことはめったにありません。
1.2 エントリーポイント
通常、C++ のプログラムのエントリーポイントは int main()
です。しかし、Siv3D ではこの main()
関数はエンジン内部ですでに実装されていて、次のようにユーザの見えないところで、ウィンドウやグラフィックスの初期化処理を行うプログラムがすでに用意されています。
// 説明のため簡略化したコード
int main()
{
Siv3D の初期化...
Main(); // この関数をユーザがプログラムする
Siv3D の終了処理...
}
ユーザが実装するプログラムは、このプログラムの void Main()
関数です。
Main()
が実行される前に初期化処理が完了しているため、ユーザは Main()
の中で最初から Siv3D の機能を使うことができ、Main()
の実行が終了したら、ウィンドウの後片付けなどサブシステムの終了処理を Siv3D が自動的に行ってくれます。
1.3 最小限のプログラム
これが Siv3D の最小のプログラムです。
# include <Siv3D.hpp>
void Main()
{
}
ただし、この Main()
は一瞬で終了してしまうので、このプログラムを実行しても、何も起こっていないように見えるでしょう。
1.4 ウィンドウを表示し続ける
プログラムがすぐに終了してしまっては、ユーザとインタラクションをするアプリケーションが作れません。Main()
がずっと続くように メインループ を実装します。次のプログラムを実行するとウィンドウが表示され続けます。
# include <Siv3D.hpp>
void Main()
{
// メインループ
while (System::Update())
{
// ここに書いた内容が毎フレーム実行される
}
}
while
文によってプログラムが半永久的に続くようになります。くり返しのたびに System::Update()
がウィンドウの表示や音楽の再生、マウスやキーボードの入力情報などを更新するので、グラフィックスの表示やユーザとのやり取りをプログラムできるようになります。
System::Update()
は普段は true
を返すため、メインループはいつまでも続きますが、ウィンドウが閉じられたり、エスケープキーが押されたりするなど、アプリケーションを終了させる特別な ユーザアクション が実行されると、以降はずっと false
を返すようになります。 アプリケーションはこの状態になったら速やかにメインループから抜け、Main()
を終了させる必要があります。上記のプログラムのように書いていれば、自然にこの通りに動作します。
デフォルトでは次の 3 つのユーザアクションが、アプリケーションを終了させる操作として設定されています
- ウィンドウを閉じる
- エスケープキーを押す
-
System::Exit()
を呼ぶ
メインループは、特に指定しない限り、使用しているモニターのリフレッシュレートと同じ速度で繰り返されます。一般的なモニタのリフレッシュレートは毎秒 60, 120, または 144 フレームです。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// ウィンドウタイトルに直近のフレームレートを表示
Window::SetTitle(Profiler::FPS());
}
}
1.5 デバッグ表示
画面にメッセージを表示してみましょう。Print
に向かって、出力の記号 <<
でテキストを送ると、そのテキストが画面に表示されます。
# include <Siv3D.hpp>
void Main()
{
Print << U"Hello, Siv3D!";
while (System::Update())
{
}
}
テキストをプログラムに登場させるときは U" "
で囲みます。Siv3D では UTF-32 という、日本語を扱いやすい形式でテキストデータを扱うため、ダブルクォーテーション "
の先頭には常に U
というプレフィックスを付け、U"Hello"
のようにします。
1.6 さまざまな値の表示
Siv3D で提供される型のほとんどは Print
で内容を表示できます。
# include <Siv3D.hpp>
void Main()
{
// 文字列リテラル
Print << U"Hello, Siv3D!";
// << の連続
Print << U"This" << U" is " << U"fun!";
// 文字リテラル
Print << U'あ';
// 整数
Print << 100 * 5 + 5;
// 浮動小数点数
Print << U"π = " << Math::Pi;
// bool
Print << Math::IsPrime(65537);
// 時間型
Print << 1h + 3s;
// 数式パーサの結果 (double 型)
Print << Eval(U"10^3 + sqrt(81) + 0.001");
// 範囲
Print << Range(0, 10);
// 日付と時刻クラス
Print << DateTime::Now();
while (System::Update())
{
}
}
1.7 デバッグ表示のあふれ
次のように Print
をメインループの中で使うと、毎フレーム新しいメッセージが追加され、古いメッセージは画面の外に追いやられます。画面外に出た古いメッセージは自動的に消去されます。
# include <Siv3D.hpp>
void Main()
{
int32 count = 0;
while (System::Update())
{
Print << count;
++count;
}
}
1.8 デバッグ表示の消去
ClearPrint()
を使うと、Print
でデバッグ表示した内容を即座に消去します。メインループの先頭で ClearPrint()
すると、現在のフレームで Print
した内容だけが表示されるようになります。
# include <Siv3D.hpp>
void Main()
{
int32 count = 0;
while (System::Update())
{
// Print したメッセージを消去
ClearPrint();
Print << count;
++count;
}
}
1.9 基本的なデータ型
Siv3D でよく使う基本的なデータ型は次のとおりです。
Siv3D で整数を扱うときは、int
や unsigned long long
のような標準の型名の代わりに、int32
や uint64
のように明示的なサイズを持つ型名を使います。これらの型名を使うことで、プラットフォーム間での移植性が高まり、一貫性のある読みやすいコードになります。
型名 | 説明 |
---|---|
bool |
ブーリアン型 (false または true ) |
int8 |
符号付き 8-bit 整数型 (-128 ~ 127) |
uint8 |
符号無し 8-bit 整数型 (0 ~ 255) |
int16 |
符号付き 16-bit 整数型 (-32,768 ~ 32,767) |
uint16 |
符号無し 16-bit 整数型 (0 ~ 65,535) |
int32 |
符号付き 32-bit 整数型 (-2,147,483,648 ~ 2,147,483,647) |
uint32 |
符号無し 32-bit 整数型 (0 ~ 4,294,967,295) |
int64 |
符号付き 64-bit 整数型 (-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807) |
uint64 |
符号無し 64-bit 整数型 (0 ~ 18,446,744,073,709,551,615) |
int128 |
符号付き 128-bit 整数型 |
uint128 |
符号無し 128-bit 整数型 |
size_t |
オブジェクトのサイズを表現する符号なし 64-bit 整数型 (0 ~ 18,446,744,073,709,551,615) |
BigInt |
任意精度多倍長整数型 |
HalfFloat |
半精度浮動小数点数型 |
float |
単精度浮動小数点数型 |
double |
倍精度浮動小数点数型 |
BigFloat |
有効数字100桁の浮動小数点数型 |
Byte |
ビット列としてのバイトデータを表す型 |
char32 |
UTF-32 のコードポイント(文字) |
String |
文字列クラス (C++ の std::u32string 相当) |
std::array<Type, N> |
固定長配列 |
Array<Type> |
動的配列 (C++ の std::vector<Type> 相当) |
Grid<Type> |
二次元配列 |
Optional<Type> |
無効値を取りうる型 (C++ の std::optional<Type> 相当) |
HashSet<Key> |
ハッシュセット (C++ の std::unordered_set<Key> 相当) |
HashTable<Key, Value> |
ハッシュテーブル (C++ の std::unordered_map<Key, Value> 相当) |
FilePath |
String のエイリアス |
StringView |
所有権を持たない文字列クラス (C++ の std::string_view 相当) |
FilePathView |
StringView のエイリアス |
# include <Siv3D.hpp>
void Main()
{
bool a = true;
int32 b = 123;
double c = 0.5;
BigInt d = 111222333444555666777888999000_big;
BigFloat e = 0.1234567890123456789_bigF;
Byte f{ 0xF7 };
char32 g = U'あ';
String h = U"Hello!";
Array<int32> i = { 10, 20, 30, 40 };
Array<String> j = { U"aaa", U"bbb" };
HalfFloat k = 3.333333f;
Print << a;
Print << b;
Print << c;
Print << d;
Print << e;
Print << f;
Print << g;
Print << h;
Print << i << U" : " << i.size();
Print << j << U" : " << j.size();
Print << k << U" : " << sizeof(k);
while (System::Update())
{
}
}
Print
以外の出力
1.10 Print
以外にも、コンソール出力 Console
, ログ出力 Logger
, 音声読み上げ出力 Say
を使えます。
# include <Siv3D.hpp>
void Main()
{
// コンソール画面に出力
Console << U"Hello, Console!";
// ログに出力 (Visual Studio の場合「出力」ウィンドウ)
Logger << U"Hello, Logger!";
// 必要に応じて読み上げ言語を設定(OS に読み上げエンジンがインストールされていない場合は使えない)
TextToSpeech::SetDefaultLanguage(LanguageCode::EnglishUS);
//TextToSpeech::SetDefaultLanguage(LanguageCode::Japanese);
// 音声読み上げ出力
Say << U"Hello, Say!";
while (System::Update())
{
}
}
1.11 画面の座標系
ウィンドウ内の黒い部分が 画面(シーン) で、Siv3D はこの領域に文字や図形、画像を表示できます。
画面のサイズは、基本の状態では 幅 800 ピクセル、高さ 600 ピクセル です。画面上の位置を表す座標系は、一番左上のピクセルを「X 座標 0」「Y 座標 0」を表す (0, 0)
と表記し、右に進むと X 座標が大きく、下に進むと Y 座標が大きくなります。画面の一番右下のピクセルの座標は (799, 599)
です。
Cursor::Pos()
を使うと、現在のマウスカーソルの座標を Point
型で取得できます。Point
型の値は X 座標を表す int32 x
と Y 座標を表す int32 y
の 2 つの成分を持っています。Point
型の値をそのまま丸ごと Print
に送って表示することもできます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
ClearPrint();
Print << Cursor::Pos(); // 現在のマウスカーソル座標を表示
Print << U"X: " << Cursor::Pos().x; // X 座標だけを表示
Print << U"Y: " << Cursor::Pos().y; // Y 座標だけを表示
}
}
2. 図形を描く
2.1 円を描く
シーンに図形を描く方法を学びましょう。Siv3D では、図形オブジェクトを作成し、その draw()
メンバ関数を呼んで描画を行います。円を描くときは Circle
を作成し、その .draw()
を呼びます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (400, 300), 半径 20 の円を描く
Circle{ 400, 300, 20 }.draw();
}
}
2.2 円の大きさを変える
Circle{}
の最後に指定するパラメータは円の半径です。この値を大きくすれば、描画される円も大きくなります。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (400, 300), 半径 100 の円を描く
Circle{ 400, 300, 100 }.draw();
}
}
2.3 X 座標がマウスカーソルと連動する円を描く
円がマウスカーソルの座標に連動して動くようにしてみましょう。Circle{}
の最初に指定するパラメータは円の中心の X 座標です。この値をマウスカーソルの X 座標にしてみます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (マウスの X 座標, 300), 半径 100 の円を描く
Circle{ Cursor::Pos().x, 300, 100 }.draw();
}
}
前章で、Print
を使って表示したメッセージはいつまでも画面に残りましたが、それは例外的なルールです。通常、Print
以外のすべての描画は System::Update()
のたびに背景の色でリセットされます。
2.4 マウスカーソルと連動する円を描く
Siv3D で X 座標、Y 座標 2 つの値を受け取る関数は、多くの場合、1 つの Point
型、もしくは Vec2
型を受け取る別バージョンの関数(オーバーロード)を提供しているケースがよくあります。Circle
も、「X 座標」「Y 座標」「半径」の 3 つの引数ではなく、「中心座標 (Vec2
型)」「半径」の 2 つの引数を受け取るオーバーロードがあります。これを使って円をマウスカーソルと連動させます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 中心座標 (マウスの X 座標, マウスの Y 座標), 半径 100 の円を描く
Circle{ Cursor::Pos(), 100 }.draw();
}
}
2.5 色を付ける
図形に色を付けたいときは draw()
関数に色を渡します。色の指定の方法は大きく 4 通りあります。
色の表現 | 値の範囲 |
---|---|
Palette::色名 | Web カラー の名前で色を指定 |
ColorF{ r, g, b, a } | 0.0 - 1.0 の範囲で RGBA の各成分を指定 |
Color{ r, g, b, a } | 0 - 255 の整数の範囲で RGBA の各成分を指定 |
HSV{ h, s, v, a } | 色相 h , 彩度 s , 明度 v とアルファ値 a の各成分を指定。h は 0.0 - 360.0 (370.0 は 10.0 と同じ). s, v, a は 0.0 - 1.0,の範囲 |
Palette::色名 は、Palette::Orange
, Palette::Yellow
のように、RGB 値がわからなくても使えます。
ColorF は、Siv3D で最も使われる色の表現形式です。
Color は、Image
型の要素で、Siv3D で画像処理をするときに使われる形式です。
HSV は、赤っぽい、青っぽいなど色の種類を表す色相 (hue) と、色の鮮やかを表す彩度 (saturation), 色の明るさを表す明度 (value) の 3 要素を使った HSV 色空間で色を表現します。
ColorF
, Color
, HSV
はいずれも アルファ値 a
を持ちます。アルファ値は「不透明度」を表し、最大値 (ColorF
, HSV
の場合 1.0, Color
の場合 255) ではまったく透過しませんが、値を小さくするとそれに応じて背景を透過する半透明になり、0 になると完全に透明になります。
色の指定はプログラムでよく使われるので、次のような短い書き方も用意されています。例えば ColorF{ 0.5 }
は ColorF{ 0.5, 0.5, 0.5, 1.0 }
と同等です。
短い書き方 | 意味 |
---|---|
ColorF{ r, g, b } |
ColorF{ r, g, b, 1.0 } |
ColorF{ rgb, a } |
ColorF{ rgb, rgb, rgb, a } |
ColorF{ rgb } |
ColorF{ rgb, rgb, rgb, 1.0 } |
Color{ r, g, b } |
Color{ r, g, b, 255 } |
Color{ rgb, a } |
Color{ rgb, rgb, rgb, a } |
Color{ rgb } |
Color{ rgb, rgb, rgb, 255 } |
HSV{ h, s, v } |
HSV{ h, s, v, 1.0 } |
HSV{ h, a } |
HSV{ h, 1.0, 1.0, a } |
HSV{ h } |
HSV{ h, 1.0, 1.0, 1.0 } |
色の付いたいくつかの円を描いてみましょう。.draw()
に色を指定しなかった場合のデフォルトの色は Palette::White
(ColorF{ 1.0, 1.0, 1.0, 1.0 }
) です。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 左から順に 7 つの円を描く
Circle{ 100, 200, 40 }.draw();
Circle{ 200, 200, 40 }.draw(Palette::Green);
Circle{ 300, 200, 40 }.draw(Palette::Skyblue);
Circle{ 400, 200, 40 }.draw(ColorF{ 1.0, 0.8, 0.0 });
Circle{ 500, 200, 40 }.draw(Color{ 255, 127, 127 });
Circle{ 600, 200, 40 }.draw(HSV{ 160.0, 1.0, 1.0 });
Circle{ 700, 200, 40 }.draw(HSV{ 160.0, 0.75, 1.0 });
// 半透明の円
Circle{ Cursor::Pos(), 80 }.draw(ColorF{ 0.0, 0.5, 1.0, 0.8 });
}
}
2.6 背景の色を変える
シーンの背景色を変えるには Scene::SetBackground()
に色を渡します。新しい背景色は、それ以降の System::Update()
で画面の描画内容をリセットするときから反映されます。背景色は一度設定すると、再度変更されるまで同じ設定が使われます。
# include <Siv3D.hpp>
void Main()
{
// 背景色を ColorF{ 0.3, 0.6, 1.0 } に設定
Scene::SetBackground(ColorF{ 0.3, 0.6, 1.0 });
while (System::Update())
{
Circle{ Cursor::Pos(), 80 }.draw();
}
}
2.7 背景の色を時間の経過とともに変える
Scene::Time()
はプログラムの経過時間(秒)を double
型の値で返します。これを用いて、時間に応じて背景色の色相を変化させてみましょう。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 色相 hue = 経過時間 (秒) * 60
const double hue = (Scene::Time() * 60);
Scene::SetBackground(HSV{ hue, 0.6, 1.0 });
}
}
2.8 長方形を描く
長方形を描くときは Rect
を作成して .draw()
します。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 座標 (20, 40) を左上の基準位置にして、幅 400, 高さ 100 の長方形を描く
Rect{ 20, 40, 400, 100 }.draw();
// 座標 (100, 200) を左上の基準位置にして、幅が 80 の正方形を描く
Rect{ 100, 200, 80 }.draw(Palette::Orange);
// 座標 (400, 300) を中心の基準位置にして、幅 80, 高さ 40 の長方形を描く
Rect{ Arg::center(400, 300), 80, 40 }.draw(Palette::Pink);
// マウスカーソルの座標を中心の基準位置にして、幅が 100 の正方形を描く
Rect{ Arg::center(Cursor::Pos()), 100 }.draw(ColorF{ 1.0, 0.0, 0.0, 0.5 });
// 座標や大きさを浮動小数点数 (小数を含む数)で指定したい場合は RectF
RectF{ 200.4, 450.3, 390.5, 122.5 }.draw(Palette::Skyblue);
}
}
図形は draw()
した順番に描画されます。このプログラムの、マウスカーソルに追従する赤い正方形と画面の下にある水色の大きな長方形を比べると、後者のほうがあとから描画されます。
Rect
型は左上の座標と幅、高さをそれぞれ int32 x
, int32 y
, int32 w
, int32 h
というメンバ変数で表します。整数ではなく浮動小数点数で扱いたい場合は、すべての要素が double
型である RectF
を使います。
2.9 枠を描く
図形の枠だけを描きたい場合、.draw()
の代わりに .drawFrame()
を使います。.drawFrame()
の第 1 引数には図形の内側方向への太さを、第 2 引数には外側方向への太さを指定します。図形の .draw()
や .drawFrame()
の戻り値はその図形自身なので、rect.draw().drawFrame()
のように関数を続けて書くこともできます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 長方形の内側に 3px の枠を描く
Rect{ 100, 100, 100, 30 }
.drawFrame(3, 0);
// 長方形の外側に 3px の枠を描く
Rect{ 220, 100, 100, 30 }
.drawFrame(0, 3);
// 長方形と、その内側 3px と外側 3px に枠を描く
Rect{ 200, 200, 400, 100 }
.draw(Palette::White)
.drawFrame(3, 3, Palette::Orange);
// 円の内側 1px と外側 1px に枠を描く
Circle{ Cursor::Pos(), 40 }
.drawFrame(1, 1, Palette::Seagreen);
}
}
2.10 線分を描く
始点と終点を指定して線分を描くときは Line
を作成して .draw()
します。.draw()
のパラメータには描画する線分の太さと色を指定します。線分の両端を丸くしたり、点線にしたりするなど、スタイルの変更もできます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// 座標 (100, 100) から (400, 150) まで太さ 4px の線分を描く
Line{ 100, 100, 400, 150 }.draw(4, Palette::Yellow);
// 座標 (400, 300) からマウスカーソルの座標まで太さ 10px の線分を描く
Line{ 400, 300, Cursor::Pos() }.draw(10, Palette::Skyblue);
// 通常の線
Line{ 100, 400, 700, 400 }.draw(12, Palette::Orange);
// 両端が丸い線
Line{ 100, 450, 700, 450 }.draw(LineStyle::RoundCap, 12, Palette::Orange);
// 四角いドットの線
Line{ 100, 500, 700, 500 }.draw(LineStyle::SquareDot, 12, Palette::Orange);
// 丸いドットの線
Line{ 100, 550, 700, 550 }.draw(LineStyle::RoundDot, 12, Palette::Orange);
}
}
2.11 様々な図形
Siv3D にはこれ以外にも様々な図形クラスが用意されていて、複雑な形状を短いコードで記述できます。
クラス | 図形 |
---|---|
Line |
線分 |
Rect |
長方形 (int32) |
RectF |
長方形 (double) |
Circle |
円 |
Ellipse |
楕円 |
Triangle |
三角形 |
Quad |
凸な四角形 |
RoundRect |
角丸四角形 |
Polygon |
多角形 |
MultiPolygon |
多角形の集合 |
Bezier2 |
2 次ベジェ曲線 |
Bezier3 |
3 次ベジェ曲線 |
LineString |
連続する複数の線分 |
Spline2D |
スプライン曲線 |
Shape2D |
正多角形や星、ハート、十字などのコレクション |
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
const double angle = (Scene::Time() * 60_deg);
Line{ 20, 20, 80, 80 }.draw(4);
Rect{ 120, 40, 60, 40 }.draw();
Circle{ 250, 50, 30 }.draw();
Ellipse{ 350, 50, 30, 20 }.draw();
Triangle{ 450, 60, 70, angle }.draw();
Quad{ {520, 20}, {580, 40}, {580, 80}, {520, 80} }.draw();
RoundRect{ 620, 20, 80, 60, 10 }.draw();
Rect{ 720, 30, 60, 40 }.rounded(20, 0, 20, 0).draw();
Rect{ 20, 130, 60, 40 }.rotated(angle).draw();
Polygon{ {120, 120}, {150, 110}, {180, 150}, {150, 140}, {120, 180} }.draw();
Bezier2{ {220, 180}, {250, 100}, {280, 120} }.draw(3);
Bezier3{ {320, 180}, {350, 180}, {350, 120}, {380, 120} }.draw(3);
LineString{ {420, 180}, {450, 120}, {460, 180}, {480, 120} }.draw(3);
LineString{ {520, 180}, {540, 150}, {560, 160}, {580, 120} }.asSpline().draw(3);
Shape2D::Plus(40, 20, { 650, 150 }, angle).draw();
Shape2D::Cross(40, 15, { 750, 150 }, angle).draw();
Shape2D::Pentagon(40, { 50, 250 }, angle).draw();
Shape2D::Hexagon(40, { 150, 250 }, angle).draw();
Shape2D::Ngon(8, 40, { 250, 250 }, angle).draw();
Shape2D::Star(40, { 350, 250 }, angle).draw();
Shape2D::NStar(8, 40, 30, { 450, 250 }, angle).draw();
Line{ 520, 220, 580, 280 }.drawArrow(4, { 20, 30 });
Shape2D::Rhombus(60, 40, { 650, 250 }, angle).draw();
Shape2D::RectBalloon(Rect{ 720, 220, 60, 40 }, { 710, 290 }).draw();
Shape2D::Stairs({ 80, 380 }, 60, 60, 4).draw();
Shape2D::Heart(40, { 150, 350 }, angle).draw();
Shape2D::Hexagon(40, { 250, 350 })
.asPolygon().addHole({ {260, 340}, {240, 340}, {240, 360}, {260, 360} }).draw();
Rect{ 320, 320, 60, 60 }.drawFrame(5);
Circle{ 450, 350, 40 }.drawFrame(5);
Circle{ 550, 350, 40 }.drawPie(angle, 120_deg);
Circle{ 650, 350, 40 }.drawArc(angle, 240_deg, 10, 0);
Circle{ 750, 350, 40 }.drawArc(LineStyle::RoundCap, angle, 240_deg, 10, 0);
Line{ 20, 420, 80, 480 }.draw(LineStyle::RoundCap, 6);
Line{ 120, 420, 180, 480 }.draw(LineStyle::SquareDot, 6);
Line{ 220, 420, 280, 480 }.draw(LineStyle::RoundDot, 6);
}
}
2.12 グラデーションを描く
Line
や Triangle
, Rect
, RectF
, Quad
には、頂点ごとに色を指定し、グラデーションで塗りつぶすオプションがあります。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
Line{ 100, 100, 500, 150 }
.draw(6, Palette::Yellow, Palette::Red);
Triangle{ 200, 200, 100 }
.draw(HSV{ 0 }, HSV{ 120 }, HSV{ 240 });
// 左から右へのグラデーション
Rect{ 400, 200, 200, 100 }
.draw(Arg::left = Palette::Skyblue, Arg::right = Palette::Blue);
// 上から下へのグラデーション
Rect{ 200, 400, 400, 100 }
.draw(Arg::top = ColorF{ 1.0, 1.0 }, Arg::bottom = ColorF{ 1.0, 0.0 });
}
}
3. 画像を描く
3.1 絵文字を描画する
シーンに画像を描きたいときは Texture
を作成し、.draw()
または .drawAt()
します。Texture
は、次のような方法で作成できます。
- 画像ファイルから画像データを読み込む
- プログラムで作成した画像データ (
Image
) から作成する - Siv3D が標準で提供する絵文字コレクションから作成する
- Siv3D が標準で提供するアイコンコレクションから作成する
Texture
はテクスチャのリソースをデストラクタで自動的に解放するため、リソースの解放処理を明示的に書く必要はありません。
まず最初に、絵文字コレクションから好きな絵文字を選んで Texture
を作成し、それを描画するプログラムを書いてみましょう。Texture
の作成にはメモリ確保などの実行時負荷がかかります。メインループの中で毎フレーム新しい Texture
を作成するのは避け、作成が 1 回だけで済むようにしましょう。
絵文字コレクションから Texture
を作成するには、Texture
のコンストラクタ引数に U"🐈"_emoji
のように絵文字を渡します。
Texture::drawAt()
.drawAt()
では、テクスチャの中心をどこに据えるかをシーンの座標で指定します。絵文字やアイコンを描く場合はこの指定方法が便利です。
# include <Siv3D.hpp>
void Main()
{
// 🐈 の絵文字からテクスチャを作成
const Texture texture{ U"🐈"_emoji };
while (System::Update())
{
// テクスチャを座標 (0, 0) を中心に描画
texture.drawAt(0, 0);
// テクスチャを座標 (200, 200) を中心に描画
texture.drawAt(200, 200);
// テクスチャを、シーン中央を中心にして描画
texture.drawAt(Scene::Center());
}
}
Texture::draw()
.draw()
では、テクスチャの左上をどこに据えるかをシーンの座標で指定します。背景画像や UI などを描くときにはこの指定方法が便利な場合があります。
# include <Siv3D.hpp>
void Main()
{
// 🐈 の絵文字からテクスチャを作成
const Texture texture{ U"🐈"_emoji };
while (System::Update())
{
// テクスチャを座標 (400, 0) から描画
texture.draw(400, 0);
// 座標を指定しない場合 (0, 0) から描画
texture.draw();
// テクスチャをシーン中心から描画
texture.draw(Scene::Center());
}
}
Siv3D で使える絵文字の一覧
Siv3D で使える絵文字は約 3,600 種類あります。絵文字を探すときは emojipedia の Categories から調べるのが便利です。 OpenSiv3D v0.6.5 はオープンソースの絵文字フォント Noto Color Emoji (Unicode 14.0 版) を内蔵しているので、Siv3D アプリはどのプラットフォームでも同じ見た目の絵文字を表示できます。
3.2 テクスチャを拡大縮小して描画する
Texture::scaled()
Texture::scaled()
に拡大縮小倍率を指定すると、Texture
にサイズ情報が付加された TextureRegion
を作成できます。TextureRegion
は Texture
のように .draw()
または .drawAt()
できます。Texture
を作成するのと異なり、既存の Texture
から TextureRegion
を作成するのは非常に軽い実行時負荷です。サンプルでは示していませんが、縦横で異なる倍率に設定することもできます。
# include <Siv3D.hpp>
void Main()
{
const Texture cat{ U"🐈"_emoji };
const Texture dog{ U"🐕"_emoji };
while (System::Update())
{
// 2 倍に拡大して描画
cat.scaled(2.0).drawAt(200, 300);
// 1.5 倍に拡大して描画
cat.scaled(1.5).drawAt(400, 300);
// 半分のサイズに縮小して描画
dog.scaled(0.5).drawAt(600, 300);
}
}
Texture::resized()
Texture::resized()
は、拡大縮小後のサイズを倍率ではなくピクセル単位で指定します。Siv3D 標準の絵文字のサイズは幅が 136 ピクセルなので、136 より大きい数を指定すると拡大、小さい数を指定すると縮小表示になります。サンプルでは示していませんが、縦横で異なるサイズに設定することもできます。
# include <Siv3D.hpp>
void Main()
{
const Texture cat{ U"🐈"_emoji };
const Texture dog{ U"🐕"_emoji };
while (System::Update())
{
// 300px に拡大して描画
cat.resized(300).drawAt(200, 300);
// 200px に拡大して描画
cat.resized(200).drawAt(400, 300);
// 20px に縮小して描画
dog.resized(20).drawAt(600, 300);
}
}
3.3 テクスチャを回転して描画する
Texture::rotated()
または Texture::rotatedAt()
によって、テクスチャに回転情報を付加した TexturedQuad
を作成できます。TexturedQuad
は Texture
のように .draw()
または .drawAt()
できます。TextureRegion
と同様、TexturedQuad
を作成するのは非常に軽い実行時負荷です。
Texture::rotated()
テクスチャの中心を軸にして回転します。回転角度はラジアンで指定します。
# include <Siv3D.hpp>
void Main()
{
const Texture cat{ U"🐈"_emoji };
while (System::Update())
{
// 毎秒 90° 時計回りに回転
cat.rotated(Scene::Time() * 90_deg).drawAt(Scene::Center());
}
}
Texture::roatedAt()
テクスチャ上の指定した座標を軸にして回転します。Vec2{ 0, 0 }
を指定すればテクスチャの左上が軸になります。回転角度はラジアンで指定します。
# include <Siv3D.hpp>
void Main()
{
const Texture cat{ U"🐈"_emoji };
while (System::Update())
{
// 画像中の (40, 20) を軸に回転させて描画
cat.rotatedAt(Vec2{ 40, 20 }, Scene::Time() * 90_deg).draw(Scene::Center());
}
}
3.4 テクスチャを上下・左右反転して描画する
Texture::flipped()
で上下反転、Texture::mirrored()
で左右反転した TextureRegion
を作成できます。それぞれ、引数をとらずに反転する関数、引数の bool
値が true
のときだけ反転する関数の 2 種類のオーバーロードがあります。
# include <Siv3D.hpp>
void Main()
{
const Texture cat{ U"🐈"_emoji };
while (System::Update())
{
// 上下反転
cat.flipped().drawAt(200, 300);
// 左右反転
cat.mirrored().drawAt(400, 300);
// 反転するかを bool 値で指定するオーバーロード
cat.mirrored(false).drawAt(600, 300);
}
}
3.5 アイコンを描画する
アイコンコレクションから Texture
を作成するには、Texture
のコンストラクタ引数にアイコンオブジェクトを渡します。アイコンは全部で約 7,700 種類用意されています。
Texture
のコンストラクタには、Font Awesome アイコン一覧 または Material Design Icons で調べられる 16 進数コードに _icon
を付けた値と、アイコンの基本サイズ(ピクセル)を渡します。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.92 });
// 歯車 (f013) のアイコン
const Texture icon0{ 0xf013_icon, 40 };
// 南京錠 (f023) のアイコン
const Texture icon1{ 0xf023_icon, 80 };
// 拡大鏡 (f00e) のアイコン
const Texture icon2{ 0xf00e_icon, 120 };
while (System::Update())
{
icon0.drawAt(200, 300, ColorF{ 0.25 });
icon1.drawAt(400, 300, ColorF{ 0.25 });
icon2.drawAt(600, 300, ColorF{ 0.25 });
}
}
3.6 画像ファイルを読み込んで描画する
画像ファイルから Texture
を作成するには、Texture
のコンストラクタ引数に、読み込みたい画像ファイルのパスを渡します。このファイルパスは、実行ファイルがあるフォルダ(開発中は App
フォルダ)を基準とする相対パスか、絶対パスを使用します。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
// 風車の画像
const Texture textureWindmill{ U"example/windmill.png" };
// Siv3D くん(Siv3D の公式マスコットキャラクター)の画像
const Texture textureSiv3DKun{ U"example/siv3d-kun.png" };
while (System::Update())
{
textureWindmill.draw(40, 20);
textureSiv3DKun.draw(400, 100);
}
}
対応している画像フォーマット
OpenSiv3D v0.6.5 では、9 種類の画像フォーマットの読み込みがサポートされています。
フォーマット | 拡張子 | 対応状況 |
---|---|---|
PNG | png | ✔ |
JPEG | jpg/jpeg | ✔ |
BMP | bmp | ✔ |
SVG | svg | ✔ |
GIF | gif | ✔ |
TGA | tga | ✔ |
PPM | ppm/pgm/pbm/pnm | ✔ |
WebP | webp | ✔ |
TIFF | tif/tiff | ✔ |
JPEG2000 | jp2 | (将来のバージョン) |
DDS | dds | (将来のバージョン) |
3.7 テクスチャの一部を描画する
テクスチャの全部ではなく、特定の長方形の領域だけを描画したい場合は Texture::operator()
で、テクスチャ上の描画したい領域を指定して TextureRegion
を作成します。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
const Texture textureWindmill{ U"example/windmill.png" };
const Texture textureSiv3DKun{ U"example/siv3d-kun.png" };
while (System::Update())
{
// 画像の (250, 100) から幅 200, 高さ 150 の長方形部分
textureWindmill(250, 100, 200, 150).draw(40, 20);
// 画像の (100, 0) から幅 100, 高さ 150 の長方形部分
textureSiv3DKun(100, 0, 100, 150).draw(400, 100);
}
}
3.8 動画をテクスチャとして扱う
シーンに動画を描きたいときは VideoTexture
を作成し、.draw()
または .drawAt()
します。VideoTexture
は Texture
とほぼ同じインタフェースを持ち、動画ファイルを Texture
のように扱えます。VideoTexture
は毎フレーム .advance()
を呼ぶことで再生位置を進めます。Siv3D はバックグラウンドのスレッドで動画の次のフレームの先読みを行っています。
対応する動画フォーマットはプラットフォームによって異なりますが、MP4 ファイルは Windows, macOS, Linux でサポートされています。音声付き動画の音声を再生する方法は OpenSiv3D v0.6.5 では用意されていません。映像のみです。
# include <Siv3D.hpp>
void Main()
{
// ループする場合は Loop::Yes, ループしない場合は Loop::No
const VideoTexture videoTexture{ U"example/video/river.mp4", Loop::Yes };
while (System::Update())
{
ClearPrint();
// 再生位置(秒) / 動画の長さ(秒)
Print << videoTexture.posSec() << U" / " << videoTexture.lengthSec();
// 動画の時間を進める (デフォルトでは Scece::DeltaTime() 秒)
videoTexture.advance();
videoTexture
.scaled(0.5).draw();
videoTexture
.scaled(0.25)
.rotated(Scene::Time() * 30_deg)
.drawAt(Cursor::Pos());
}
}
4. フォントを使う
4.1 Font
前章までテキストの表示に使ってきた Print
は、フォントのサイズや種類、描画位置に自由度がありませんでした。自由にカスタマイズしたフォントを使ってテキストを描きたいときは Font
を作成し、描画したい内容を ()
でつなげたあと、.draw()
または .drawAt()
します。
Texture
と同じように、Font
の作成にはメモリ確保などの実行時負荷がかかります。メインループの中で毎フレーム新しい Font
を作成するのは避け、作成が 1 回だけになるようにしましょう。
# include <Siv3D.hpp>
void Main()
{
// 基本サイズ 50 のフォントを作成
const Font font{ 50 };
while (System::Update())
{
// 左上位置 (20, 20) からテキストを描く
font(U"Hello, Siv3D!").draw(20, 20);
// テキストの中心座標が画面の中心になるようにテキストを描く
font(U"C++").drawAt(Scene::Center(), Palette::Skyblue);
// 文字列以外を渡すと Format される
font(Cursor::Pos()).draw(50, 300);
// 複数渡すと、それぞれを Format した文字列をつなげる
font(123, U"ABC").draw(50, 400, ColorF{ 0.5, 1.0, 0.5 });
font(U"{}/{}/{}"_fmt(2021, 12, 31)).draw(50, 500, ColorF{ 1.0, 0.5, 0.0 });
}
}
4.2 改行する
テキストの中に改行文字 '\n'
が含まれていると、そこで改行されます。
# include <Siv3D.hpp>
void Main()
{
const Font font{ 50 };
while (System::Update())
{
font(U"Hello,\nSiv3D\n\n!!!").draw(20, 20);
}
}
4.3 フォントのサイズ
Font
のコンストラクタの第 1 引数にはフォントの基本サイズを指定します。単位はピクセルです。基本サイズはあとから変更できません。1 つの Font
からさまざまなサイズのテキストを描く方法はのちほど紹介します。
# include <Siv3D.hpp>
void Main()
{
// 基本サイズ 20 のフォント
const Font font20{ 20 };
// 基本サイズ 40 のフォント
const Font font40{ 40 };
// 基本サイズ 60 のフォント
const Font font60{ 60 };
// 基本サイズ 80 のフォント
const Font font80{ 80 };
const String text = U"Hello, Siv3D!";
while (System::Update())
{
font20(text).draw(20, 20);
font40(text).draw(20, 60);
font60(text).draw(20, 120);
font80(text).draw(20, 200);
}
}
4.4 フォントの種類
Siv3D には異なる太さの 7 種類の日本語フォントと、5 地域向けの CJK(中国語・韓国語・日本語対応)フォント、白黒絵文字フォント、カラー絵文字フォントが同梱されています。Font
のコンストラクタにおいて Typeface::
で書体を指定することで、それらの書体を利用できます。何も指定しなかった場合 Typeface::Regular
が選択されます。
Typeface | 説明 |
---|---|
Typeface::Thin |
細い日本語フォント |
Typeface::Light |
やや細い日本語フォント |
Typeface::Regular |
通常日本語フォント |
Typeface::Medium |
やや太い日本語フォント |
Typeface::Bold |
太い日本語フォント |
Typeface::Heavy |
とても太い日本語フォント |
Typeface::Black |
最も太い日本語フォント |
Typeface::CJK_Regular_JP |
日本語デザインの CJK フォント |
Typeface::CJK_Regular_KR |
韓国語デザインの CJK フォント |
Typeface::CJK_Regular_SC |
簡体字デザインの CJK フォント |
Typeface::CJK_Regular_TC |
台湾繁体字デザインの CJK フォント |
Typeface::CJK_Regular_HK |
香港繁体字デザインの CJK フォント |
Typeface::MonochromeEmoji |
モノクロ絵文字フォント |
Typeface::ColorEmoji |
カラー絵文字フォント |
# include <Siv3D.hpp>
void Main()
{
const Font fontThin{ 36, Typeface::Thin };
const Font fontLight{ 36, Typeface::Light };
const Font fontRegular{ 36, Typeface::Regular };
const Font fontMedium{ 36, Typeface::Medium };
const Font fontBold{ 36, Typeface::Bold };
const Font fontHeavy{ 36, Typeface::Heavy };
const Font fontBlack{ 36, Typeface::Black };
const Font fontJP{ 36, Typeface::CJK_Regular_JP };
const Font fontKR{ 36, Typeface::CJK_Regular_KR };
const Font fontSC{ 36, Typeface::CJK_Regular_SC };
const Font fontTC{ 36, Typeface::CJK_Regular_TC };
const Font fontHK{ 36, Typeface::CJK_Regular_HK };
const Font fontMono{ 36, Typeface::MonochromeEmoji };
// カラー絵文字フォントは、サイズの指定が無視される仕様
const Font fontEmoji{ 36, Typeface::ColorEmoji };
const String s0 = U"Hello, Siv3D!";
const String s1 = U"こんにちは 你好 안녕하세요 骨曜喝愛遙扇";
const String s2 = U"🐈🐕🚀";
while (System::Update())
{
fontThin(s0).draw(20, 20);
fontLight(s0).draw(20, 60);
fontRegular(s0).draw(20, 100);
fontMedium(s0).draw(20, 140);
fontBold(s0).draw(20, 180);
fontHeavy(s0).draw(20, 220);
fontBlack(s0).draw(20, 260);
fontJP(s1).draw(20, 300);
fontKR(s1).draw(20, 340);
fontSC(s1).draw(20, 380);
fontTC(s1).draw(20, 420);
fontHK(s1).draw(20, 460);
fontMono(s2).draw(20, 500);
fontEmoji(s2).draw(20, 540);
}
}
4.5 フォントファイルからフォントを読み込んで使う
コンピュータ上にあるフォントファイルから Font
を作成するには、Font
のコンストラクタに、読み込みたいフォントファイルのパスを渡します。このファイルパスは、実行ファイルがあるフォルダ(App フォルダ)を基準とする相対パスか、絶対パスを使用します。リリース用のアプリを作るときには、のちの章で説明する「リソース」パスの使用を推奨します。
# include <Siv3D.hpp>
void Main()
{
// RocknRollOne-Regular.ttf をロードして使う
const Font font{ 50, U"example/font/RocknRoll/RocknRollOne-Regular.ttf" };
while (System::Update())
{
font(U"Hello, Siv3D!\nこんにちは!").draw(20, 20);
}
}
4.6 PC にインストールされているフォントを使う
PC にインストールされているフォントは OS ごとに特殊なフォルダに保存されています。そのフォルダのパスを FileSystem::GetFolderPath()
で取得し、フォントファイル名とつなげることで、ファイルパスを構築できます。FileSystem::GetFolderPath()
に渡す SpecialFolder
の種類と OS によって取得できるパスの対応表は次の通りです。
Windows | macOS | Linux | |
---|---|---|---|
SpecialFolder::SystemFonts |
(OS):/WINDOWS/Fonts/ | /System/Library/Fonts/ | /usr/share/fonts/ |
SpecialFolder::LocalFonts |
(OS):/WINDOWS/Fonts/ | /Library/Fonts/ | /usr/local/share/fonts/ (存在する場合) |
SpecialFolder::UserFonts |
(OS):/WINDOWS/Fonts/ | ~/Library/Fonts/ | /usr/local/share/fonts/ (存在する場合) |
# include <Siv3D.hpp>
void Main()
{
# if SIV3D_PLATFORM(WINDOWS)
const Font font{ 60, FileSystem::GetFolderPath(SpecialFolder::SystemFonts) + U"arial.ttf" };
# elif SIV3D_PLATFORM(MACOS)
const Font font{ 60, FileSystem::GetFolderPath(SpecialFolder::SystemFonts) + U"Helvetica.dfont" };
# endif
while (System::Update())
{
# if SIV3D_PLATFORM(WINDOWS)
font(U"Arial").draw(20, 40);
# elif SIV3D_PLATFORM(MACOS)
font(U"Helvetica").draw(20, 40);
# endif
}
}
SIV3D_PLATFORM(WINDOWS)
や SIV3D_PLATFORM(MACOS)
は Siv3D でプラットフォーム別のコードを書くときに使えるマクロです。
5. GUI
5.1 ボタン
ボタンの表示と入力の取得を実装するときは SimpleGUI::Button()
関数を使うと便利です。関数にはボタンのテキストや位置、幅、状態などを設定できます。SimpleGUI::Button()
は自身が押されたときに true
を返します。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
if (SimpleGUI::Button(U"Red", Vec2{ 100, 100 }))
{
Scene::SetBackground(ColorF{ 0.8, 0.2, 0.2 });
}
if (SimpleGUI::Button(U"Green", Vec2{ 100, 150 }))
{
Scene::SetBackground(ColorF{ 0.2, 0.8, 0.2 });
}
if (SimpleGUI::Button(U"Blue", Vec2{ 100, 200 }))
{
Scene::SetBackground(ColorF{ 0.2, 0.2, 0.8 });
}
// ボタンの幅を 200px に指定
if (SimpleGUI::Button(U"White", Vec2{ 100, 250 }, 200))
{
Scene::SetBackground(ColorF{ 0.9 });
}
if (SimpleGUI::Button(U"Black", Vec2{ 100, 300 }, 200))
{
Scene::SetBackground(ColorF{ 0.1 });
}
// ボタンを無効化
if (SimpleGUI::Button(U"Gray", Vec2{ 100, 350 }, 200, false))
{
Scene::SetBackground(ColorF{ 0.5 });
}
// ボタンを無効化、ボタンの幅はテキストに合わせる
if (SimpleGUI::Button(U"Yellow", Vec2{ 100, 400 }, unspecified, false))
{
Scene::SetBackground(ColorF{ 0.8, 0.8, 0.1 });
}
}
}
5.2 スライダー
スライダーの表示と値の取得を実装するときは SimpleGUI::Slider()
関数を使うと便利です。関数にはスライダーのテキストや位置、幅、値の範囲などを設定できます。テキストを持たない縦方向のスライダーは SimpleGUI::VerticalSlider()
を使います。SimpleGUI::Slider()
と SimpleGUI::VerticalSlider()
は値が変更されたときに true
を返します。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
ColorF color1{ 1.0 };
ColorF color2{ 1.0, 0.5, 0.0 };
ColorF color3{ 0.2, 0.6, 0.9 };
double value1 = 5.0;
double value2 = 7.0;
double value3 = 2.0;
double value4 = 4.0;
while (System::Update())
{
SimpleGUI::Slider(color1.r, Vec2{ 100, 40 });
SimpleGUI::Slider(color1.g, Vec2{ 100, 80 });
SimpleGUI::Slider(color1.b, Vec2{ 100, 120 });
Circle{ 50, 100, 30 }.draw(color1);
SimpleGUI::Slider(U"Red", color2.r, Vec2{ 100, 200 });
SimpleGUI::Slider(U"Green", color2.g, Vec2{ 100, 240 });
SimpleGUI::Slider(U"Blue", color2.b, Vec2{ 100, 280 });
Circle{ 50, 260, 30 }.draw(color2);
// スライダーの値を表示、ラベルの幅 100px, スライダーの幅 200px
SimpleGUI::Slider(U"R {:.2f}"_fmt(color3.r), color3.r, Vec2{ 100, 360 }, 100, 200);
SimpleGUI::Slider(U"G {:.2f}"_fmt(color3.g), color3.g, Vec2{ 100, 400 }, 100, 200);
SimpleGUI::Slider(U"B {:.2f}"_fmt(color3.b), color3.b, Vec2{ 100, 440 }, 100, 200);
Circle{ 50, 420, 30 }.draw(color3);
// 値の範囲が 0.0~10.0
SimpleGUI::Slider(U"{:.2f}"_fmt(value1), value1, 0.0, 10.0, Vec2{ 500, 40 }, 60, 150);
// スライダーを無効化
SimpleGUI::Slider(U"{:.2f}"_fmt(value2), value2, 0.0, 10.0, Vec2{ 500, 100 }, 60, 150, false);
// 縦のスライダー
SimpleGUI::VerticalSlider(value3, 0.0, 10.0, Vec2{ 500, 160 }, 200);
SimpleGUI::VerticalSlider(value4, 0.0, 10.0, Vec2{ 560, 160 }, 200, false);
}
}
5.3 チェックボックス
チェックボックスの表示と入力の取得を実装するときは SimpleGUI::CheckBox()
関数を使うと便利です。関数にはチェックボックスのテキストや位置、幅、状態などを設定できます。SimpleGUI::CheckBox()
は値が変更されたときに true
を返します。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
bool checked0 = false;
bool checked1 = true;
bool checked2 = false;
bool checked3 = false;
bool checked4 = false;
bool checked5 = false;
while (System::Update())
{
SimpleGUI::CheckBox(checked0, U"Label0", Vec2{ 100, 40 });
SimpleGUI::CheckBox(checked1, U"Label1", Vec2{ 100, 80 });
SimpleGUI::CheckBox(checked2, U"Label2", Vec2{ 100, 120 });
// 幅 200px
SimpleGUI::CheckBox(checked3, U"Label3", Vec2{ 100, 180 }, 200 );
// 無効化
SimpleGUI::CheckBox(checked4, U"Label4", Vec2{ 100, 220 }, 200, false);
// 幅はテキストに合わせる
SimpleGUI::CheckBox(checked5, U"Label5", Vec2{ 100, 260 }, unspecified, false);
}
}
5.4 ラジオボタン
ラジオボタンの表示と入力の取得を実装するときは SimpleGUI::RadioButtons()
関数を使うと便利です。関数にはラジオボタンのテキストや位置、幅、状態などを設定できます。SimpleGUI::RadioButtons()
は値が変更されたときに true
を返します。
# include <Siv3D.hpp>
void Main()
{
size_t index0 = 0;
size_t index1 = 2;
size_t index2 = 0;
size_t index3 = 1;
size_t index4 = 0;
const Array<String> options = { U"Red", U"Green", U"Blue" };
constexpr std::array<ColorF, 3> colors = { ColorF{ 0.8, 0.2, 0.2 }, ColorF{ 0.2, 0.8, 0.2 }, ColorF{ 0.2, 0.2, 0.8 } };
Scene::SetBackground(colors[index1]);
while (System::Update())
{
SimpleGUI::RadioButtons(index0, { U"Option1", U"Option2", U"Option3" }, Vec2{ 100, 40 });
// 選択肢を Array<String> で指定
if (SimpleGUI::RadioButtons(index1, options, Vec2{ 100, 180 }))
{
Scene::SetBackground(colors[index1]);
}
// 幅 200px
SimpleGUI::RadioButtons(index2, { U"A", U"B" }, Vec2{ 400, 40 }, 200);
// 無効化
SimpleGUI::RadioButtons(index3, { U"A", U"B" }, Vec2{ 400, 140 }, 200, false);
// 幅はテキストに合わせる
SimpleGUI::RadioButtons(index4, { U"A", U"B" }, Vec2{ 400, 240 }, unspecified, false);
}
}
5.5 テキストボックス
テキストボックスを実装するときは SimpleGUI::TextBox()
関数を使うと便利です。関数にはテキストボックスの位置、幅、文字数の上限、状態などを設定できます。テキストは TextEditState
型のオブジェクトによって管理します。SimpleGUI::TextBox()
はテキストが変更されたときに true
を返します。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
TextEditState te0;
TextEditState te1;
te1.text = U"Siv3D"; // デフォルトの文字列
TextEditState te2;
TextEditState te3;
while (System::Update())
{
ClearPrint();
Print << te0.active; // アクティブかどうか
Print << te0.text; // 入力されたテキスト (String)
SimpleGUI::TextBox(te0, Vec2{ 100, 140 });
SimpleGUI::TextBox(te1, Vec2{ 100, 200 });
if (SimpleGUI::Button(U"Clear", Vec2{ 320, 200 }))
{
// テキストを消去
te1.clear();
}
// 幅 100px, 文字数を 4 文字までに制限
SimpleGUI::TextBox(te2, Vec2{ 100, 260 }, 100, 4);
// 無効化
SimpleGUI::TextBox(te3, Vec2{ 100, 320 }, 100, 4, false);
}
}
5.6 カラーピッカー
カラーピッカーは SimpleGUI::ColorPicker()
関数を使うと便利です。関数にはカラーピッカーの位置、状態などを設定できます。SimpleGUI::ColorPicker()
は色が変更されたときに true
を返します。
# include <Siv3D.hpp>
void Main()
{
Scene::SetBackground(ColorF{ 0.8, 0.9, 1.0 });
HSV color0 = Palette::Orange;
HSV color1 = Palette::Skyblue;
while (System::Update())
{
SimpleGUI::ColorPicker(color0, Vec2{ 100, 100 });
Rect{ 100, 300, 100 }.draw(color0);
SimpleGUI::ColorPicker(color1, Vec2{ 300, 100 }, false);
Rect{ 300, 300, 100 }.draw(color1);
}
}
6. キーボード入力
6.1 キーの入力状態を調べる
キーボードのキーには「Key~」と名付けられた Input
型の値が割り当てられています。
- A, B, C, ... は
KeyA
,KeyB
,KeyC
, ... - 1, 2, 3, ... は
Key1
,Key2
,Key3
, ... - F1, F2, F3, ... は
KeyF1
,KeyF2
,KeyF3
, ... - ↑, ↓, ←, → は
KeyUp
,KeyDown
,KeyLeft
,KeyRight
- スペースキーは
KeySpace
- エンターキーは
KeyEnter
- バックスペースキーは
KeyBackspace
- Tab キーは
KeyTab
- Esc キーは
KeyEscape
- PageUp, PageDown は
KeyPageUp
,KeyPageDown
- Delete キーは
KeyDelete
- Numpad の 0, 1, 2, ... は
KeyNum0
,KeyNum1
,KeyNum2
, ... - シフトキーは
KeyShift
- 左シフトキー、右シフトキーは
KeyLShift
,KeyRShift
- コントロールキーは
KeyControl
- (macOS) コマンドキーは
KeyCommand
- 「,」「.」「/」キーは
KeyComma
,KeyPeriod
,KeySlash
- 上記以外のキーは
<Siv3D/Keyboard.hpp>
を参照
Input
型の値はメンバ関数を持ち、押した瞬間であるかを .down()
, 押し続けているかを .pressed()
, 離した瞬間であるかを .up()
を使って bool
値で取得できます。
関数 | 押していないとき | 押した瞬間 | 押し続けている | 離した瞬間 | 離し続けている |
---|---|---|---|---|---|
.down() |
false | ✔ true | false | false | false |
.pressed() |
false | ✔ true | ✔ true | false | false |
.up() |
false | false | false | ✔ true | false |
# include <Siv3D.hpp>
void Main()
{
Vec2 pos = Scene::Center();
while (System::Update())
{
const double delta = (Scene::DeltaTime() * 200);
// 上下左右キーで移動
if (KeyLeft.pressed())
{
pos.x -= delta;
}
if (KeyRight.pressed())
{
pos.x += delta;
}
if (KeyUp.pressed())
{
pos.y -= delta;
}
if (KeyDown.pressed())
{
pos.y += delta;
}
// [C] キーが押されたら中央に戻る
if (KeyC.down())
{
pos = Scene::Center();
}
Circle{ pos, 50 }.draw();
}
}
6.2 キーが押されている時間を調べる
Input
の .pressedDuration()
は、その入力が押され続けている時間を Duration
型の値で返します。
押され続けている時間は .up()
が true
になるフレームまで有効です。.up()
されたときに .pressedDuration()
を調べると、そのキーが離されるまで何秒間押され続けていたかを取得できます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
ClearPrint();
Print << KeyA.pressedDuration();
if (1s <= KeySpace.pressedDuration())
{
Print << U"Space";
}
}
}
6.3 複数のキーの組み合わせ
A または B
|
を使って複数のキーを組み合わせると、そのいずれかが押されているかどうかを判定できます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
ClearPrint();
// [スペース] または [エンター] が押されている
if ((KeySpace | KeyEnter).pressed())
{
Print << U"KeySpace / KeyEnter";
}
}
}
A を押しながら B
+
を使って 2 つのキーを組み合わせると、左のキーが押されながら、右のキーが押されたかどうかを判定できます。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
// [Ctrl + C] または [Command + C] が押された
if ((KeyControl + KeyC).down()
|| (KeyCommand + KeyC).down())
{
Print << U"Ctrl + C / Command + C";
}
}
}
6.4 テキスト入力
TextInput::UpdateText()
に String
型の変数を渡すことで、テキスト入力を処理できます。TextInput::GetEditingText()
は未変換の文字入力を取得できます。
# include <Siv3D.hpp>
void Main()
{
const Font font{ 30 };
String text;
constexpr Rect area{ 50, 50, 700, 300 };
while (System::Update())
{
// キーボードからテキストを入力
TextInput::UpdateText(text);
// 未変換の文字入力を取得
const String editingText = TextInput::GetEditingText();
area.draw(ColorF{ 0.3 });
font(text + U'|' + editingText).draw(area.stretched(-20));
}
}
7. マウス入力
7.1 マウスカーソルの座標
マウスカーソルの座標は Cursor::Pos()
を使うと Point
型で取得できます。シーンが実ウィンドウサイズと異なる (チュートリアル 15 参照) 場合、Cursor::PosF()
を使うと Vec2
型で小数点数以下の座標も取得できます。
Cursor::Pos()
で取得できるマウスカーソル座標は、最後の System::Update()
の呼び出し時点での座標のため、実際画面に見えているマウスカーソルよりも古い座標を示す場合があります。
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
ClearPrint();
Print << Cursor::Pos();
Print << Cursor::PosF();
Circle{ Cursor::Pos(), 50 }.draw(Palette::Skyblue);
}
}
7.2 マウスカーソルの移動量
1 フレーム前のマウスカーソル座標は Cursor::PreviousPos()
/ Cursor::PreviousPosF()
で取得できます。1 フレーム前からのマウスカーソルの移動量は Cursor::Delta()
/ Cursor::DeltaF()
で取得できます。
Cursor::Delta() == (Cursor::Pos() - Cursor::PreviousPos())
です。
# include <Siv3D.hpp>
void Main()
{
// 円をつかんでいるか
bool grab = false;
Circle circle{ Scene::Center(), 50 };
while (System::Update())
{
if (grab)
{
// 移動量分だけ円を移動
circle.moveBy(Cursor::Delta());
}
if (circle.leftClicked()) // 円を左クリックしたら
{
grab = true;
}
else if (MouseL.up()) // マウスの左ボタンが離されたら
{
grab = false;
}
if (grab || circle.mouseOver())
{
Cursor::RequestStyle(CursorStyle::Hand);
}
circle.draw(Palette::Skyblue);
}
}
図形の .moveBy()
関数は、与えられた x, y 成分だけ自身の座標を移動します。似たような名前の .movedBy()
関数は、与えられた x, y 成分だけ座標を移動した新しい図形を返し、自身の座標は変更しません。
7.3 マウスのボタンの入力状態
マウスのボタンには、以下の Input
型の値が割り当てられています。
定数 | 対応するボタン |
---|---|
MouseL |
左ボタン |
MouseR |
右ボタン |
MouseM |
中央ボタン |
MouseX1 |
拡張ボタン 1 |
MouseX2 |
拡張ボタン 2 |
MouseX3 |
拡張ボタン 3 |
MouseX4 |
拡張ボタン 4 |
MouseX5 |
拡張ボタン 5 |
キーボードと同様に、押した瞬間であるかを .down()
, 押し続けているかを .pressed()
, 離した瞬間であるかを .up()
を使って bool
値で取得できます。
関数 | 押していないとき | 押した瞬間 | 押し続けている | 離した瞬間 | 離し続けている |
---|---|---|---|---|---|
.down() |
false | ✔ true | false | false | false |
.pressed() |
false | ✔ true | ✔ true | false | false |
.up() |
false | false | false | ✔ true | false |
# include <Siv3D.hpp>
void Main()
{
while (System::Update())
{
ClearPrint();
Print << MouseL.pressed();
Print << MouseM.pressed();
Print << MouseR.pressed();
}
}
7.4 マウスホイールの回転量
直前のフレームからのマウスホイールのスクロール量は、Mouse::Wheel()
によって double
型で取得できます。水平ホイールのスクロール量は、Mouse::WheelH()
によって double
型で取得できます。
マウスホイールのスクロール量はフレームレートに依存しないため、Scene::Delta()
で調整する必要はありません。
# include <Siv3D.hpp>
void Main()
{
Vec2 pos = Scene::Center();
while (System::Update())
{
ClearPrint();
// マウスホイールのスクロール量
Print << Mouse::Wheel();
// マウスの水平ホイールのスクロール量
Print << Mouse::WheelH();
pos.y -= (Mouse::Wheel() * 10);
pos.x += (Mouse::WheelH() * 10);
RectF{ Arg::center = pos, 200 }.draw();
}
}