MinGW64でDXライブラリを動かすときにちょっと詰まったところがあったので、その解決方法なんかのメモしときます。
DXライブラリはVisualStudioでの使用を前提としているので、MinGWで使おうとすると資料がちょっと少なかったりするんですな(まぁ、本家サイトを読んでいじってれば普通にできるような気もしますが)。
ってことで、DXライブラリが何者なのかってことと、MinGWとかMSYSについて軽く説明してから64ビット版MinGWでのDXライブラリの動かし方(Makefileの書き方とか)を説明していこうというのが、今回の記事になります。
今回は前提知識が多めなので、DXライブラリが何か、MinGWが何か、Makefileが何なのかなんてもう知ってるよ!って方は『Makefileの書き方』の節まで飛んでいただいても大丈夫です。
DXライブラリはゲーム作りに必要な機能を集めたもの
DXライブラリって何?って方もいらっしゃるかと思うので、まずはその説明を。
DXライブラリってのは、ざっくりと言うと「マルチメディアを扱うための処理を大量に集めたもの」になります。マルチメディアを扱うための処理ってのは、例えば、音楽を再生するとか画像を表示する、図形を表示するなんが挙げられます。そういったことをしようと思ったときにDXライブラリを使うわけですな。
なので、stdio.hが入出力関数を扱うときに必要な処理をまとめて色んな関数として定義しているように、DXライブラリは画像を表示したり図形を表示したりといったマルチメディアを扱うときに必要な処理をまとめて色んな関数として定義したものになります。
Dxlib.hっていうヘッダファイルを読み込んで使うことになります。(後の方に詳しく書いています)
DXライブラリにある機能を使えば色んなゲームを作れるようになるんで、色々といじってみている次第です。まぁ、今からであればVulkanを勉強した方がいいような気もしますが…。
MinGW64とはC言語(C++も)の開発環境のこと
前まではVisualStudioを使ってC++のプログラムを書いてたんですが、今はMSYS2という開発環境を使っておるんです。
MSYS2ってのはざっくりと説明すると、Windows上にコマンドラインでの開発環境を整えるソフトのことです。こいつをインストールしてやれば、C言語とかC++言語とかの開発環境を作ることができるようになっています。実際には必要な機能を追加でインストールしてやらないといけないんですが、MSYS2に用意された機能を脳死状態で動かしてやれば勝手に終わります。
C言語で書いた簡単なプログラムであればgccコマンドだけでコンパイル(コンパイル:C言語から)したり(C++で書いたプログラムならg++)、複雑なプログラムであればGNUmakeとMakefileを使ってコンパイルしたりリンクしたりできます。
そんなMSYS2なんですが、Microsoft社のVisualStudioとは違って、コンパイルするときに必要なインクルードディレクトリとか外部ライブラリとかをMakefileに書いとかないといけないんです(書かずに済ますこともできるけど、滅茶苦茶めんどくさいし無駄なので、実質書かないといけない)。コンパイルとかリンクはオプションを付けた状態でやる必要があるんですが、そのオプションをMakefileに書いとくってわけですな。
Makefileは実行ファイル(exeファイル)の組立図のこと
Makefileってのはどんな完成品を、どんな材料をどう加工して作るかを書いたテキストファイルのことになります。完成品の組立図がMakefileってことですな。Makefileに、「EXEファイルという完成品を、C++ファイルを機械言語に変換するという加工をして作る」ということを書いてやれば、それは実行ファイルの組立図になります。
機械が組立図がないと組み立てられないのと同じように、実行ファイルもMakefileという組立図がないと作ることができないんですな。
そして、makeというコマンドで実際に組み立てることができます。makeにはいくつか種類があるんですが、僕はGNUmakeを使っているんで、これからはGNUmakeを前提に話を進めていきます。
まぁ、この辺はまた気が向いたり要望があったりしたら詳しく書きます。とりあえず、MinGWを使ってexeファイルを作るときにはMakefileってのが必要なんだって思っといてもらえば大丈夫です。
Makefileはこんな感じ
前提知識の説明が終わったところで、Makefileの書き方とコンパイル、リンクの方法を説明していきます。
まず、今回のファイル構成はこんな感じになります。
DxLib_test
|--build(作られたオブジェクトファイルが入れられるフォルダ)
| |--main.d(が作られる予定)
| '--main.o(が作られる予定)
|--include
| '--DxLibの「プロジェクトに追加すべきファイル_GCC(MinGW)用」の中にある.hファイルすべて
| (自分の使っているGCCのバージョンに一番近いものだけでよい)
|--lib
| '--DxLibの「プロジェクトに追加すべきファイル_GCC(MinGW)用」の中にある.aファイルすべて
| (自分の使っているGCCのバージョンに一番近いものだけでよい)
|--src
| '--main.cpp
|--Dxlib_test.exe(が作られる予定)
'--GNUmakefile
DxLib_testの中にbuild、include、lib、srcというフォルダとGNUmakefileというファイルがあって、buildの中にmakeコマンドを実行した後はmain.d、main.oが作られて、includeの中にはDxLibの中にある「プロジェクトに追加すべきファイル_GCC(MinGW)用」フォルダ中で、バージョンに適したフォルダの中にある.aファイルをすべてコピーしておき、libには同様のフォルダから.hをすべてコピーしておき、srcには自分の書くC++ソースファイルを入れておくという形になります。
で、makeで実行ファイルを作るときはDxLib_testをカレントディレクトリに設定した状態でmakeコマンドを実行すればいいようにしておきます。
Makefileはこんな感じになります。上に書いたフォルダ構成の中にあるGNUmakefileってファイルの中身です。ほとんどこのサイトのパクリですが。
# 定義済みマクロの再定義
CC := g++
CFLAGS := -Wall # エラーチェックのレベルを最大にする
CFLAGS += -O2
CFLAGS += -g # デバッグ情報を生成
CFLAGS += -MMD # オブジェクトファイルの依存関係を*.dに出力する
CFLAGS += -MP # ヘッダファイルに依存関係がないとして依存関係を出力する
CFLAGS += -DDX_GCC_COMPILE # DxLibを使うときに必要
CFLAGS += -DDX_NON_INLINE_ASM # DxLibを使うときに必要
# 実行ファイルの名前を指定
TARGET = Dxlib_test
LIB_DIR = ./lib # ライブラリパスを指定
LIBS := # 動的にリンクするライブラリを指定
LDFLAGS := -lDxLib \
-lDxUseCLib \
-lDxDrawFunc \
-ljpeg \
-lpng \
-lzlib \
-ltiff \
-ltheora_static \
-lvorbis_static \
-lvorbisfile_static \
-logg_static \
-lbulletdynamics \
-lbulletcollision \
-lbulletmath \
-lopusfile \
-lopus \
-lsilk_common \
-lcelt # 静的にリンクするライブラリの指定
# 使用するヘッダファイルが置かれているディレクトリを指定
INCLUDE = ./include
# ソースのあるディレクトリとコンパイルするソースファイルを指定
SRC_DIR = ./src
SRCS = $(shell ls $(SRC_DIR)/*.cpp)
# オブジェクトのあるディレクトリとリンクするオブジェクトファイルを指定
OBJ_DIR = ./build
OBJS = $(subst $(SRC_DIR), $(OBJ_DIR), $(SRCS:.cpp=.o)) # ソースの名前を.cppから.oに変換した後で、ディレクトリをソースからオブジェクトディレクトリに変換
# 依存関係を示す中間ファイルの指定
DEPENDS = $(OBJS:.o=.d)
# サフィックスルールの適用対象とする拡張子の定義
#.SUFFIXES: .o .cpp
# ファイルを生成しないターゲットを指定
.PHONY: all clean
all: $(TARGET)
# プライマリターゲット
$(TARGET): $(OBJS) $(LIBS)
$(CC) $^ -L $(LIB_DIR) $(LDFLAGS) -o $@
# サフィックスルール
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
@if [ ! -d $(OBJ_DIR) ]; \
then echo "mkdir -p $(OBJ_DIR)"; mkdir -p $(OBJ_DIR); \
fi
$(CC) $(CFLAGS) -I $(INCLUDE) -c $< -o $@
# 依存関係をインクルード
-include $(DEPENDS)
# オブジェクトファイル、実行ファイル、依存記述ファイルをすべて削除
clean:
$(RM) $(TARGET) $(OBJS) $(DEPENDS)
何をしているのかをざっくりと説明しておきます。Makefileの文法とかはここでは説明しませんので、気になる方は検索してみてください。
最終目標(makeコマンドでやりたいこと)は、main.cppをコンパイルしてmain.oを作ること、main.oとDxLibの.aファイルをリンクしてからDxLib_test.exeという実行ファイルを作ることの2つなので、本当にやりたいことが書いてあるのはコメントで「プライマリターゲット」と書かれている部分と「サフィックスルール」と書かれている部分の2か所になります。
まず、「プライマリターゲット」と書かれている部分の説明です。
この部分は、$(TARGET)を$(OBJS)と$(LIBS)から作ってくださいね。
具体的には$(CC) $^ -L $(LIB_DIR) $(LDFLAGS) -o $@という処理を実行してくださいね。
という意味になります。$(TARGET)は上の方でDxlib_testが、$(OBJS)は./build/*.oが代入されています。$(LIBS)には何も代入されていないので、「$(TARGET)を$(OBJS)と$(LIBS)から作ってくださいね。」という部分は「Dxlib_testを./build/*.oから作ってくださいね」という意味になります。
具体的な処理は$(CC) $^ -L $(LIB_DIR) $(LDFLAGS) -o $@になります。
$(CC)はg++、$^は$(OBJS)と$(LIBS)を指すので今回はmain.o、$(LIB_DIR)は./build、$(LDFLAGS)はDxLibを使うときに必要なライブラリファイルすべてになります。$(LDFLAGS)は本家サイトのものをコピーして\(バックスラッシュ)を追加していっただけです。$@は上の行の:の左側を指すので、今回はDxLib_testになります。
ということで、処理としては、main.oと./buildの中にあるDxLibを使うときに必要なライブラリファイルすべてをリンクの対象としてリンクを実行し、DxLib_testという名前のexeファイルを作成するという処理になります。
※ ./build/*.oというのは、buildの中にあって*以外の部分が一致するすべてのファイルを指します。なので、今回はmain.oが該当します。もしも、buildフォルダの中に他にも.oファイルがあったら、そのファイルも対象になります。
次に、「サフィックスルール」と書かれている部分です。
この部分は、上の「Dxlib_testを./build/*.oから作って」いる最中に、.oファイルがないが.cppファイルなら存在する場合に実行される部分になります。例えば、main.oはないけど、main.cppならある場合とかに実行される部分になります。
具体的な処理としては、$(SRC_DIR)の中にある.cppファイルをコンパイルして同名の.oファイルを$(OBJ_DIR)に作るという処理になります。
$(SRC_DIR)には./src、$(OBJ_DIR)には./buildが代入されているので、実行する処理としては、
もしもDxlib_testを./build/*.oから作っているときに.cppファイルはあるけど同名の.oファイルがない場合は、./srcの中にある.cppをコンパイルして./buildに同名の.oファイルを作るという処理になります。
main.cppは本家サイトの物を完全コピー
次に、srcフォルダの中にあるmain.cppです。このページと本家様とを行ったり来たりするのは面倒だと思うので一応載せてはおきますが、こちらは本家サイトのサンプルを完全にコピーしただけです。なので、本家サイト様のものをコピーしていただいてもまったく問題ありません。
#include "DxLib.h"
// プログラムは WinMain から始まります
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
if( DxLib_Init() == -1 ) // DXライブラリ初期化処理
{
return -1 ; // エラーが起きたら直ちに終了
}
DrawPixel( 320 , 240 , GetColor( 255,255,255 ) ) ; // 点を打つ
WaitKey() ; // キー入力待ち
DxLib_End() ; // DXライブラリ使用の終了処理
return 0 ; // ソフトの終了
}
ここまで出来たら、後はMinGWのコマンドラインでカレントディレクトリをDxLib_testのフォルダに変更してmakeコマンドを実行するだけです。
MinGWのコマンドライン上でmakeコマンドを実行すると、makefileに書いた処理がすべて自動で実行されてDxLib_test.exeが作られるわけです。ちなみに、このMakefileはMinGWの64ビット版でしか動きませんでした。32ビット版でコンパイルとリンクを使用とすると、コンパイルは通ってもリンクができないって状況が起きたので、この記事を参考にされる方はご注意くださいませ~。
P.S. includeファイルとlibファイルにDxLibのライブラリとヘッダをコピーしましたが、絶対パスでその場所を指定しておけば、毎回ライブラリとヘッダをコピーする必要はなくなるかもです。
P.P.S. 素直にVisualStudioを使えばいいものを。いやぁ~、なんとも無駄な努力をやってるなぁ~笑