最初に
僕はここ最近まで、SDL2を使っていたのですが、
諸事情によりSFMLを使い始めました
理由は、
SDL2のVersion 2.0.5までは問題なかったのですが、
Version 2.0.7ではSDL_mixerのMix_Init関数で正しく初期化ができない、という
バグ?仕様変更?があったため、
SDL2からの移行を考えて、せっかくなので、もっと使いやすいライブラリを探していたら、
SFMLというライブラリが使いやすそうなので、これに移行しようと考えました
** 追記
** SDL2だけでなく、
SDL_imageと、
SDL_mixerと、
SDL_ttfも新バージョンにしないと、
正しく動作しないようでした**
ずっとこれに気が付かず、バグだと思ってました
**
** さらに追記
** SDL2 Ver 2.0.5と Ver2.0.7では内部使用が違うためか、
Ver 2.0.7では、Mix_Init関数にMIX_INIT_FLACを指定したときに、
エラーが出るということが分かりました**
**
SFMLのことはまだわからないことだらけですので、
日本語の情報がかなり少ないようなので、
情報を少し書いてみようと思いました
説明下手ですが、どうか
SFML関連のURL
↓SFML公式サイト
https://www.sfml-dev.org/
↓SFMLのダウンロードページ(C++用)
https://www.sfml-dev.org/download/sfml/2.4.2/
↓SFMLのバインディング集
https://www.sfml-dev.org/download/bindings.php
↓SFML.netのダウンロードページ
https://www.sfml-dev.org/download/sfml.net/
↓数少ないと思われるSFMLの日本語情報
http://www.site-a.info/programming/sfml/SFML_unofficialTranslation.html
準備
C#ユーザーなので、SFML.netというライブラリを選びました
なので、SFML.netを使う前提でこの記事を書きます
1・SFML.netのダウンロードページにある、
All compilers - 32-bitか、
All compilers - 64-bitのどちらかをダウンロードします
(必要ならば、両方ダウンロードしても構いません)
2・ダウンロードしたZip内の
extlibsフォルダとlibフォルダ内のDLLを全て
プロジェクトの出力先フォルダに入れます
(lib内に含まれているDLLはすべて、C#用のライブラリです)
3・以下の4つのDLLをプロジェクトの参照に追加します
sfmlnet-audio-2.dll
sfmlnet-graphics-2.dll
sfmlnet-system-2.dll
sfmlnet-window-2.dll
(上記DLL全てlib内のフォルダにあるDLLです)
これで実行に必要なライブラリの準備ができました
ウィンドウを表示するまで
SFML.netを動かすための
最低限のコードです
using SFML.Graphics;
namespace Game_Project1 {
static class Program {
static void Main() {
//ウィンドウを作成
var window = new RenderWindow(
new SFML.Window.VideoMode {
Width = 400, //横幅
Height = 400, //縦幅
},
"テストプログラム", //表示する文字列
SFML.Window.Styles.Close //ウィンドウ右上に×ボタンを設置
);
//window.Closedイベントで、
//×ボタンが押されたときの処理を追加できる
//
//window.Close関数を呼ぶとウィンドウを閉じる
window.Closed += (sender, e) => window.Close();
//メインループを開始
while (window.IsOpen) {
//ウィンドウのイベント処理を実行
//(必ずこれを実行すること!)
window.DispatchEvents();
}
}
}
}
ビットマップ描画
using SFML.Graphics;
namespace Game_Project1 {
static class Program {
static void Main() {
var window = new RenderWindow(
new SFML.Window.VideoMode {
Width = 400,
Height = 400,
},
"テストプログラム",
SFML.Window.Styles.Close
);
window.Closed += (sender, e) => window.Close();
//画像を読み込み
var image = new Image("0.png");
//画像をテクスチャに変換
var texture = new Texture(image);
//描画用オブジェクトを作成
var sprite = new Sprite(texture);
while (window.IsOpen) {
window.DispatchEvents();
//ウィンドウを黒で塗りつぶす
window.Clear();
sprite.Position = new SFML.System.Vector2f(20, 20);
//画像の描画
window.Draw(sprite);
//描画したものを表示
window.Display();
}
}
}
}
テキスト描画
比較的楽なテキスト描画コードです
using SFML.Graphics;
namespace Game_Project1 {
static class Program {
const string fontName = "0.ttf";
const string textStr = "表示テスト";
static void Main() {
var window = new RenderWindow(new SFML.Window.VideoMode {
Width = 400,
Height = 400,
}, "テストプログラム", SFML.Window.Styles.Close);
window.Closed += (sender, e) => window.Close();
//フォント読み込み
var font = new Font(fontName);
//テキスト描画用オブジェクトを作成
var text = new Text(textStr, font);
while (window.IsOpen) {
window.DispatchEvents();
//ウィンドウを黒で塗りつぶす
window.Clear();
text.Position = new SFML.System.Vector2f(20, 20);
//テキストを描画
window.Draw(text);
//描画したものを表示
window.Display();
}
}
}
}
テキスト描画(もう一つの方法)
やや面倒な方法ですが
こちらのほうが柔軟性が高めです
using SFML.Graphics;
namespace Game_Project1 {
static class Program {
const string fontName = "0.ttf";
const string textStr = "表示テスト";
const uint textSize = 32;
static void Main() {
var window = new RenderWindow(new SFML.Window.VideoMode {
Width = 400,
Height = 400,
}, "テストプログラム", SFML.Window.Styles.Close);
window.Closed += (sender, e) => window.Close();
//フォント読み込み
var font = new Font(fontName);
foreach (var i in textStr) {
//フォントテクスチャに、
//指定した文字を書き込む
font.GetGlyph(i, textSize, false);
}
//フォントからテクスチャを作成
var texture = font.GetTexture(textSize);
var sprite = new Sprite(texture);
while (window.IsOpen) {
window.DispatchEvents();
//ウィンドウを黒で塗りつぶす
window.Clear();
for (var i = 0; i < textStr.Length; i++) {
//表示したい文字を描画するためのデータを取得
var glyph = font.GetGlyph(textStr[i], textSize, false);
sprite.Position = new SFML.System.Vector2f(20 + textSize * i, 20);
sprite.TextureRect = glyph.TextureRect;
//文字列を表示
window.Draw(sprite);
}
//描画したものを表示
window.Display();
}
}
}
}
音再生
SFMLでの音再生で使えるファイルの拡張子は
WAVとOGGの2つが使えることを確認
MIDファイルとMP3のファイルの再生はSFMLでは不可
SDL2ではできるのに・・・
using SFML.Graphics;
using SFML.Audio;
namespace Game_Project1 {
static class Program {
static void Main() {
var window = new RenderWindow(
new SFML.Window.VideoMode {
Width = 400,
Height = 400,
},
"テストプログラム",
SFML.Window.Styles.Close
);
window.Closed += (sender, e) => window.Close();
//音再生用オブジェクトの作成
//
//WAVファイルとOGGファイルの再生が
//できることを確認
//
//MIDとMP3の再生は残念ながらできない
//(読み込み時にエラーが出る)
var music = new Music("0.wav");
//Loopプロパティでループ再生できるかどうかを指定できる
//(ただし、Play関数を呼ぶ前に指定しないと反映されない)
//初期値はfalse
//
//falseならループしない
//trueならループする
music.Loop = true;
//音再生の開始
music.Play();
while (window.IsOpen) {
window.DispatchEvents();
}
}
}
}
フレームレートの実装
上記例では60Fpsになっていませんでした
ゲームループを60Fpsにするサンプルです
using SFML.Graphics;
using SFML.Window;
using SFML.System;
namespace Game_Project {
static unsafe class Program {
const uint fps = 60;
static void Main() {
var window = new RenderWindow(new VideoMode(320, 320), "", Styles.Close);
window.Closed += (sender, e) => window.Close();
//フレームレートの指定
//引数はuint型
//
//(この例では60 Fpsを指定)
window.SetFramerateLimit(fps);
var texture = new Texture("0.png");
var sprite = new Sprite(texture);
while (window.IsOpen) {
window.DispatchEvents();
window.Clear();
if (Mouse.IsButtonPressed(Mouse.Button.Left)) {
sprite.Position += new Vector2f(1, 0);
}
window.Draw(sprite);
//この関数を使うと、
//window.SetFramerateLimit関数で指定したFpsになる
//
//(この例では60 Fpsになる)
window.Display();
}
}
}
}
Spriteを使わずに描画するサンプルコード
Spriteクラスを使わずにテクスチャ描画をする方法が分かったので
*追記*
Spriteを使って描画するのと
自前で描画するのとでは処理速度にあまり差がありませんでした
using SFML.Graphics;
using SFML.Window;
using SFML.System;
namespace Game_Project {
static unsafe class Program {
const uint fps = 60;
static void Main() {
var window = new RenderWindow(new VideoMode(320, 320), "", Styles.Close);
window.Closed += (sender, e) => window.Close();
window.SetFramerateLimit(fps);
var texture = new Texture("0.png");
var sprite = new Sprite(texture);
sprite.Position = new Vector2f(20, 20);
var spriteTester = new SpriteTester(texture);
spriteTester.X = 20;
spriteTester.Y = 50;
while (window.IsOpen) {
window.DispatchEvents();
window.Clear();
//既定のスプライトを表示
window.Draw(sprite);
//自作スプライトを表示
window.Draw(spriteTester);
//スプライトを使わずに画像を表示
TextureDrawer.Draw(window, texture, 20, 100);
window.Display();
}
}
}
//スプライトを使わずに画像を表示するためのクラス
static class TextureDrawer {
public static Vertex[] Vertexs { get; } = new Vertex[4];
public static void Draw(RenderTarget target,Texture texture, float x, float y){
//描画データの指定
var transform = Transform.Identity;
//描画範囲を指定
var left = x;
var right = x + texture.Size.X;
var top = y;
var bottom = y + texture.Size.Y;
//左上
Vertexs[0].Position = new Vector2f(left, top);
Vertexs[0].TexCoords = new Vector2f(0, 0);
//左下
Vertexs[1].Position = new Vector2f(left, bottom);
Vertexs[1].TexCoords = new Vector2f(0, texture.Size.Y);
//右上
Vertexs[2].Position = new Vector2f(right, top);
Vertexs[2].TexCoords = new Vector2f(texture.Size.X, 0);
//右下
Vertexs[3].Position = new Vector2f(right, bottom);
Vertexs[3].TexCoords = new Vector2f(texture.Size.X, texture.Size.Y);
for (var i = 0; i < Vertexs.Length; i++) {
//new Color(255, 255, 255, 255)
//で画像の色変更なし
Vertexs[i].Color = new Color(255, 255, 255, 255);
}
var states = default(RenderStates);
//これも指定しないと表示されない
states.BlendMode = BlendMode.Alpha;
//描画データをstatesに指定する
states.Transform = transform;
//テクスチャをstatesに指定する
//
//states.Textureがnullの場合は矩形が表示される
states.Texture = texture;
//描画開始
target.Draw(Vertexs, 0, (uint)Vertexs.Length, PrimitiveType.TrianglesStrip, states);
}
}
//自作スプライト
sealed class SpriteTester : Drawable {
public Vertex[] Vertexs { get; } = new Vertex[4];
public Texture Texture { get; }
public Transform Transform { get; set; }
public float X { get; set; }
public float Y { get; set; }
public float Width { get; set; }
public float Height { get; set; }
public float Left => X;
public float Right => X + Width;
public float Top => Y;
public float Bottom => Y + Height;
public SpriteTester(Texture texture) {
//テクスチャの指定
Texture = texture;
//描画データの指定
Transform = Transform.Identity;
Width = Texture.Size.X;
Height = Texture.Size.Y;
}
public void Draw(RenderTarget target, RenderStates states) {
//左上
Vertexs[0].Position = new Vector2f(Left, Top);
Vertexs[0].TexCoords = new Vector2f(0, 0);
//左下
Vertexs[1].Position = new Vector2f(Left, Bottom);
Vertexs[1].TexCoords = new Vector2f(0, Height);
//右上
Vertexs[2].Position = new Vector2f(Right, Top);
Vertexs[2].TexCoords = new Vector2f(Width, 0);
//右下
Vertexs[3].Position = new Vector2f(Right, Bottom);
Vertexs[3].TexCoords = new Vector2f(Width, Height);
for (var i = 0; i < Vertexs.Length; i++) {
//new Color(255, 255, 255, 255)
//で画像の色変更なし
Vertexs[i].Color = new Color(255, 255, 255, 255);
}
//描画データをstatesに指定する
states.Transform = Transform;
//テクスチャをstatesに指定する
//
//states.Textureがnullの場合は矩形が表示される
states.Texture = Texture;
//描画開始
target.Draw(Vertexs, 0, (uint)Vertexs.Length, PrimitiveType.TrianglesStrip, states);
}
}
}
最後に
SDL2よりも必要なコードがかなり短く
楽そうですね
ここまででSDL2よりかなり使いやすく感じられました
SDL2は初期化から描画までにもっとコードを書く必要がありますし
もう少し使い方を探して
SDL2からの移行を目指します
・・・SDL2のversion 2.0.8では初期化関数が元通りになるかなあ・・・
一部、SFMLではなくSDL2のこと書いているけど、
これのためにページを作るほどではないと思ったので、
ここに書きました
~~ SDL2 2.0.7のエラーの原因がわかったため、
SFMLに移行する理由がなくなりました~~
でもこの記事は残します
数少ないSFML情報なので