SDL2でのゲームの作り方入門-画像表示編-

SDL

前回はウィンドウ表示をマスターしました。今度はそのウィンドウに画像を表示します。画像を表示するのに標準ではBMPというファイルしかサポートしていないのですが、SDL_Imageという公式のAPIを入れると、png,jpegという普通に使われている型式を扱うことが可能になります。薄々気づいている方も居るでしょう。また、導入からスタートです

SDL_Image導入

まずはここ↓からダウンロードしましょう

https://www.libsdl.org/projects/SDL_image/release/SDL2_image-devel-2.0.5-VC.zip (例によって公式のURLです。怖い人は直接調べてダウンロードしましょう)

ダウンロードして解凍したら、前回使ったプロジェクトを開いて設定していきます。

またこのようにプロパティを開きます。

開いたら、前々回と同じように赤い枠の部分を確認して、追加のライブラリディレクトリの赤い枠をクリックします。

赤い枠で囲んでいるファイルボタンをクリックして、解凍したファイル→lib→x86のファイルパスを指定してあげます。

今度は入力で追加の依存ファイルから以下のようにSDL2_image.libを追加してあげましょう。

追加のインクルードディレクトリも同じように解凍したファイル→includeで指定してあげましょう。

これで完了!ではなく、解凍したファイル→lib→x86の中にある.dll拡張子ファイル全てをコピーしてソースファイルがあるフォルダに張り付けます。その上で、下の画像をsample.pngという名前に変えてダウンロードした後、ソースファイルがあるフォルダに張り付けます。

ロゴ

こんな感じ

これで導入完了!

画像表示していく!

やっと画像表示に移れます。今回のコード全体はこれ

#include <SDL.h>//SDLを使う為にインクルードする
#include<sdl_image.h>//SDL_IMageを使う為にインクルードする
#define SCREEN_X 1280//SCREEN_Xと打つと1280が入力されるようにする
#define SCREEN_Y 720//SCREEN_Yと打つと720が入力されるようにする
int main(int argc, char** argv) {
	SDL_Surface* image;//画像を入れる用
	SDL_Texture* image_texture;//画像を使うためのデータ用
	SDL_Window* window;//ウィンドウの情報入れる用
	SDL_Renderer* renderer;//ウィンドウを使う為のデータ用

	//初期化部
	if (SDL_Init(SDL_INIT_VIDEO) && IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG)) {//初期化
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

	//画像読み込み部
	image = IMG_Load("sample.png");// sample.pngをimageに読み込む
	if (image == NULL) {//画像が読み込めなかった時
		SDL_UnlockSurface(image);//画像データを捨てる処理
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

	//ウィンドウ作る部
	window = SDL_CreateWindow("TestWindow", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_X, SCREEN_Y, 0);//ウィンドウ作成
	if (window == NULL) {//ウィンドウが作成できていなかったとき
		SDL_UnlockSurface(image);//画像データを捨てる処理
		SDL_DestroyWindow(window);//ウィンドウを捨てる処理
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

	//ウィンドウを使う為のデータを登録部
	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if (renderer == NULL) {//レンダーが作成できていなかったとき
		SDL_UnlockSurface(image);//画像データを捨てる処理
		SDL_DestroyRenderer(renderer);//ウィンドウの描画するための情報を削除する
		SDL_DestroyWindow(window);//ウィンドウを捨てる処理
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

	//画像を使う為のデータ入れる部
	image_texture = SDL_CreateTextureFromSurface(renderer, image);
	if (image_texture == NULL) {//テクスチャが作成できていなかったとき
		SDL_UnlockSurface(image);//画像データを捨てる処理
		SDL_DestroyTexture(image_texture);//画像を使う為のデータを削除する
		SDL_DestroyRenderer(renderer);//ウィンドウの描画するための情報を削除する
		SDL_DestroyWindow(window);//ウィンドウを捨てる処理
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

	//画像加工部
	int image_x, image_y;//画像の大きさを保存するための変数
	SDL_QueryTexture(image_texture, NULL, NULL, &image_x, &image_y);//テクスチャ(画像)の大きさを知る関数
	SDL_Rect imageRect = { 0,0,image_x,image_y };//画像の標準の大きさの指定
	SDL_Rect drawRect = { 0,0,image_x,image_y };//画像を描画する時の場所の指定

	//描画部
	SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);//指定のカラーで画面を塗る設定をする
		SDL_RenderClear(renderer);//実際にぬる
	SDL_RenderCopy(renderer, image_texture, &imageRect, &drawRect);//画像をウィンドウに描画するための情報の描画部分に送る
	SDL_RenderPresent(renderer);//ここで描画!

	SDL_Delay(2000);//2000ミリ秒(2秒)待つと伝える

	//終了処理
	SDL_UnlockSurface(image);//画像データを捨てる処理
	SDL_DestroyTexture(image_texture);//画像を使う為のデータを削除する
	SDL_DestroyRenderer(renderer);//ウィンドウの描画するための情報を削除する
	SDL_DestroyWindow(window);//ウィンドウを削除する
	IMG_Quit();//SDL_IMageを終了する処理
	SDL_Quit();//SDLを終了する処理

	return 0;//プログラムに終わりだから終了すると伝える
}

多い!と思った方、前回説明した所以外は丁寧に説明していくのでゆっくり理解していきましょう。

大まかなフロー

まずは大体やっている流れを把握していきましょう。エラー処理で多く見えますが、単純化すればそれほどやっていることは多くないです。

初期化→画像読み込み→ウィンドウ作成→ウィンドウからキャンバスを作る画像から使う為のデータを作る画像の大きさと場所を指定する構造体の作成これらを元に描画→終了処理

赤いラインを引いた所が新しく追加した所です。うん、少ない! 書いた後にちょっと多いなと思ったけど

実際のコードの解説

前回解説した部分は省いて解説していきますので、ウィンドウの作り方がわからない人は前回参照お願いします!

初期化

//初期化部
	if (SDL_Init(SDL_INIT_VIDEO)&&IMG_Init(IMG_INIT_JPG|IMG_INIT_PNG)) {//初期化
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

前回解説した部分がなんかパワーアップしてますね。新しくIMG_Init(IMG_INIT_JPG|IMG_INIT_PNG)、IMG_Quit()というのが増えてます。IMG_Initは中身のフラッグに応じて使える画像のフォーマットを増やしてくれます。この場合はjpg,pngですね。これを使った時には終了する時にIMG_Quit()が必要です。面倒くさい

画像の読み込み

	image = IMG_Load("sample.png");// sample.pngをimageに読み込む
	if (image==NULL) {//画像が読み込めなかった時
		SDL_UnlockSurface(image);//画像データを捨てる処理
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

画像読み込みはIMG_Load(ファイルの場所と名前)で行えます。わかりづらいですが、その情報はSDL_Surface*に保存します。この場合はファイルの場所はソースファイルと同じ場所なので、特にその情報はいりません。ファイルの名前だけで大丈夫です。要らなくなったらSDL_UnlockSurface(SDL_Surface*)で捨てます。エラー処理はいつものに前述をしたものを足してものです。勘が良い方は気づいたかもしれませんが、エラー処理のコードはだるま式に増えていきます…..

ウィンドウを作る

前回の通りです!ただ、SDL_UnlockSurfaceが増えている所に注意!

ウィンドウからキャンバスを作る

//ウィンドウを使う為のデータを登録部
	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if (renderer == NULL) {//レンダーが作成できていなかったとき
		SDL_UnlockSurface(image);//画像データを捨てる処理
		SDL_DestroyRenderer(renderer);//ウィンドウの描画するための情報を削除する
		SDL_DestroyWindow(window);//ウィンドウを捨てる処理
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

ウィンドウの情報を元にキャンバス(レンダー)を作ります。SDL_Renderer*にその情報は保存します。まんまです。SDL_CreateRenderer(SDL_Window*,int index,SDL_RendererFlags)で作ります。ウィンドウの情報を入れて作るわけですね。インデックスはグラボとか内臓GPUとかを選ぶ部分です。滅多なことがない限り-1で指定しておけば一番良い奴を選んでくれます。フラグに関しては長くなるので割愛。今回はハードウェアアクセラレーションを使うという意味のSDL_RENDERER_ACCELERATEDを指定してます。捨てるときはる処理
SDL_DestroyRenderer(SDL_Renderer*)で捨てます。例によってエラー処理はそれを加えた前の破棄処理全部入れてます…

画像から使う為のデータを作る

	//画像を使う為のデータ入れる部
	image_texture = SDL_CreateTextureFromSurface(renderer, image);
	if (image_texture == NULL) {//テクスチャが作成できていなかったとき
		SDL_UnlockSurface(image);//画像データを捨てる処理
		SDL_DestroyTexture(image_texture);//画像を使う為のデータを削除する
		SDL_DestroyRenderer(renderer);//ウィンドウの描画するための情報を削除する
		SDL_DestroyWindow(window);//ウィンドウを捨てる処理
		IMG_Quit();//SDL_IMageを終了する処理
		SDL_Quit();//SDLを終了する処理
		return 1;//プログラムにエラーだから終了すると伝える
	}

SDL_CreateTextureFromSurface(SDL_Renderer*,SDL_Surface*)で画像を描画で使う為のデータ(テクスチャ)を作ります。SDL_Texture*に作ったデータを格納します。キャンバスに合う形で画像を加工して作ってくる関数です。SDL_DestroyTexture(SDL_Texture*)でその情報を捨てます。例によってエラー処理はそれを加えた前の破棄処理全部(ry

画像の大きさと場所を指定する構造体の作成

//画像加工部
	int image_x, image_y;//画像の大きさを保存するための変数
	SDL_QueryTexture(image_texture, NULL, NULL, &image_x, &image_y);//テクスチャ(画像)の大きさを知る関数
	SDL_Rect imageRect = {0,0,image_x,image_y };//画像の標準の大きさの指定
	SDL_Rect drawRect = {0,0,image_x,image_y };//画像を描画する時の場所の指定

SDL_QueryTexture(SDL_Texture*, NULL, NULL, &int 画像xの大きさ, &int 画像yの大きさ)テクスチャ(画像)の大きさを得ることが出来ます。下ではそれを利用して画像の大きさと場所をしていしてます。SDL_Rect={int 始点x,int 始点y,int 終点x,int 終点y}です。そう考えるとそのままの大きさで、一番端に表示しようとしているのがわかりますね。

描画

//描画部
	SDL_SetRenderDrawColor(renderer, 255,255,255,255);//指定のカラーで画面を塗る設定をする
	SDL_RenderClear(renderer);//実際に塗る
	SDL_RenderCopy(renderer, image_texture, &imageRect, &drawRect);//画像をウィンドウに描画するための情報の描画部分に送る
	SDL_RenderPresent(renderer);//ここで描画!
	
	SDL_Delay(2000);//2000ミリ秒(2秒)待つと伝える

SDL_SetRenderDrawColor(SDL_Renderer*,int Red,int Green,int Blue,int a)指定した色で画面をまずは塗ります。光の三原色であるRGBで指定します。255が最大値です。今回は無駄に白で塗っています。自分の好みで塗るのもいいでしょう。

SDL_RenderClear(renderer)で実際に塗ります。

SDL_RenderCopy(SDL_Renderer*,SDL_Texture*, &SDL_Rect(画像の大きさ), &SDL_Rect(画像の場所))実際に描画するキャンバスであるRendererに画像の情報を送ります。

SDL_RenderPresent(SDL_Renderer*)描画を反映させる関数です!やっとか

SDL_Delay安定の待ちを入れる関数です。

終了処理

//終了処理
	SDL_UnlockSurface(image);//画像データを捨てる処理
	SDL_DestroyTexture(image_texture);//画像を使う為のデータを削除する
	SDL_DestroyRenderer(renderer);//ウィンドウの描画するための情報を削除する
	SDL_DestroyWindow(window);//ウィンドウを削除する
	IMG_Quit();//SDL_IMageを終了する処理
	SDL_Quit();//SDLを終了する処理

	return 0;//プログラムに終わりだから終了すると伝える

今までのエラー処理と同じことをここでします。全部削除するべきものを削除するわけですね。これでこういう風に表示されてたら終わりです!

次回

こんなの毎回画像を表示する毎に書けますか?私は書く気力はありません。なので、自作関数とファイル分けを利用して綺麗にしていきます。大掃除ですね!ちょっと早いけど

コメント

タイトルとURLをコピーしました