スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

C++でオープンソースのライブラリを作ってます、手伝って下さい!

この記事は、C++ Advent Calendar 2013、26日目の参加記事です。

・はじめに
 記事タイトル通りです。

 今年のGW頃からマルチプラットフォームなC++のゲーム用ライブラリを作り初めたのですが、規模が思ったより大きくなったので、せっかくだし年内ぐらいで目処をつけてオープンソースにして開発者を募集し楽をしようと思っていた矢先、たまたまCalenderの事を知って参加した次第です。

 ということで、2D用のゲームライブラリを作った経験のまとめと、開発者の募集宣伝を書きます。
ライブラリを作ってる人がいたら参考になれば幸いです。

●他のライブラリの特徴

 自力で開発しなくても、C++のゲーム用ライブラリは既にいくつもあります。
有名所だと、DXライブラリや、cocos2d-x、Qt、あと13日の記事のSiv3Dなんかもそうですね。
C++やプログラミング言語に限定しなければ、XNA、HSP、Unity、ツクールやらウディタ等もあります。

 既にあるなら別に自分で作る必要は無く、利用人数も多くメンテナンスされているライブラリを使うべきで車輪の再発明は避けるべきだ!となりかねないので、ライブラリを作る前にそれ系のライブラリを調べました。

※個人的感想です、DXライブラリとSDL以外はチュートリアルぐらいしかしていません
・DXライブラリ
 日本ではわりとメジャーなC++のライブラリでDirectX9を使用しておりマルチメディアの機能がひと通り揃っています。
初学者向けでC++のうちCと共通する文法だけ分かっていれば(さらにポインタの知識すら不要)、大体使えるような設計になっているのが長所であり、短所でC++と言うよりスクリプトっぽいです。
 開発が長く続いているのでライブラリとしては枯れているのも長所ですが、互換性の問題で残っている不要な機能や、文字描画に改行を使えないなど細かい不満もあるので、C++erが使うならラッパーして使いたいです。

・cocos2d-x
 マルチプラットフォームなC++のライブラリで、初心者でも使えるように作られているようです。
x-codeをメイン開発環境にする必要がありそうだったのと、イベントドリブンな設計が合いませんでした。

・SDL2
 わりと以前からあるライブラリで、以前はQtのようにLGPLでしたが、最近ライセンスがzlibに変わった上、タブレット端末に対応したマルチプラットフォームのライブラリです。
 C++でなく、CのライブラリなのでC++erが使う場合ラッパーするのが必須で、機能が少ないのでOpenGLを組み合わせて使う必要があり結構面倒です、海外では結構使われているようですが、日本語情報は古いか少ないです。

・siv3D
 自分が調べた時はリファレンスがなく、現在作成中のようです、それでもSDL2よりは分かりやすいですね。
DirectX11を使っているそうなので、DXLibより高速だったりしそうです。XP対応してないのはもういいですよね。
 何故かワイド文字列だらけなのと、多機能だけど実際必要かどうか微妙な機能(色を文字列で指定とか)や一部の人しか使わない機能(Kinect対応等)が多いので、様子見してます。
 初心者向けを目指しているらしいです。

 他にも色々調べたんですが省きます。

●初心者向けにしない
 ツクールやUnityはいわずもがな、既存のライブラリは、初心者を意識して作っているわけです。
利用者を増やすのであれば、当然そうした方がいいんですが、C++erからするとラップしないと微妙に使いにくくあれです。

 という事で、既存のライブラリをラッパーして初心者向けの要素を消せば、最低限の作業で自分にとって使いやすいライブラリがDirectXや、OpenGL等の低レベルな描画ライブラリやOSの勉強せずに作れそうだと思い作り始めました。
実際はそう簡単ではありませんでしたが・・・

●ライブラリの設計について
 他のライブラリを参考にしつつ、設計を考えた所、大体下の図のような設計に落ち着きました。
ライブラリ設計イメージ

「マルチメディア」は描画、音声再生、入力等、ハードウェアよりでプラットフォーム毎に違う処理を実装します。

「どんなゲームでも使うフレームワーク」は当たり判定や、アニメーション等、プラットフォーム非依存な機能や、ゲーム別の拡張フレームワークのインターフェースとなるような機能を実装します。

「ゲーム別の拡張フレームワーク」はSTGとRPGなどではオブジェクト管理の仕組みやら、必要な機能が変わるので、それらは交換可能にして何種類か用意し、さらにその上にレベルデザインをしていく感じです。レベルデザインはライブラリには出来ません。

「ユーティリティ」はシングルトンな乱数エンジンのクラスとか、暗号化して保存できるファイル入出力クラスとか、独立性が高くて便利なクラスとかです。

 こういう設計だと、例えばSDLが1.3から2に変わるみたいな事があっても影響範囲が小さいですし、後からOSが増えて別のライブラリを使う必要が出た場合もプラットフォーム対応が比較的楽です。
 STGでもRPGでも会話シーンは似たような感じなので、その部分をアドベンチャーゲームのフレームワークとして設計すれば、使い回しやすいと言った事も考えています。

●マルチメディア
 1からやる場合や処理を最適化したい場合は、マルチメディアの部分をDirectXやOpenGLのラッパーとして書く必要があります。そこまで速度を気にしないのであれば、DXLibとSDLをラッパーする構成で良いでしょう。
 DXLibの2D機能をラッパーするだけなら、1~2週間もあれば大体出来ますし、SDLも一ヶ月もあれば十分出来るんじゃないかと思います。あとはヘッダーのパスを切り替えるなり、プリプロセッサを使うなりして内部ライブラリを切り替えれば良いです。
 
 マルチメディア部分は誰が設計しても似たような機能になると思います。行き当たりでライブラリを作ると際限なくクラスや関数が増えるので、あまり使わないであろう機能は実装しないか見つけたら消すのが大事だと思います。

●どんなゲームでも使うフレームワーク
 この部分は実装はそれ程難しくありませんが、必要な機能の見極めや、共通機能にするかジャンル別の拡張機能にするか、どこまで拡張可能にするかの判断が難しいです。
cocos2d-xにはこの部分がありますが、DXLibやSDLにはありません。

 一回で完成させずにサンプルゲームを作って設計を試しながら、改善していくとわりとまとまっていくと思います。

●ゲーム別の拡張フレームワーク
 今作っている途中です、とりあえず久しぶりにSTGを作りたくなったので、STG用のフレームワークを作っています。
その次はタワーディフェンスのフレームワークを作るつもりです。

 どんなゲームも同じフレームワークにするのは、RPGツクールでADVを作れるように無理では無いんですが、出来ればゲーム毎に設計した方が良いでしょう。

 ここまでくるとC++以外にゲーム制作に関するノウハウが重要になってくるので、訓練されたC++erであっても良い設計をするのは難しいです。

●実際の設計
 マルチメディアの設計は特に書く事がないのと、STGフレームワークは未完成なので、中間層をどのように設計したり追加したか一部を解説します。
・Anime

//画像を分割して読み込む
Anime animeA = Anime("image.bmp", 9, 3, 3, 5);
//アニメーション
Anime::Player playerA(&animeA);

while (System::Update())
{
playerA.UpDate();
player.GetFrame()->Draw(10, 10);
if (Input::key.Return.on) break;//Enterで終了
}

 image.bmpを9分割して読込、5回Updateする度に、次のコマを表示するような感じで、アニメーションします。
このようなクラスがないとコマ送りアニメが結構面倒です、実装は簡単ですし必要な人は多いと思います。
だれでも思いつきそうですが、思いつかないのがライブラリを作る難しさです。

・ISpeed

//Easeing関数的な実装
double GetLiner(double 経過時間, double 初速, double 使わない引数A, double 使わない引数B)
{
return 初速;
}

double GetAccel(double 経過時間, double 初速, double 加速度, double 使わない引数)
{
return 初速 + 経過時間 * 加速度;
}

double GetWave(double 経過時間, double 速度, double 角速度, double 初角)
{
return std::abs(std::sin(経過時間*角速度 + 初角) * 速度);
}

//引数が多いので使えない
double GetWave2(double 経過時間, double 速度, double 加速度, double 角速度, double 初角)
{
return std::abs(std::sin(経過時間*角速度 + 初角) * 速度 + 経過時間 * 加速度);
}

bool SampleSpeedA()
{
using namespace SDX;
System::Initialise("sample", 600, 400);

//図形を宣言
Circle circle(0, 50, 10);
Rect rect(0, 150, 20, 20);
Line line(0, 250, PAI / 2, 20, 5);

double(*pfunc[3])(double,double,double,double);

pfunc[0] = GetLiner;
pfunc[1] = GetAccel;
pfunc[2] = GetWave;

int time = 0;
while (System::Update())
{
++time;
//図形を移動する
circle.Move(pfunc[0](time,1,0,0), 0);
rect.Move(pfunc[1](time, 0, 0.01, 0), 0);
line.Move(pfunc[2](time, 2, 0.1, 0), 0);

//図形を描画する
circle.Draw(Color::White, 255);
rect.Draw(Color::White, 255);
line.Draw(Color::White, 255);

if (Input::key.Return.on) break;//Enterで終了
}

System::End();
return true;
}


//自分がした設計
bool SampleSpeedB()
{
using namespace SDX;
System::Initialise("sample", 600, 400);

//位置と形を持った型
std::vector> shapes;
shapes.emplace_back(new Circle(0, 50, 10));
shapes.emplace_back(new Rect(0, 150, 20,20));
shapes.emplace_back(new Line(0, 250, PAI/2 , 20, 5));

//変化する数値を表す型
std::vector> speeds;
speeds.emplace_back(new Speed::Liner(1));//等速
speeds.emplace_back(new Speed::Accel(0,0.01));//加速する
speeds.emplace_back(new Speed::Wave(2, 0.1, 0));//平均√2で進む

while (System::Update())
{
for (int i = 0; i < 3; ++i)
{
//速度を更新する
speeds[i]->Update();

//図形を移動させる
shapes[i]->MoveA(speeds[i]->Get() ,0);

//描画する
shapes[i]->Draw(Color::White, 255);
}

if (Input::key.Return.on) break;//Enterで終了
}

System::End();
return true;
}

 2日目の記事と似てるようで違う、時間経過で変化していく数値の型を作ります。
 上の設計だと、引数の種類や数がバラバラになるので分かりにくいです、下の設計であれば、クラスによって引数の種類や数を変えれるので楽です。どっちがいいかは場合によるんですが、これも思いつけば簡単に実装出来ますね。

 もし自分が作るライブラリが初心者向けであれば、下のサンプルは長い解説を書く必要があるでしょう。ポリモーフィズムがどうとか、スマートポインタがどうとか、解説するのは大変なので、上の設計にするかもしれません。初心者を意識せずに作ると言うのはこういう事でした。別に変な事はしてないです。

 どっちのクラスも機能としては単純で実装も簡単です。
でも思いつかなければ実装出来ません、この難しさを伝えるのが難しいですね。
言語は文法を覚えても使えるようにはならないわけです。

●その他の作業
 ライブラリを作ったら、即公開出来るかというとそうは行きませんでした。

 まずは、ドキュメントです。
 今回は初心者向けに作らないと決めたのですが、それでも全てのクラスに十数行のサンプルコードを付けて、
関数に一行以上のコメントをつけ、導入方法がかかれたリファレンスを作る必要があり、何日かかかりました。
引数に対してコメントが合計数千行必要だったので、引数をリファクタリングして全て日本語にしました。
さらに初心者向けならC++の解説や、テトリスの作り方みたいな物や、画像付きのアレやらこれやら、自分には無理です。

 オープンソースにするなら、コーディング規約も必要でした。
関数やクラスの命名規則は当然として、{}の改行の仕方とか、○○は使うなとか、日本語識別子の利用範囲や、デバッグビルドを考えて++iと書くのは当然としても、シフト演算や三項演算子まで徹底するのか等、細かい部分は開発者が集まるかも不明なので決めていませんが、最低限の部分を決めるだけで何日もかかりました。

 そしてレビューです。
 C++erの友人がいないので「一ヶ月程度開発を停止する」→「久しぶりに見たコードが他人のコードのように見える」→「レビューする」と言った工程を2回やり、大規模な破壊的変更を2回行う必要がありました。

 さらにサンプルゲームを作りました。
 そこそこの規模のゲームを作らないと、ライブラリの設計の不備や問題点が見つからないので大変です。
ライブラリの設計にミスがあればサンプルゲームも作り直しです、サンプルは1から作りなおしていますが未完成です。

 ライブラリの実装が簡単だと思える程、それ以外の作業は大変でした。

 ただ最初は自分が読まないドキュメントを書くのは無駄だろうと思ってましたが、公開すると言う意識を持ちだしてからコードの可読性が飛躍的に向上したので、開発者が集まらなかったとしても公開してよかったと思います。

 オープンソース開発も初めてなので、オープンソースの育て方とか読んで勉強したりもしました。

●開発者募集中
 SDXフレームワーク
 という事で最後に宣伝です。
 上のページでライブラリの配布と、開発者募集をしています。
詳しくは配布しているmanualをダウンロードして下さい。

 ライブラリの規模はそこまで大きくないので、一人開発者が増えるだけでも相当助かります。
デバッグも100人ぐらい利用者が集まれば十分な成果が出ると思っています。

・特徴
 内部でDXLibとSDLを切り替えてマルチプラットフォームの2D用ライブラリを作っています。
VisualStudio以外に環境を作っていないので、今のところそれ以外はまだ対応していません。
現在破壊的変更は少なくなっていますが、今後絶対無いとは言い切れません。

 元はDXライブラリのラッパーとして作った経緯があり、DXライブラリを使っている人なら命名規則が似ているので使いやすいかと思います。2D機能に関しては、殆ど全ての機能をラッパー済みです。

 SDL+OpenGLでマルチプラットフォーム向けに機能を強化出来るので、その辺りに詳しい人が参加してくれるとありがたいです。3Dを追加すると規模が大きくなりすぎるのと、自分は3Dを使わないのでもし3Dがやりたい人がいたらforkするかもしれません。
 普通に利用して、コードレビューやデバッグをして貰うのも歓迎です。

 不明な点がありましたら、この記事のコメントなり、コミュニティの掲示板で気軽にご質問下さい。

●まとめ
 ライブラリを作るのは大変だけど、勉強になるので車輪の再発明しても良いと思いました。
いくつか役に立たないライブラリが作られようが大した損失ではないと思います。

 コードを公開する意識を持つと可読性が向上するので、開発者が集まらなくてもオープンソースは良いですね。

 ライブラリ作るより、ゲーム作る方が楽しいです。
スポンサーサイト

コメントの投稿

Secre

プロフィール

(´・@・)

Author:(´・@・)
ゲームの製作日誌です
 コメントは返事するけど、拍手コメントは返さない事があります。

メール:mr.dagonn★gmail.com
★を@に変えて下さい

twitter(更新情報をつぶやくbot)
アカウント

●公開しているゲーム
Vector作者ページ

●アンケート
FBSアンケート

TDアンケート

●バグ報告/質問/感想 掲示板
掲示板

●製作中のゲームライブラリ
SDXフレームワーク

●3DSのフレンドコード
2363-5987-4359
スマブラとかやってる

●自由ソフトウェア財団の賛助会員です
[FSF Associate Member]

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
検索フォーム
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。