SFML には基本的な図形を表すクラスがたくさんあります。
図形ごとに別々のクラスなのですが、同じ親クラスを継承している子たちです。
なので、共通している機能がたくさんあります。
共通している機能のほかに、それぞれの図形ごとに特有の機能がある、という感じです。
たとえば「円形クラス」には「半径」があるとか、
「矩形クラス」には「サイズ」があるとか、
「多角形クラス」には「辺の数」がある、などなど。
トランスフォーメーション(位置、回転、拡大)
この機能は、SFML のどのグラフィック関係のクラスにも共通してます。
なので、
別のチュートリアルでまとめて説明してます。
色
色は基本ですよね。
setFillColor() 関数で設定できます。
sf::CircleShape shape(50);
// 緑に設定
shape.setFillColor (sf::Color (100, 250, 50) );
縁取り
図形には縁取りをつけることができます。
setOutlineThickness() 関数で、縁取りの太さを。
setOutlineThickness() 関数で、縁取りの色を設定できます。
sf::CircleShape shape (50);
shape.setFillColor (sf::Color (150, 50, 250) );
// 10ピクセルでオレンジ色の縁取りにする
shape.setOutlineThickness (10);
shape.setOutlineColor (sf::Color (250, 150, 100));
![](../../../image/loading.png)
デフォルトでは、縁取りは図形の外側に向かって太くなっています(図形自体を含まない、外側の範囲)。
たとえば、半径 10 の円に、太さ 5 の縁取りをつけた場合、
全体の半径は 15、ということになります。
太さの設定値をマイナスにすると、図形の内側に向かって縁取りを太くできます。
縁取りを消すには、太さを 0 にします。
縁取りだけを描画したい場合は、図形の内側を塗りつぶす色を透明(sf::Color::Transparent)に指定します。
テクスチャ
図形にはテクスチャを貼り付けることもできます。スプライトと同じです。
setTextureRect()関数で、テクスチャのどの部分を図形に貼り付けるかを設定します。
これで設定した範囲のテクスチャが、図形内に貼り付けられます。
このやり方だと、大雑把な貼り方しかできないのですけど、
その分、簡単に使えると思うですよ。
図形の形状に合わせて貼り付ける座標を1つ1つ設定するのは面倒ですよね?
sf::CircleShape shape (50);
// サイズ 100x100 のテクスチャを 図形に貼り付ける。
shape.setTexture (&texture); // texture の型は sf::Texture
shape.setTextureRect (sf::IntRect(10, 10, 100, 100));
![](../../../image/loading.png)
注意点! 縁取りのラインにはテクスチャが貼られません。
それと、図形に色が塗られているときには、テクスチャの色と混ざります。
図形の色が真っ白に設定されているときは、テクスチャそのままの色で描画されます。
※ 訳注 ※
「図形の色が真っ白に設定されているとき」というのは、つまり、
shape.setFillColor (sf::Color::White)
ということです。
テクスチャを剥がすには setTexture(NULL) 関数をコールします。
四角形
四角形を描画したいならば、sf::RectangleShape を使うと便利です。
この子が持っている属性は1つだけ。「四角形のサイズ」です。
// サイズ 120x50 の矩形を作成
sf::RectangleShape rectangle (sf::Vector2f(120, 50) );
// サイズを 100x100 に変更
rectangle.setSize (sf::Vector2f(100, 100) );
円形
円形は sf::CircleShape で作れます。
属性は2つ。「半径」と「辺の数」です。
「辺の数」は未設定でも OK ですが、円形の「なめらかさ」を表しています。
円形の正体は、実際に厳密に「円」なのではなくて、辺がたくさんある多角形なのです。
(グラフィックカードは完璧な円形を描くことができないのです)
つまりこの属性で「辺の数」を明示的に設定できる、ということです。
円形のサイズが小さい場合は、辺の数が少なくても見た目に影響しません。
一方、大きな円を描いたり、大きく拡大した場合は、辺の数が多くないとギザギザになってしまいます。
// 半径 200 の円を作成
sf::CircleShape circle (200);
// 半径を 40 に変更
circle.setRadius (40);
// 辺の数を 100 に設定
circle.setPointCount (100);
正多角形
実は正多角形の専用のクラス、というのはありません。
でも、円形クラス sf::CircleShape の正体は、辺の多い多角形なのでしたね?
なので、辺の数をいじってあげれば、お望みの多角形が手に入ります。
辺の数が3つの「円」は「三角形」。辺の数が4つの「円」は「正方形」。
// 三角形
sf::CircleShape triangle (80, 3);
// 正方形
sf::CircleShape square (80, 4);
// 正八角形
sf::CircleShape octagon (80, 8);
凸型多角形
sf::ConvexShape クラスは最強の図形クラスです。どんな形でも作れます。(ただし、ヘコんでいる辺がないこと)
そうなのです。SFML は、ヘコみのある図形を扱えないのです。
ヘコんだ図形が必要な場合は、別の図形を組み合わせて実現してね。
さて、凸型多角形を作るには、まず頂点の数を設定します。
次に、それぞれの頂点を設定します。
// 空の図形を用意して、
sf::ConvexShape convex;
// 頂点を5つに設定
convex.setPointCount (5);
// それぞれの頂点を設定
convex.setPoint (0, sf::Vector2f(0, 0) );
convex.setPoint (1, sf::Vector2f(150, 10) );
convex.setPoint (2, sf::Vector2f(120, 90) );
convex.setPoint (3, sf::Vector2f(30, 100) );
convex.setPoint (4, sf::Vector2f(0, 50) );
頂点を設定する順番に注意。
時計回りか、反時計回りに設定すること。
デタラメな順番に設定すると、おかしなことになるよ!
![](../../../image/loading.png)
sf::ConvexShape(凸型図形)というぐらいなので、その名の通り凸型の図形しか扱えない……と見せかけて、実際にはそうでもありません。
実際のところ守らなければならない条件は何かと言うと、
仮に図形の重心から各頂点へ線を引いた場合、それらの線は一定の順番で引かれること、です。
先に引いた線を逆向きにジャンプしてはいけません。
内部実装としては、sf::ConvexShape は
トライアングルファン
で生成されます。
ということは、トライアングルファンで作ることができるような形状の図形であれば、sf::ConvexShape を使って作成できる、ということです。
なので、凸型図形(ConvexShape)と言いつつ、たとえば星型を描くことなんかが出来てしまったりします。
線
線を描くための専用のクラスはありません。
え、どうして?
それはですね、線に幅があるなら、それは線じゃなくて四角形だし、
幅がないなら、クラスを使うまでもないから、なのです。
幅のあるライン:
sf::RectangleShape line (sf::Vector2f(150, 5) );
line.rotate (45);
幅のないライン:
sf::Vertex line[] =
{
sf::Vertex (sf::Vector2f(10, 10) ),
sf::Vertex (sf::Vector2f(150, 150) )
};
window.draw (line, 2, sf::Lines);
![](../../../image/loading.png)
幅のないラインを描くのには
頂点リストを使ってます。
詳しくは、そちらのチュートリアルを見てね。
SFML の図形クラスを拡張して、自家製の図形クラスを作れます。
sf::Shape クラスを継承して、次の2つの関数をオーバーライドしてね。
- getPointCount(): 頂点の数を取得
- getPoint(): 頂点を取得
それと、自家製図形の頂点が変更されるたびに、update()関数を呼んでね。
ベースクラスが内部のジオメトリを更新するために必要だよ。
では、自家製図形クラスのサンプルです。名付けて「楕円クラス」。
class EllipseShape : public sf::Shape
{
public :
explicit EllipseShape(const sf::Vector2f& radius = sf::Vector2f(0, 0)) :
m_radius(radius)
{
update();
}
void setRadius(const sf::Vector2f& radius)
{
m_radius = radius;
update();
}
const sf::Vector2f& getRadius() const
{
return m_radius;
}
virtual unsigned int getPointCount() const
{
// ここでは値を固定してますが、必要ならクラスの属性にしてもよいですね。
return 30;
}
virtual sf::Vector2f getPoint(unsigned int index) const
{
static const float pi = 3.141592654f;
float angle = index * 2 * pi / getPointCount() - pi / 2;
float x = std::cos(angle) * m_radius.x;
float y = std::sin(angle) * m_radius.y;
return sf::Vector2f(m_radius.x + x, m_radius.y + y);
}
private :
sf::Vector2f m_radius;
};
図形ごとにアンチエイリアスの設定をすることはできません。
ウィンドウを作るときに、グローバルな属性として設定してね。
sf::ContextSettings 構造体の中に、設定項目があります。
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window (
sf::VideoMode(800, 600),
"SFML shapes",
sf::Style::Default,
settings
);
![](../../../image/loading.png)
アンチエイリアスが使えるかどうかはグラフィックカードに依存してます。
もしかしたら、アンチエイリアスはサポートされてないかもしれません。
または、ドライバの設定でオフになってるかもしれません。