「D言語ゲーム開発入門」の編集履歴(バックアップ)一覧はこちら
D言語ゲーム開発入門 - (2006/10/25 (水) 16:40:53) の1つ前との変更点
追加された行は緑色になります。
削除された行は赤色になります。
*D言語ゲーム開発入門
**コレは何?
D言語勉強中のkenmoが、D言語でのゲーム開発情報をメモしていくページです。
-目次
----
#contents
**D言語の特徴
***メリット
-ランタイムのいらない、コンパクトな実行ファイルが作れる
⇒JavaとかC#とかRubyとかPythonにはないメリットだぞ。
-ガベコレがある
⇒C/C++のメモリ管理に泣かされていた日々とはサヨナラです。
-クラスを使わなくても良い
⇒Cライクな書き方でもOK!モダンな書き方もできるぞ!
-コンパイルが爆速
⇒スクリプト言語なみに速いぞ
***デメリット
-情報が少ない
⇒ある程度出揃っていますが、まだ初心者向きの情報がないです
-未だベータ版
⇒2006/10/23現在のコンパイラのバージョンは、0.172です。仕様が結構変わる。先は長いぞー
-ライブラリ作るのがめんどい
⇒SDLなりOpenGLなりライブラリを自作しないと、ゲーム作るのは大変。そこの苦労はC/C++とかと一緒。
-統合環境がまだまだ
⇒VisualStudioやEclipseと比べると、デバッガやリファクタリングがやりにくい・できないのが少しつらいです。
今のところ、結局テキストエディタでコード書いて、コマンドプロンプトからビルド、、という流れに落ち着きます。
個人的には、C+SDLとか、C+DirectXとか、C+OpenGLなりやっていないとツライかな、という気がします。
ということで、そこらへんをやったことある人が
「そろそろD言語はじめっかなー?」
と思っている人が対象の内容となっております。
**インストール~Hello, world.
とりあえず、インストールして「Hello, world.」を出すまで。
***コンパイラとリンカのインストール
http://www.kmonos.net/alang/d/dcompiler.html
ここから、
-Win32版 dmd.zip (D コンパイラ)
と
-Win32版 dmc.zip (リンカなどの小物ツール)
をダウンロード。
dmd.zipを解凍すると、「dm」と「dmd」ができる。
とりあえず、「C:\work\D\」に置いてみます。(※「デスクトップ」とか「C:\Program Files」とかの日本語パス、半角スペースがあるところに置いちゃダメ)
これでインストール完了。
***Hello, world.
void main()
{
printf("Hello, world.");
}
こんなプログラムを書いて、「test.d」で保存。
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd test.d
pause
で、こんなバッチファイル(例えば「conv.bat」)を書いて保存。
バッチファイルをダブルクリックで、コンパイル。
「test.exe」ができれば、ビルド完了。
コマンドプロンプトから、「test.exe」を実行すると、
Hello, world.
と出れば、OKです。
**SDLをインストール~動かす
[[D - porting>http://shinh.skr.jp/d/porting.html]]から、「SDL. Based on DedicateD's.」の「SDL」のリンクをぺちっと押して、「SDL.zip」をダウンロード。
解凍してできた「SDL」フォルダを「C:\work\D\」に配置。
「SDL」フォルダ内にある、「SDL.dll」と「SDL.lib」をコピーして、「C:\work\D\」に移動します。
これでインストール完了。
***SDLを動かす(画面を出す)
import SDL;
void main()
{
// 初期化ー
if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
throw new Error("Couldn't initialize SDL");
}
// 640x480で。デフォルトのbpp。ソフトウェアで頑張る
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
if ( screen == null ) {
throw new Error("Couldn't set 640x480 video mode");
}
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
SDL_Quit();
}
これを「test.d」で保存。
&color(red){(※文字コードを「UTF-8」にして保存します)}
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd test.d -ISDL SDL.lib
pause
こんなバッチファイルを書いて、ビルド。
ちなみに、
dmd test.d -ISDL SDL.lib
の部分は、
「-ISDL」はインポートディレクトリ「SDL」の指定で、
「SDL.lib」はライブラリをスタティックリンクしています。
それで、できたtest.exeを実行して、画面が出れば、とりあえずOKです。
**画像を表示
BMPを読み込んで描画します。(エラー処理は省略)
import SDL;
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// ビットマップ読込
SDL_Surface *image = SDL_LoadBMP("hell.bmp");
// 転送
SDL_BlitSurface(image, cast(SDL_Rect*)0, screen, cast(SDL_Rect*)0);
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
SDL_Quit();
}
**マウスイベント取得
import SDL;
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
// マウス押下イベント取得
case SDL_MOUSEBUTTONDOWN:
switch (e.button.button)
{
case SDL_BUTTON_LEFT:
SDL_WM_SetCaption("push left", null);
break;
case SDL_BUTTON_RIGHT:
SDL_WM_SetCaption("push right", null);
break;
default:
break;
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
SDL_Quit();
}
**マウスイベントの取得方法2
わんきちさんのリクエストにお答えして、マウスのイベントをコールバックで取る方法を。
import SDL;
extern (C) Uint32 handler(Uint32 interval, void* param)
{
SDL_PumpEvents(); // イベントをキューにセット
SDL_Event e;
if(SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN)) < 1) {
return interval;
}
printf("SDL_MOUSEBUTTONDOWN --> ");
switch(e.type)
{
case SDL_MOUSEBUTTONDOWN:
switch(e.button.button)
{
case SDL_BUTTON_LEFT:
printf("SDL_BUTTON_LEFT\n");
break;
default:
printf("else\n");
break;
}
break;
default:
break;
}
return interval;
}
void main()
{
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// イベントループ
bool done = false;
SDL_Event e;
// 10msの精度でコールバック関数登録
SDL_TimerID id = SDL_AddTimer(10, &handler, null);
while ( !done ) {
SDL_PumpEvents(); // イベントをキューにセット
// SDL_MOUSEBUTTONDOWN以外を吸い上げ。
while (SDL_PeepEvents(&e, 1, SDL_GETEVENT, ~SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN)) == 1) {
done = e.type == SDL_QUIT;
}
SDL_Delay(1000/30);
}
SDL_RemoveTimer(id);
SDL_Quit();
}
**音楽の再生
生SDLだとしんどいので、SDL_mixerを使用します。
やね先生のサイト「http://www.sun-inet.or.jp/~yaneurao/dlang/Chapter-10.html#100100000000」
から、
「http://www.sun-inet.or.jp/~yaneurao/dlang/lib/yaneSDK4D0401280603.zip」をダウンロード。
「yaneSDK4D0401280603\SDL\SDL_mixer.d」を「SDL」フォルダに、
「yaneSDK4D0401280603\import\SDL_mixer.lib」を同一ディレクトリに入れます。
"chimes.wav"を鳴らします。
import SDL;
import SDL_mixer; // SDL_mixerを使うよー
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// 44100Hz, 16bit, ステレオ, バッファサイズ4092byte
Mix_OpenAudio(44100, AUDIO_S16, 2, 4092);
Mix_Music* music = Mix_LoadMUS("chimes.wav");
// 再生。2つ目の引数に再生回数を指定。-1だと無限ループ
if (Mix_PlayMusic(music, -1) != 0) {
throw new Error("Couldn't play music");
}
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
// 演奏停止
Mix_HaltMusic();
// 曲データ開放
Mix_FreeMusic(music);
// サウンドデバイスクローズ
Mix_CloseAudio();
SDL_Quit();
}
バッチファイルはこんな感じ。
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd test.d -ISDL SDL.lib SDL_mixer.lib
pause
「SDL_mixer.lib」を追加します。
あと、サウンドフォーマットは他に「Ogg」も対応しているみたいです。
**BGMとSEを同時に鳴らす
チャンクを使います。
import SDL;
import SDL_mixer; // SDL_mixerを使うよー
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// 44100Hz, 16bit, ステレオ, バッファサイズ4092byte
Mix_OpenAudio(44100, AUDIO_S16, 2, 4092);
// BGM
Mix_Music* music = Mix_LoadMUS("bgm.ogg");
if (!music) {
throw new Error("Couldn't load music");
}
// 再生。2つ目の引数に再生回数を指定。-1だと無限ループ
if (Mix_PlayMusic(music, -1) != 0) {
throw new Error("Couldn't play music");
}
// SE
Mix_Chunk* chunk = Mix_LoadWAV("chimes.wav");
if (!chunk) {
throw new Error("Couldn't load chunk");
}
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_MOUSEBUTTONDOWN:
// 再生する。第1引数はチャンネル番号(-1で自動割り当て)
// 第3の引数には再生回数(「0」は1 回再生。「-1」 なら無限ループ)
Mix_PlayChannel(-1, chunk, 0);
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
// サウンド停止
Mix_HaltChannel(-1);
Mix_HaltMusic();
// サウンドデータ開放
Mix_FreeChunk(chunk);
Mix_FreeMusic(music);
// サウンドデバイスクローズ
Mix_CloseAudio();
SDL_Quit();
}
ただ、このままビルドしても、
test.obj(test)
Error 42: Symbol Undefined _Mix_LoadWAV
test.obj(test)
Error 42: Symbol Undefined _Mix_PlayChannel
--- errorlevel 2
というリンカエラーが出てしまいます。
理由は良く分からないですが、「SDL_mixer.d」のオブジェクトファイルを作ってリンクするとうまくいきます。
SDLフォルダから「SDL_mixer.d」を引っ張ってきて、
dmd -c -ISDL SDL_mixer.d
とすると、「SDL_mixer.obj」ファイルができます。
そして、
dmd test.d -ISDL SDL.lib SDL_mixer.lib SDL_mixer.obj
こんな感じで「SDL_mixer.obj」をリンクするとビルドできます。
**コンソールを消す
コンソールが表示されたままでも、個人的にはシュールでよいのですが、一般的にはおかしいので、消してみます。
import SDL;
int boot(char[][] args)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
done = e.type == SDL_QUIT;
}
SDL_Delay(1000/30);
}
SDL_Quit();
return 0;
}
version (Win32_release)
{
// コンソールなし
private import std.c.windows.windows;
private import std.string;
extern (C) void gc_init();
extern (C) void gc_term();
extern (C) void _minit();
extern (C) void _moduleCtor();
extern (Windows)
public int WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
int result;
gc_init();
_minit();
try
{
_moduleCtor();
char exe[4096];
GetModuleFileNameA(null, exe, 4096);
char[][1] prog;
prog[0] = std.string.toString(exe);
result = boot(prog ~ std.string.split(std.string.toString(lpCmdLine)));
}
catch(Object o)
{
throw new Error("Exception: " ~ o.toString());
}
gc_term();
return result;
}
}
else
{
// コンソールあり
public int main(char[][] args)
{
return boot(args);
}
}
あと、例えば、「test.def」というファイルを作っておきます。
内容は、こんな感じ
EXETYPE NT
SUBSYSTEM WINDOWS
こんなバッチファイルでビルドできます。
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd -version=Win32_release -ISDL test.d SDL.lib test.def
pause
「-version=Win32_release」オプションを指定して、「test.def」をリンクさせます。
参考として、コードの中身の詳しい内容についてはこちら。
-[[Win32でのD>http://www.kmonos.net/alang/d/windows.html]]
**EXEファイルにリソースアイコンを埋め込む
D言語あんまり関係ないですが、念のため。
やり方は、「.res」ファイルを作ってリンクするだけです。
で、「.res」をどうやって作るのかというと、
VisualStudioとか持っていれば、そこからぺちぺち操作すれば作れまする。
「そんなのないよ!」という人はここから、
[[Borland® C++Compiler 5.5無償ダウンロード>http://www.borland.com/jp/products/cbuilder/freecompiler.html]]
Borland C++ Compiler 5.5をダウンロード。
インストーラー形式なので、「OK」⇒「完了」でインストール完了。
リソースをビルドするには、「.rc」が必要なので、
test ICON PRELOAD "test.ico"
と書いて、「test.rc」で保存。
SET PATH=C:\borland\bcc55\Bin;%PATH%
brcc32 test.rc
pause
こんなバッチファイルを書いて、「brcc32.exe」で「test.rc」をビルドすると、「test.res」ができます。
で、
dmd -ISDL test.d SDL.lib test.res
というように、「test.res」をリンクすれば、EXEファイルにアイコンが埋め込まれます。
**モジュール分割・ディレクトリ階層を作って配置する
1ファイルで頑張っても良いのですが、
Javaっぽく1ファイル1クラス、ディレクトリ階層を使ったりしたい人のために。
ビルドには「Ant」というツールを使います。
***Javaランタイムのインストール
ただ、「Ant」を使うには、Javaのランタイム(JRE)をインストールする必要があります。
http://www.java.com/ja/
「Ant」の機能を全部使うにはJDKを入れなければならないそうですが、とりあえず基本的なタスクしか使わないので、これでOK。
***Antのダウンロード・インストール
次にAntをダウンロード。
http://ant.apache.org/bindownload.cgi
から、apache-ant-1.6.5-bin.zipをダウンロードします。
解凍したら、フォルダ名を「ant」に変えて、とりあえず「C:\work\D\」に置いてみます。
***build.xmlを書く
「build.xml」とは、Antを使ってビルドするときの設定ファイルみたいなものです。
<!-- Antを使ってビルドするよー(http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/) -->
<project name="d_build" default="all" basedir=".">
<!-- ターゲット名(実行ファイル名) -->
<property name="name" value="test"/>
<property name="prog" value="${name}.exe"/>
<!-- ライブラリディレクトリ -->
<property name="libsdir" value="lib\"/>
<!-- ここに使うライブラリを追加します -->
<property name="libs" value="${libsdir}SDL.lib"/>
<!-- 「.d」のあるインポートディレクトリ -->
<property name="import" location="SDL"/>
<!-- ソースファイルのディレクトリ -->
<property name="src" location="src"/>
<!-- リソースのディレクトリ -->
<property name="resource" location="resource"/>
<!-- インポートライブラリのディレクトリ -->
<property name="builtimport" value="SDL_mixer.d"/>
<!-- 全部ビルド -->
<target name="all" depends="compile, link"/>
<target name="rebuild" depends="clean, compile, link"/>
<target name="compile">
<apply executable="dmd" dir="${src}" dest="${src}" parallel="true" failonerror="true" skipemptyfilesets="true">
<mapper type="glob" from="*.d" to="*.obj"/>
<fileset dir="${src}" includes="**/*.d"/>
<arg value="-c"/>
<arg value="-I${import}"/>
<arg value="-op"/>
<arg value="-O"/>
<arg value="-release"/>
<arg value="-version=Win32_release"/>
<srcfile/>
</apply>
</target>
<target name="link">
<apply executable="dmd" dir="." parallel="true" failonerror="true">
<fileset dir="${src}" includes="**/*.obj"/>
<fileset dir="${import}" includes="**/*.obj"/>
<fileset dir="${resource}" includes="test.RES"/> <!-- アイコンリソース -->
<fileset dir="${resource}" includes="test.def"/> <!-- コンソールを表示させない定義ファイル -->
<arg value="${prog}"/>
<arg value="${libs}"/>
<srcfile/>
</apply>
</target>
<!-- クリーン -->
<target name="clean">
<delete file="${prog}"/>
<delete file="${name}.map"/>
<delete>
<fileset dir="${src}" includes="**/*.obj"/>
</delete>
</target>
<!-- 実行 -->
<target name="run">
<exec executable="${name}" dir=".">
</exec>
</target>
<!-- インポートディレクトリのモジュールをビルド -->
<target name="buildlib">
<apply executable="dmd" dir="${import}" dest="${import}" parallel="false" failonerror="false">
<mapper type="glob" from="*.d" to="*.obj"/>
<fileset dir="${import}" includes="${builtimport}"/>
<arg value="-c"/>
<arg value="-I${import}"/>
<arg value="-op"/>
<srcfile/>
</apply>
</target>
</project>
これを「build.xml」という名前で保存します。
さて、この設定ファイルを使う前に以下の準備をしておきます。
-ソースコードは「src」ディレクトリに入れる(src\test.d)
-インポートモジュール(SDL関連)は「SDL」ディレクトリに入れる(そのまま)
-ライブラリは「lib」ディレクトリに入れる(lib\SDL.libなど)
-リソース(.def/.res)は「resource」ディレクトリに入れる
それで、こんなバッチファイルを実行すると、
SET PATH=C:\work\D\ant\bin;%PATH%
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
ant
pause
「src」以下のディレクトリ内のソースコードを自動でビルドしてくれます。
(※Antはデフォルトで、カレントディレクトリ内の「build.xml」を探しに行ってくれるため)
ただ、Antは終了するとpauseしてくれないので、コマンドプロンプトから、
ant
と入力した方が良さそうです。
あと、
ant clean
と入力すると、「.obj/.map/.exe」を全て削除してくれます。
D言語は.objの依存関係が強すぎるのか、EXEファイルを実行して、不明なエラーが出る場合は、
「ant clean」して、ビルドし直すと、うまくいく場合があります。
ほかにも
ant run
すると、実行ファイルを実行してくれたり、
ant buildlib
すると、インポートディレクトリ内の「.d」をコンパイルしてくれます。(この設定では「SDL_mixer.d」のみ)
Antの詳細については、
-[[Apache Ant 1.6.1 マニュアル>http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/manual/index.html]]
が参考になると思います。
**参考(D言語)
-[[プログラミング言語 D>http://www.kmonos.net/alang/d/]]--D言語リファレンスマニュアルです。
-[[D言語研究室>http://www.sun-inet.or.jp/~yaneurao/dlang/]]--やね先生のD言語研究室。マニアックな情報がたっぷりあります
-[[D言語研究>http://f17.aaa.livedoor.jp/~labamba/]]--D言語のWikiページ
-[[D言語入門>http://wisdom.sakura.ne.jp/programming/d/index.html]]--D言語の文法について細かく書いてあります
-[[D Memo>http://www.kmonos.net/alang/etc/d.php]]--これも、D言語の文法について。情報がやや古いですが、参考になる内容も多いです
-[[ABA Games>http://www.asahi-net.or.jp/~cs8k-cyu/]]--D言語で作ったゲームがたくさん。ソースコードを公開されているので、それが膨大なドキュメントです。
**参考(SDL)
-[[SDLドキュメント(日本語訳)>http://zinnia.dyndns.org/~cvsweb/sdldoc-jp/index.html]]
**コメント
なにかあればコメントをどうぞ。
- マウスイベント取得はポンプでくみ上げる以外の方法ってあるんですよね?自分で調べればいいんだけど...マウスイベントハンドラの実装例をおねがいします -- わんきち (2006-10-24 13:19:26)
- 初回コメント特典でやり方を書いておきましたー -- kenmo (2006-10-24 15:20:03)
- やり方ありがとうございます。続けて質問、SDL_MOUSEBUTTONDOWNにあたる部分が見当たらないのですが、、、あぁ今度こそ自分で探しますよw -- わんきち (2006-10-24 17:04:36)
- SDL_GetMouseStateするためのトリガーとして最低SDL_MOUSEBUTTONDOWNは拾えってことですね -- わんきち (2006-10-24 17:14:07)
- 何が言いたかったかっていうと、Javaのイベントリスナーみたいな実装が希望だったんですが、自分でやれってことですね、うん -- わんきち (2006-10-24 17:20:05)
- あ、勘違いしてました…。うーん、SDL_PeepEventsする処理をSDL_AddTimerすればいいのかなー -- kenmo (2006-10-24 18:16:44)
- ふー、頑張って取れたよ。>マウスイベントハンドラ -- kenmo (2006-10-24 20:16:34)
- おぉ、ありがとうございます。タイマースレッドで管理ですね。D言語の精度がどれくらいなのかしらないのでなんともなんですが、ご存知のとおり、タイマーはタイマーイベントをメッセージキューに投げるだけなので緊急度の高いメッセージがキューに投げ込まれると後送りにされてしまいます。できればスレッド制御にしたいですね。あとで調べてみます。スレッド版。 -- わんきち (2006-10-24 21:24:32)
- yaneSDK4Dはだいぶ古くなってるので http://shinh.skr.jp/ のD言語始めました、からインポートヘッダ取ってきたほうがいいかもしれません。importの仕様とか結構変わったので。 -- みかげ (2006-10-24 22:34:20)
- あと、SDL.dllもバージョンアップしてるみたいなんで本家から取ってきて上書きするといいかもしれません。SDL_SetVideoMode() の引数で0入れるとウィンドウがデスクトップのサイズになったりとか。 -- みかげ (2006-10-24 22:38:59)
- あうあー、早とちりだー書いてあるのに・・・申し訳ないです。 -- みかげ (2006-10-24 22:45:31)
#comment
*D言語ゲーム開発入門
**コレは何?
D言語勉強中のkenmoが、D言語でのゲーム開発情報をメモしていくページです。
-目次
----
#contents
**D言語の特徴
***メリット
-ランタイムのいらない、コンパクトな実行ファイルが作れる
⇒JavaとかC#とかRubyとかPythonにはないメリットだぞ。
-ガベコレがある
⇒C/C++のメモリ管理に泣かされていた日々とはサヨナラです。
-クラスを使わなくても良い
⇒Cライクな書き方でもOK!モダンな書き方もできるぞ!
-コンパイルが爆速
⇒スクリプト言語なみに速いぞ
***デメリット
-情報が少ない
⇒ある程度出揃っていますが、まだ初心者向きの情報がないです
-未だベータ版
⇒2006/10/23現在のコンパイラのバージョンは、0.172です。仕様が結構変わる。先は長いぞー
-ライブラリ作るのがめんどい
⇒SDLなりOpenGLなりライブラリを自作しないと、ゲーム作るのは大変。そこの苦労はC/C++とかと一緒。
-統合環境がまだまだ
⇒VisualStudioやEclipseと比べると、デバッガやリファクタリングがやりにくい・できないのが少しつらいです。
今のところ、結局テキストエディタでコード書いて、コマンドプロンプトからビルド、、という流れに落ち着きます。
個人的には、C+SDLとか、C+DirectXとか、C+OpenGLなりやっていないとツライかな、という気がします。
ということで、そこらへんをやったことある人が
「そろそろD言語はじめっかなー?」
と思っている人が対象の内容となっております。
**インストール~Hello, world.
とりあえず、インストールして「Hello, world.」を出すまで。
***コンパイラとリンカのインストール
http://www.kmonos.net/alang/d/dcompiler.html
ここから、
-Win32版 dmd.zip (D コンパイラ)
と
-Win32版 dmc.zip (リンカなどの小物ツール)
をダウンロード。
dmd.zipを解凍すると、「dm」と「dmd」ができる。
とりあえず、「C:\work\D\」に置いてみます。(※「デスクトップ」とか「C:\Program Files」とかの日本語パス、半角スペースがあるところに置いちゃダメ)
dmc.zipを解凍すると、「dm」ができる。
それを先ほどの「dm」に上書き。
これでインストール完了。
***Hello, world.
void main()
{
printf("Hello, world.");
}
こんなプログラムを書いて、「test.d」で保存。
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd test.d
pause
で、こんなバッチファイル(例えば「conv.bat」)を書いて保存。
バッチファイルをダブルクリックで、コンパイル。
「test.exe」ができれば、ビルド完了。
コマンドプロンプトから、「test.exe」を実行すると、
Hello, world.
と出れば、OKです。
**SDLをインストール~動かす
[[D - porting>http://shinh.skr.jp/d/porting.html]]から、「SDL. Based on DedicateD's.」の「SDL」のリンクをぺちっと押して、「SDL.zip」をダウンロード。
解凍してできた「SDL」フォルダを「C:\work\D\」に配置。
「SDL」フォルダ内にある、「SDL.dll」と「SDL.lib」をコピーして、「C:\work\D\」に移動します。
これでインストール完了。
***SDLを動かす(画面を出す)
import SDL;
void main()
{
// 初期化ー
if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
throw new Error("Couldn't initialize SDL");
}
// 640x480で。デフォルトのbpp。ソフトウェアで頑張る
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
if ( screen == null ) {
throw new Error("Couldn't set 640x480 video mode");
}
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
SDL_Quit();
}
これを「test.d」で保存。
&color(red){(※文字コードを「UTF-8」にして保存します)}
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd test.d -ISDL SDL.lib
pause
こんなバッチファイルを書いて、ビルド。
ちなみに、
dmd test.d -ISDL SDL.lib
の部分は、
「-ISDL」はインポートディレクトリ「SDL」の指定で、
「SDL.lib」はライブラリをスタティックリンクしています。
それで、できたtest.exeを実行して、画面が出れば、とりあえずOKです。
**画像を表示
BMPを読み込んで描画します。(エラー処理は省略)
import SDL;
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// ビットマップ読込
SDL_Surface *image = SDL_LoadBMP("hell.bmp");
// 転送
SDL_BlitSurface(image, cast(SDL_Rect*)0, screen, cast(SDL_Rect*)0);
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
SDL_Quit();
}
**マウスイベント取得
import SDL;
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
// マウス押下イベント取得
case SDL_MOUSEBUTTONDOWN:
switch (e.button.button)
{
case SDL_BUTTON_LEFT:
SDL_WM_SetCaption("push left", null);
break;
case SDL_BUTTON_RIGHT:
SDL_WM_SetCaption("push right", null);
break;
default:
break;
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
SDL_Quit();
}
**マウスイベントの取得方法2
わんきちさんのリクエストにお答えして、マウスのイベントをコールバックで取る方法を。
import SDL;
extern (C) Uint32 handler(Uint32 interval, void* param)
{
SDL_PumpEvents(); // イベントをキューにセット
SDL_Event e;
if(SDL_PeepEvents(&e, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN)) < 1) {
return interval;
}
printf("SDL_MOUSEBUTTONDOWN --> ");
switch(e.type)
{
case SDL_MOUSEBUTTONDOWN:
switch(e.button.button)
{
case SDL_BUTTON_LEFT:
printf("SDL_BUTTON_LEFT\n");
break;
default:
printf("else\n");
break;
}
break;
default:
break;
}
return interval;
}
void main()
{
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// イベントループ
bool done = false;
SDL_Event e;
// 10msの精度でコールバック関数登録
SDL_TimerID id = SDL_AddTimer(10, &handler, null);
while ( !done ) {
SDL_PumpEvents(); // イベントをキューにセット
// SDL_MOUSEBUTTONDOWN以外を吸い上げ。
while (SDL_PeepEvents(&e, 1, SDL_GETEVENT, ~SDL_EVENTMASK(SDL_MOUSEBUTTONDOWN)) == 1) {
done = e.type == SDL_QUIT;
}
SDL_Delay(1000/30);
}
SDL_RemoveTimer(id);
SDL_Quit();
}
**音楽の再生
生SDLだとしんどいので、SDL_mixerを使用します。
やね先生のサイト「http://www.sun-inet.or.jp/~yaneurao/dlang/Chapter-10.html#100100000000」
から、
「http://www.sun-inet.or.jp/~yaneurao/dlang/lib/yaneSDK4D0401280603.zip」をダウンロード。
「yaneSDK4D0401280603\SDL\SDL_mixer.d」を「SDL」フォルダに、
「yaneSDK4D0401280603\import\SDL_mixer.lib」を同一ディレクトリに入れます。
"chimes.wav"を鳴らします。
import SDL;
import SDL_mixer; // SDL_mixerを使うよー
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// 44100Hz, 16bit, ステレオ, バッファサイズ4092byte
Mix_OpenAudio(44100, AUDIO_S16, 2, 4092);
Mix_Music* music = Mix_LoadMUS("chimes.wav");
// 再生。2つ目の引数に再生回数を指定。-1だと無限ループ
if (Mix_PlayMusic(music, -1) != 0) {
throw new Error("Couldn't play music");
}
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
// 演奏停止
Mix_HaltMusic();
// 曲データ開放
Mix_FreeMusic(music);
// サウンドデバイスクローズ
Mix_CloseAudio();
SDL_Quit();
}
バッチファイルはこんな感じ。
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd test.d -ISDL SDL.lib SDL_mixer.lib
pause
「SDL_mixer.lib」を追加します。
あと、サウンドフォーマットは他に「Ogg」も対応しているみたいです。
**BGMとSEを同時に鳴らす
チャンクを使います。
import SDL;
import SDL_mixer; // SDL_mixerを使うよー
void main()
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface *screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
// 44100Hz, 16bit, ステレオ, バッファサイズ4092byte
Mix_OpenAudio(44100, AUDIO_S16, 2, 4092);
// BGM
Mix_Music* music = Mix_LoadMUS("bgm.ogg");
if (!music) {
throw new Error("Couldn't load music");
}
// 再生。2つ目の引数に再生回数を指定。-1だと無限ループ
if (Mix_PlayMusic(music, -1) != 0) {
throw new Error("Couldn't play music");
}
// SE
Mix_Chunk* chunk = Mix_LoadWAV("chimes.wav");
if (!chunk) {
throw new Error("Couldn't load chunk");
}
// イベントループ
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
switch (e.type) {
case SDL_MOUSEBUTTONDOWN:
// 再生する。第1引数はチャンネル番号(-1で自動割り当て)
// 第3の引数には再生回数(「0」は1 回再生。「-1」 なら無限ループ)
Mix_PlayChannel(-1, chunk, 0);
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
SDL_Delay(1000/30);
}
// サウンド停止
Mix_HaltChannel(-1);
Mix_HaltMusic();
// サウンドデータ開放
Mix_FreeChunk(chunk);
Mix_FreeMusic(music);
// サウンドデバイスクローズ
Mix_CloseAudio();
SDL_Quit();
}
ただ、このままビルドしても、
test.obj(test)
Error 42: Symbol Undefined _Mix_LoadWAV
test.obj(test)
Error 42: Symbol Undefined _Mix_PlayChannel
--- errorlevel 2
というリンカエラーが出てしまいます。
理由は良く分からないですが、「SDL_mixer.d」のオブジェクトファイルを作ってリンクするとうまくいきます。
SDLフォルダから「SDL_mixer.d」を引っ張ってきて、
dmd -c -ISDL SDL_mixer.d
とすると、「SDL_mixer.obj」ファイルができます。
そして、
dmd test.d -ISDL SDL.lib SDL_mixer.lib SDL_mixer.obj
こんな感じで「SDL_mixer.obj」をリンクするとビルドできます。
**コンソールを消す
コンソールが表示されたままでも、個人的にはシュールでよいのですが、一般的にはおかしいので、消してみます。
import SDL;
int boot(char[][] args)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 0, SDL_SWSURFACE);
bool done = false;
SDL_Event e;
while ( !done ) {
while ( SDL_PollEvent(&e) ) {
done = e.type == SDL_QUIT;
}
SDL_Delay(1000/30);
}
SDL_Quit();
return 0;
}
version (Win32_release)
{
// コンソールなし
private import std.c.windows.windows;
private import std.string;
extern (C) void gc_init();
extern (C) void gc_term();
extern (C) void _minit();
extern (C) void _moduleCtor();
extern (Windows)
public int WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
int result;
gc_init();
_minit();
try
{
_moduleCtor();
char exe[4096];
GetModuleFileNameA(null, exe, 4096);
char[][1] prog;
prog[0] = std.string.toString(exe);
result = boot(prog ~ std.string.split(std.string.toString(lpCmdLine)));
}
catch(Object o)
{
throw new Error("Exception: " ~ o.toString());
}
gc_term();
return result;
}
}
else
{
// コンソールあり
public int main(char[][] args)
{
return boot(args);
}
}
あと、例えば、「test.def」というファイルを作っておきます。
内容は、こんな感じ
EXETYPE NT
SUBSYSTEM WINDOWS
こんなバッチファイルでビルドできます。
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
dmd -version=Win32_release -ISDL test.d SDL.lib test.def
pause
「-version=Win32_release」オプションを指定して、「test.def」をリンクさせます。
参考として、コードの中身の詳しい内容についてはこちら。
-[[Win32でのD>http://www.kmonos.net/alang/d/windows.html]]
**EXEファイルにリソースアイコンを埋め込む
D言語あんまり関係ないですが、念のため。
やり方は、「.res」ファイルを作ってリンクするだけです。
で、「.res」をどうやって作るのかというと、
VisualStudioとか持っていれば、そこからぺちぺち操作すれば作れまする。
「そんなのないよ!」という人はここから、
[[Borland® C++Compiler 5.5無償ダウンロード>http://www.borland.com/jp/products/cbuilder/freecompiler.html]]
Borland C++ Compiler 5.5をダウンロード。
インストーラー形式なので、「OK」⇒「完了」でインストール完了。
リソースをビルドするには、「.rc」が必要なので、
test ICON PRELOAD "test.ico"
と書いて、「test.rc」で保存。
SET PATH=C:\borland\bcc55\Bin;%PATH%
brcc32 test.rc
pause
こんなバッチファイルを書いて、「brcc32.exe」で「test.rc」をビルドすると、「test.res」ができます。
で、
dmd -ISDL test.d SDL.lib test.res
というように、「test.res」をリンクすれば、EXEファイルにアイコンが埋め込まれます。
**モジュール分割・ディレクトリ階層を作って配置する
1ファイルで頑張っても良いのですが、
Javaっぽく1ファイル1クラス、ディレクトリ階層を使ったりしたい人のために。
ビルドには「Ant」というツールを使います。
***Javaランタイムのインストール
ただ、「Ant」を使うには、Javaのランタイム(JRE)をインストールする必要があります。
http://www.java.com/ja/
「Ant」の機能を全部使うにはJDKを入れなければならないそうですが、とりあえず基本的なタスクしか使わないので、これでOK。
***Antのダウンロード・インストール
次にAntをダウンロード。
http://ant.apache.org/bindownload.cgi
から、apache-ant-1.6.5-bin.zipをダウンロードします。
解凍したら、フォルダ名を「ant」に変えて、とりあえず「C:\work\D\」に置いてみます。
***build.xmlを書く
「build.xml」とは、Antを使ってビルドするときの設定ファイルみたいなものです。
<!-- Antを使ってビルドするよー(http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/) -->
<project name="d_build" default="all" basedir=".">
<!-- ターゲット名(実行ファイル名) -->
<property name="name" value="test"/>
<property name="prog" value="${name}.exe"/>
<!-- ライブラリディレクトリ -->
<property name="libsdir" value="lib\"/>
<!-- ここに使うライブラリを追加します -->
<property name="libs" value="${libsdir}SDL.lib"/>
<!-- 「.d」のあるインポートディレクトリ -->
<property name="import" location="SDL"/>
<!-- ソースファイルのディレクトリ -->
<property name="src" location="src"/>
<!-- リソースのディレクトリ -->
<property name="resource" location="resource"/>
<!-- インポートライブラリのディレクトリ -->
<property name="builtimport" value="SDL_mixer.d"/>
<!-- 全部ビルド -->
<target name="all" depends="compile, link"/>
<target name="rebuild" depends="clean, compile, link"/>
<target name="compile">
<apply executable="dmd" dir="${src}" dest="${src}" parallel="true" failonerror="true" skipemptyfilesets="true">
<mapper type="glob" from="*.d" to="*.obj"/>
<fileset dir="${src}" includes="**/*.d"/>
<arg value="-c"/>
<arg value="-I${import}"/>
<arg value="-op"/>
<arg value="-O"/>
<arg value="-release"/>
<arg value="-version=Win32_release"/>
<srcfile/>
</apply>
</target>
<target name="link">
<apply executable="dmd" dir="." parallel="true" failonerror="true">
<fileset dir="${src}" includes="**/*.obj"/>
<fileset dir="${import}" includes="**/*.obj"/>
<fileset dir="${resource}" includes="test.RES"/> <!-- アイコンリソース -->
<fileset dir="${resource}" includes="test.def"/> <!-- コンソールを表示させない定義ファイル -->
<arg value="${prog}"/>
<arg value="${libs}"/>
<srcfile/>
</apply>
</target>
<!-- クリーン -->
<target name="clean">
<delete file="${prog}"/>
<delete file="${name}.map"/>
<delete>
<fileset dir="${src}" includes="**/*.obj"/>
</delete>
</target>
<!-- 実行 -->
<target name="run">
<exec executable="${name}" dir=".">
</exec>
</target>
<!-- インポートディレクトリのモジュールをビルド -->
<target name="buildlib">
<apply executable="dmd" dir="${import}" dest="${import}" parallel="false" failonerror="false">
<mapper type="glob" from="*.d" to="*.obj"/>
<fileset dir="${import}" includes="${builtimport}"/>
<arg value="-c"/>
<arg value="-I${import}"/>
<arg value="-op"/>
<srcfile/>
</apply>
</target>
</project>
これを「build.xml」という名前で保存します。
さて、この設定ファイルを使う前に以下の準備をしておきます。
-ソースコードは「src」ディレクトリに入れる(src\test.d)
-インポートモジュール(SDL関連)は「SDL」ディレクトリに入れる(そのまま)
-ライブラリは「lib」ディレクトリに入れる(lib\SDL.libなど)
-リソース(.def/.res)は「resource」ディレクトリに入れる
それで、こんなバッチファイルを実行すると、
SET PATH=C:\work\D\ant\bin;%PATH%
SET PATH=C:\work\D\dmd\bin\;C:\work\D\dm\bin\;%PATH%
ant
pause
「src」以下のディレクトリ内のソースコードを自動でビルドしてくれます。
(※Antはデフォルトで、カレントディレクトリ内の「build.xml」を探しに行ってくれるため)
ただ、Antは終了するとpauseしてくれないので、コマンドプロンプトから、
ant
と入力した方が良さそうです。
あと、
ant clean
と入力すると、「.obj/.map/.exe」を全て削除してくれます。
D言語は.objの依存関係が強すぎるのか、EXEファイルを実行して、不明なエラーが出る場合は、
「ant clean」して、ビルドし直すと、うまくいく場合があります。
ほかにも
ant run
すると、実行ファイルを実行してくれたり、
ant buildlib
すると、インポートディレクトリ内の「.d」をコンパイルしてくれます。(この設定では「SDL_mixer.d」のみ)
Antの詳細については、
-[[Apache Ant 1.6.1 マニュアル>http://www.jajakarta.org/ant/ant-1.6.1/docs/ja/manual/index.html]]
が参考になると思います。
**参考(D言語)
-[[プログラミング言語 D>http://www.kmonos.net/alang/d/]]--D言語リファレンスマニュアルです。
-[[D言語研究室>http://www.sun-inet.or.jp/~yaneurao/dlang/]]--やね先生のD言語研究室。マニアックな情報がたっぷりあります
-[[D言語研究>http://f17.aaa.livedoor.jp/~labamba/]]--D言語のWikiページ
-[[D言語入門>http://wisdom.sakura.ne.jp/programming/d/index.html]]--D言語の文法について細かく書いてあります
-[[D Memo>http://www.kmonos.net/alang/etc/d.php]]--これも、D言語の文法について。情報がやや古いですが、参考になる内容も多いです
-[[ABA Games>http://www.asahi-net.or.jp/~cs8k-cyu/]]--D言語で作ったゲームがたくさん。ソースコードを公開されているので、それが膨大なドキュメントです。
**参考(SDL)
-[[SDLドキュメント(日本語訳)>http://zinnia.dyndns.org/~cvsweb/sdldoc-jp/index.html]]
**コメント
なにかあればコメントをどうぞ。
- マウスイベント取得はポンプでくみ上げる以外の方法ってあるんですよね?自分で調べればいいんだけど...マウスイベントハンドラの実装例をおねがいします -- わんきち (2006-10-24 13:19:26)
- 初回コメント特典でやり方を書いておきましたー -- kenmo (2006-10-24 15:20:03)
- やり方ありがとうございます。続けて質問、SDL_MOUSEBUTTONDOWNにあたる部分が見当たらないのですが、、、あぁ今度こそ自分で探しますよw -- わんきち (2006-10-24 17:04:36)
- SDL_GetMouseStateするためのトリガーとして最低SDL_MOUSEBUTTONDOWNは拾えってことですね -- わんきち (2006-10-24 17:14:07)
- 何が言いたかったかっていうと、Javaのイベントリスナーみたいな実装が希望だったんですが、自分でやれってことですね、うん -- わんきち (2006-10-24 17:20:05)
- あ、勘違いしてました…。うーん、SDL_PeepEventsする処理をSDL_AddTimerすればいいのかなー -- kenmo (2006-10-24 18:16:44)
- ふー、頑張って取れたよ。>マウスイベントハンドラ -- kenmo (2006-10-24 20:16:34)
- おぉ、ありがとうございます。タイマースレッドで管理ですね。D言語の精度がどれくらいなのかしらないのでなんともなんですが、ご存知のとおり、タイマーはタイマーイベントをメッセージキューに投げるだけなので緊急度の高いメッセージがキューに投げ込まれると後送りにされてしまいます。できればスレッド制御にしたいですね。あとで調べてみます。スレッド版。 -- わんきち (2006-10-24 21:24:32)
- yaneSDK4Dはだいぶ古くなってるので http://shinh.skr.jp/ のD言語始めました、からインポートヘッダ取ってきたほうがいいかもしれません。importの仕様とか結構変わったので。 -- みかげ (2006-10-24 22:34:20)
- あと、SDL.dllもバージョンアップしてるみたいなんで本家から取ってきて上書きするといいかもしれません。SDL_SetVideoMode() の引数で0入れるとウィンドウがデスクトップのサイズになったりとか。 -- みかげ (2006-10-24 22:38:59)
- あうあー、早とちりだー書いてあるのに・・・申し訳ないです。 -- みかげ (2006-10-24 22:45:31)
#comment
表示オプション
横に並べて表示:
変化行の前後のみ表示: