67
ソフトウェア実験(ユーザインタフェイス) プログラミング資料 1 SDL プログラミング ― GUI を中心に ― 2011 9 徳島大学工学部知能情報工学科 光原弘幸 目次 1.SDL とは ・・・・・2 2.SDL のインストール ・・・・・3 3.SDL プログラミング ・・・・・4 3.1 事始め ・・・・・4 3.2 GUI プログラミング ・・・・・6 3.2.1 ウィンドウ ・・・・・6 3.2.2 色指定 ・・・・・8 3.2.3 図形描画 ・・・・・9 3.2.4 文字(列)描画 ・・・・・11 3.2.5 描画反映 ・・・・・15 3.2.6 サーフェイスのロックと解放 ・・・・・15 3.3 イベントドリブン・プログラミング ・・・・・17 3.3.1 イベントの取得方法 ・・・・・17 3.3.2 イベントの種類 ・・・・・18 3.3.3 各イベントの処理方法 ・・・・・20 3.4 画像関係プログラミング ・・・・・29 3.4.1 サーフェイス ・・・・・29 3.4.2 ピクセルフォーマット ・・・・・30 3.4.3 空のサーフェイス生成 ・・・・・31 3.4.4 画像ファイルの読み込み ・・・・・32 3.4.5 画像ファイルの表示 ・・・・・33 3.4.6 画像の透過 ・・・・・34 3.4.7 アニメーション ・・・・・36 3.4.8 ダブルバッファリング ・・・・・39 3.5 サウンドプログラミング ・・・・・41 3.5.1 SDL_mixer ライブラリによるサウンドプログラミング ・・・・・41 3.6 マルチスレッドプログラミング ・・・・・46 3.6.1 マルチスレッドとは ・・・・・46 3.6.2 注意点 ・・・・・46 3.6.3 マルチスレッドプログラムの基本的な流れ ・・・・・47 3.6.4 相互排除 ・・・・・50 3.7 OpenGL ・・・・・53 3.7.1 OpenGL とは ・・・・・53 3.7.2 OpenGL プログラムの基本的な流れ ・・・・・53 3.7.3 3 次元 CG ・・・・・61 3.7.4 テクスチャ ・・・・・63

SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

1

SDLプログラミング ― GUIを中心に ―

2011年 9月

徳島大学工学部知能情報工学科

光原弘幸

目次

1.SDLとは ・・・・・2

2.SDLのインストール ・・・・・3

3.SDLプログラミング ・・・・・4

3.1 事始め ・・・・・4

3.2 GUIプログラミング ・・・・・6

3.2.1 ウィンドウ ・・・・・6

3.2.2 色指定 ・・・・・8

3.2.3 図形描画 ・・・・・9

3.2.4 文字(列)描画 ・・・・・11

3.2.5 描画反映 ・・・・・15

3.2.6 サーフェイスのロックと解放 ・・・・・15

3.3 イベントドリブン・プログラミング ・・・・・17

3.3.1 イベントの取得方法 ・・・・・17

3.3.2 イベントの種類 ・・・・・18

3.3.3 各イベントの処理方法 ・・・・・20

3.4 画像関係プログラミング ・・・・・29

3.4.1 サーフェイス ・・・・・29

3.4.2 ピクセルフォーマット ・・・・・30

3.4.3 空のサーフェイス生成 ・・・・・31

3.4.4 画像ファイルの読み込み ・・・・・32

3.4.5 画像ファイルの表示 ・・・・・33

3.4.6 画像の透過 ・・・・・34

3.4.7 アニメーション ・・・・・36

3.4.8 ダブルバッファリング ・・・・・39

3.5 サウンドプログラミング ・・・・・41

3.5.1 SDL_mixer ライブラリによるサウンドプログラミング ・・・・・41

3.6 マルチスレッドプログラミング ・・・・・46

3.6.1 マルチスレッドとは ・・・・・46

3.6.2 注意点 ・・・・・46

3.6.3 マルチスレッドプログラムの基本的な流れ ・・・・・47

3.6.4 相互排除 ・・・・・50

3.7 OpenGL ・・・・・53

3.7.1 OpenGLとは ・・・・・53

3.7.2 OpenGL プログラムの基本的な流れ ・・・・・53

3.7.3 3次元 CG ・・・・・61

3.7.4 テクスチャ ・・・・・63

Page 2: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

2

1.SDLとは

SDL(Simple DirectMedia Layer)は,Sam Lantinga 氏(アメリカのゲーム開発会社 Blizzard

Entertainmentの Lead Software Engineer)によって(ほぼ一人で?)開発されているマルチメディ

アを簡単かつ直接に扱うためのライブラリです.グラフィックス, サウンド, ジョイスティック, スレッ

ド, タイマなどの機能を扱うことができ,ゲーム開発に多く用いられています.

Linux はもちろん Windows や MacOS など主要なオペレーティングシステム(OS)で使用可能で,

C 言語だけでなく C++,Javaや Perlといった多くのプログラミング言語にも移植されています.ハー

ドウェアに依存する部分が多く,できるだけ簡潔にプログラムしたい画面描画等に関して,SDLは OS

の描画ライブラリ(API:Application Programming Interface)の上位インタフェイスとして提供され

ます.Linux であれば基本的には Xlib,Windows であれば DirectX の上位インタフェイスとして提供

されます(図1).

図1 SDLの位置づけ

http://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:SDL_Layers.svg

2010 年度までのソフトウェア実験では,Xlib ライブラリを用いてプログラミングしていましたが,

2011年度からは SDLを用います.これにより,より抽象的で利用しやすいインタフェイスを介してゲ

ーム開発に取り組むことができ,効率よく多彩なゲームを開発することが期待されます.移植性の高さ

も長所の一つです.

SDLの詳細は

http://www.libsdl.org/

http://wiki.libsdl.org/moin.cgi/FrontPage

http://www.tacoworks.jp/software/SDLdoc-jp/html/index.html

などを参照してください.

Page 3: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

3

2.SDLのインストール

電算室には SDL が既にインストールされていますが,個人の Linux(Ubuntu)マシンには SDL は

インストールされていないかもしれません.そこで,SDL のインストール方法を簡単に説明します.

SDLソースコードを取得してコンパイルすることでインストール可能ですが,ここではより簡単な方法

(apt-get)でインストールしてみます.なお,以下では Ver.1.2を対象にしていますが,最新の Ver.1.3

を試してみてもよいでしょう.

SDL は処理に応じて細分化されています. 主なライブラリ(プロジェクト)を以下に示します.そ

の他のライブラリは http://www.libsdl.org/projects/を参照のこと.

SDL-1.2 SDLの基本処理を担当する

SDL_image 画像処理を担当する

SDL_mixer 音声処理を担当する

SDL_ttf フォント処理(True Type Font)を担当する

SDL_gfx 図形処理を担当する

SDL_net ネットワーク処理を担当する

root権限でインストールします.環境によっては,sudoは必要ないかもしれません)

SDLのインストール

$ sudo apt-get install libsdl1.2-dev

SDL_imageのインストール

$ sudo apt-get install libsdl-image1.2-dev

SDL_ttfのインストール

$ sudo apt-get install libsdl-ttf2.0-dev

SDL_mixerのインストール

$ sudo apt-get install libsdl-mixer1.2-dev

SDL_gfxのインストール

$ sudo apt-get install libsdl-gfx1.2-dev

SDL_netのインストール

$ sudo apt-get install libsdl-net1.2-dev

Page 4: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

4

3.SDLプログラミング

ここでは C言語を採用し,基本的な SDLプログラミングを見ていきます.

3.1 事始め

(1)SDLライブラリの利用を宣言

SDLライブラリを利用するには,まず,ソースコード内で SDLに関するヘッダファイルをインクル

ードします.これにより,SDLの関数や定数,グローバル変数などの定義がプリプロセッサとしてソー

スコードに埋め込まれます.環境によって異なるかもしれませんが,ヘッダファイルは

/usr/include/SDL/に格納されています.関数の使用法などを確認したい時は,ヘッダファイルの中身を

見てください.

インクルード文の例

(2)main関数の記述

SDLを利用する場合,main関数の引数を次のように記述します.

(3)ライブラリの読み込みと初期化 コンパイルオプション:-lSDL

SDLプログラムではまず,SDLの読み込みと初期化を行います.初期化には,SDL_Init関数を使い

ます.

SDL_Init関数(#include <SDL.h>)

SDLを初期化します.

int SDL_Init(Uint32 flags);

引数:初期化対象のシンボルキー(サブシステム)

flagsにはシンボルキーを指定します.シンボルキーは利用するサブシステムごとに異なりま

す.以下に主なシンボルキーを示します.

SDL_INIT_VIDEO:ビデオを初期化

SDL_INIT_AUDIO:オーディオを初期化

SDL_INIT_TIMER:タイマを初期化

SDL_Init(SDL_INIT_VIDEO); // SDL_INIT_VIDEOを指定してビデオを初期化

#include <SDL/SDL.h>

#include <SDL/SDL_image.h>

#include <SDL/SDL_mixer.h>

#include <SDL/SDL_ttf.h>

#include <SDL/SDL_gfxPrimitives.h>

#include <SDL/SDL_net.h>

main(int argc, char *argv[]){ }

Page 5: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

5

SDL_INIT_JOYSTICK:ジョイスティックを初期化

SDL_INIT_CDROM:CD-ROMを初期化

SDL_INIT_EVERYTHING:以上すべてのサブシステムを初期化

SDL_INIT_NOPARACHUTE:致命的エラーのシグナルをSDLが捕捉しないようにする.

シンボルキーを複数指定する場合は,OR演算子でつなげます.

返値が負数でなければ,初期化は成功です.

SDL_InitSubSystem関数で SDL_Init関数による初期化後でもサブシステムの初期化を行え

ます.

(4)ライブラリの終了 コンパイルオプション:-lSDL

SDLの利用を終了するには,SDL_Quit関数またはSDL_QuitSubSystem関数を使います.SDL_Quit

関数はすべてのライブラリを終了する場合に使われ,SDL_QuitSubSystem関数は終了したいサブシス

テムのみを指定できます.

SDL_Quit関数(#include <SDL.h>)

SDLを終了します.

void SDL_Quit(void);

(5)コンパイル

SDL プログラムの基本的なコンパイルオプションとして,次の-l と-L を指定します.なお,ディレ

クトリは Linux環境によって異なる場合があるので,自分の環境を確認してみてください.

例えば,基本的な SDL ライブラリを用いる場合のコンパイルオプションの意味は次のようになりま

す.

-lSDL libSDL.aまたは libSDL.soをライブラリのリンク対象とする

-L/usr/lib ライブラリ検索ディレクトリのリストに/usr/libを追加

利用するライブラリの違いにより,例えば次のようにコンパイルオプションを追加指定します.

if ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0 ) { // エラー処理 }

$ gcc test.c –lSDL –L/usr/lib

$ gcc test.c –lSDL -lSDL_image -L/usr/lib

$ gcc test.c –lSDL –lSDL_mixer –L/usr/lib

$ gcc test.c –lSDL –lSDL_net –L/usr/lib

$ gcc test.c –lSDL –lGL –L/usr/lib

SDL_Quit();

SDL_QuitSubSystem(SDL_INIT_VIDEO);

Page 6: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

6

3.2 GUIプログラミング

ゲームを開発するとなると,自ずとユーザインタフェイスは GUI(Graphical User Interface)にな

るでしょう.ここでは,GUIの基礎となるウィンドウ処理について説明します.

3.2.1 ウィンドウ

(1)ウィンドウの生成 コンパイルオプション:-lSDL

ウィンドウを生成するために,まず SDL_Surface型ポインタを宣言します.そして,SDL_Init関数

で SDL_INIT_VIDEO サブシステムを初期化し,SDL_SetVideoMode 関数でウィンドウサイズなどを

指定して,ウィンドウを生成します.

SDL_SetVideoMode関数(#include <SDL.h>)

ウィンドウを生成します.

SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);

引数(第 1から):ウィンドウの横幅(ピクセル),高さ,色深度,フラグ(描画内容のメモリ上

の表現)

例えば,次のように記述すると,320×240のウィンドウを生成します.

色深度(bpp)はピクセル深度とも呼ばれ,色表現ビット数(1 ピクセルのビット数)のこと

で,32を指定すれば 1677万色(RGB各 256階調+α値)となります.

flagsには,一般的なウィンドウを生成したい場合は SDL_SWSURFACE,フルスクリーン(メ

ニューバーなし)の場合は SDL_FULLSCREENを指定します.フラグは OR演算子でつなげ

ることができます.その他のフラグについては次の URLなどを参照してください.

http://www.tacoworks.jp/software/SDLdoc-jp/html/sdlsetvideomode.html

http://www.tacoworks.jp/software/SDLdoc-jp/html/sdlsurface.html

返値として,生成が成功した場合は(ウィンドウの情報が格納された)SDL_Surface構造体を,

失敗した場合は NULLを返します.

SDL_GetVideoInfo関数や SDL_ListModes関数で環境の色情報を取得することができます.

ここで,サーフェイス(Surface)という用語が気になります.SDL では,画像などディスプレイに

表示できる対象をサーフェイスという単位で扱います.これは描画(画面)データを格納するフレーム

SDL_Surface *window; // ウィンドウを示すポインタを宣言

if(SDL_Init(SDL_INIT_VIDEO) < 0) { // SDL初期化

printf("failed to initialize SDL.¥n");

exit(-1);

}

if((window = SDL_SetVideoMode(320, 240, 32, SDL_SWSURFACE)) == NULL) { // ウ

ィンドウ生成

printf("failed to initialize videomode.¥n");

exit(-1);

}

Page 7: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

7

バッファのことで,メモリ上(メインメモリや VRAM:Video RAM)に領域が確保されます.

SDL_Surface *window;のように,ウィンドウもサーフェイス(この場合スクリーンバッファ)とし

て扱われます.ウィンドウなどサーフェイスの形式を指定するのが SDL_SetVideoMode 関数というわ

けです.サーフェイスは重要な概念であり以降で度々登場しますので,3.4.1で詳しく説明します.

サーフェイスの生成は SDL_SetVideoMode関数以外に,SDL_CreateRGBSurface関数(3.4.3

で説明)や SDL_CreateRGBSurfaceFrom関数(説明は省略)があります.

(2)ウィンドウの設定 コンパイルオプション:-lSDL

以下に示す関数を使って,生成したウィンドウに対していくつかの設定を行うことができます.

SDL_WM_SetCaption 関数(#include <SDL.h>)

ウィンドウタイトル名(タイトルバーに表示)とアイコン名を指定します.

void SDL_WM_SetCaption(const char *title, const char *icon);

引数(第 1から):ウィンドウタイトル名,アイコン名

SDL_WM_SetIcon関数(#include <SDL.h>)

ウィンドウをアイコン化した際のアイコンを指定します.

void SDL_WM_SetIcon(SDL_Surface *icon, Uint8 *mask);

引数(第 1から):アイコン画像,アイコンの形状を表すビットマスク

*iconは,SDL_LoadBMP 関数といった画像読み込み用関数を用いて指定します.画像読み込

みについては,3.4.4で説明します.

*mask(ビットマスク)は,画像の背景透過処理に用いる特別な画像(描画情報)のことです.

SDL_WM_SetIcon関数では,通常 NULLを指定すればよいです(ビットマスクについての説

明を省略します)..

SDL_WM_IconifyWindow関数(#include <SDL.h>)

ウィンドウをアイコン化/最小化します.

int SDL_WM_IconifyWindow(void);

返値として,アイコン化が失敗した場合は 0を,成功した場合は 0以外の整数を返します.

SDL_WM_ToggleFullScreen 関数(#include <SDL.h>)

ウィンドウ(アプリケーション)をウィンドウモードまたはフルスクリーンに切り替えます.

int SDL_WM_ToggleFullScreen(SDL_Surface *surface);

引数:切り替え対象サーフェイス(ウィンドウ)

返値として,失敗した場合は 0を,成功した場合は 1を返します.

if (SDL_WM_ToggleFullScreen(window)==0){ }

if (SDL_WM_IconifyWindow()==0){ } // 関数単独でも使える

SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), NULL);

SDL_WM_SetCaption(“SDL Game”, “Software Exp”);

Page 8: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

8

SDL_WM_GrabInput関数(#include <SDL.h>)

マウスとキーボードの入力をウィンドウに閉じ込めます.

SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode);

引数:Grabモード

Grabモードは列挙体で,SDL_GRAB_QUERY(変更なし),SDL_GRAB_OFF(閉じ込

めない),SDL_GRAB_ON(閉じ込める)の3つ値をもちます.SDL_GRAB_ON を指定

すると,常にウィンドウに対して入力のフォーカスが当たることになります.

3.2.2 色指定

色を指定する方法はいくつかあります.ここでは,色を指定してウィンドウ(サーフェイス)を塗る

ために,16進数で色を指定する方法と,SDL_MapRGB 関数,SDL_MapRGBA 関数を使った色指定を

紹介します.

(1)16進数による色指定

ウィンドウの色深度が 32ビットである時,16進数で色を指定するには,関数の色指定の引数に,0x

から始まる6または8桁の 0~f を指定します.最初の 2 桁が R,次の 2桁が G,その次の 2桁が Bと

なり,最後の 2桁がある場合はサーフェイス全体のα値を表します.

α値とは,ピクセル単位で色を混ぜ合わせる比率,言い換えれば,色を重ねる際の透明度を指定する

値で,0~255 までの値をとります.完全に透明にする際は最後の 2 桁に 00(0)を記述し,不透明に

する(混ぜ合わせない)際は ff(255)を記述します.α値については,3.4.5でも説明します.

例えば,サーフェイス内の矩形領域を塗り潰す SDL_FillRect 関数を使って,ウィンドウ全体を白で

塗り潰す場合は次のようになります.SDL_FillRect関数は3.2.3で説明します.

(2)SDL_MapRGB関数,SDL_MapRGBA 関数による色指定 コンパイルオプション:-lSDL

SDL_MapRGB関数(#include <SDL.h>)

色を RGB値で指定します(色を一意の整数値に変換します).

Uint32 SDL_MapRGB(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b);

引数(第 1から):対象サーフェイスのピクセルフォーマット,R値,G値,B値

例えば,ウィンドウ全体を黒で塗り潰す場合は次のようになります.

*fmt(ピクセルフォーマット)は,サーフェイスの各種ピクセル情報が格納されている構造体

です.NULLでも構いません.

α値を考慮できないので,意図しない色が指定されることがあります(?).

SDL_FillRect( window, NULL, SDL_MapRGB(window->format, 0,0,0));

SDL_FillRect(window, NULL, 0xffffff); // 白で塗り潰し

SDL_FillRect(window, NULL, 0xffffffff); // 白で塗り潰し(不透明)

SDL_FillRect(window, NULL, 0xffffff00); // 白?で塗り潰し(完全透明)

SDL_WM_GrabInput(SDL_GRAB_ON);

Page 9: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

9

SDL_MapRGBA関数(#include <SDL.h>)

色をα値も含め RGB値で指定します(色を一意の整数値に変換します).

Uint32 SDL_MapRGBA(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b, Uint8 a);

引数(第 1から):対象サーフェイスのピクセルフォーマット,R値,G値,B値,α値

α値を考慮して色指定ができます.

3.2.3 図形描画

(1)SDL_gfxライブラリを用いた図形描画 コンパイルオプション:-lSDL_gfx

ここでは,SDL_gfxライブラリを用いてウィンドウ(サーフェイス)に単純な図形(点, 線分, 矩形,

円など)を描画する方法(関数)をいくつか紹介します.SDL_gfxライブラリの詳細,紹介できなかっ

た他の関数については,http://www.ferzkopp.net/joomla/content/view/19/14/を参照してください.

pixelColor関数(#include <SDL_gfxPrimitives.h>)

サーフェイス(ウィンドウ)に点(ピクセル)を描画します.

int pixelColor(SDL_Surface * dst, Sint16 x, Sint16 y, Uint32 color);

引数(第 1から):描画サーフェイス,x座標(int型),y座標(int型),色(整数値指定)

返値として,描画が成功した場合に 0を,失敗した場合に-1を返します.

hlineColor関数(#include <SDL_gfxPrimitives.h>)

サーフェイスに水平線を描画します.

int hlineColor(SDL_Surface * dst, Sint16 x1, Sint16 x2, Sint16 y, Uint32 color);

引数(第 1から):描画サーフェイス,始点 x座標,終点 x座標,y座標,色

垂直線は,vlineColor関数を用いて描きます.

rectangleColor関数(#include <SDL_gfxPrimitives.h>)

四角形(塗り潰さない)を描画します.

int rectangleColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32

color);

引数(第 1から):描画サーフェイス,始点 x座標,始点 y座標,終点 x座標,終点 y座標,色

塗り潰した四角形は,boxColor関数を用いて描きます.

SDL_FillRect( window, NULL, SDL_MapRGBA(window->format, 0,0,0,255));

rectangleColor(window, 0, 0, 100, 100, 0xaaaaaaff);

hlineColor(window, 100, 200, 100, 0x000000ff);

pixelColor(window, 100, 100, 0xffffffff);

Page 10: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

10

lineColor関数(#include <SDL_gfxPrimitives.h>)

直線を描画します.

int lineColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Uint32 color);

引数(第 1から):描画サーフェイス,始点 x座標,始点 y座標,終点 x座標,終点 y座標,色

circleColor関数(#include <SDL_gfxPrimitives.h>)

円(塗り潰さない)を描画します.

int circleColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Uint32 color);

引数(第 1から):描画サーフェイス,中心 x座標,中心 y座標,半径,色

塗り潰した円は,filledCircleColor関数を用いて描きます.

pieColor関数(#include <SDL_gfxPrimitives.h>)

半円を描画します.

int pieColor(SDL_Surface * dst, Sint16 x, Sint16 y, Sint16 r, Sint16 start, Sint16 end, Uint32

color);

引数(第 1から):描画サーフェイス,中心 x座標,中心 y座標,半径,開始角,終端角,色

塗り潰した半円は,filledPieColor関数を用いて描きます.

どのように半円が描かれるか,引数に様々な値を入れて確認してみてください.

trigonColor関数(#include <SDL_gfxPrimitives.h>)

三角形を描画します.

int trigonColor(SDL_Surface * dst, Sint16 x1, Sint16 y1, Sint16 x2, Sint16 y2, Sint16 x3,

Sint16 y3, Uint32 color);

引数(第 1から):描画サーフェイス,第1点 x座標,第1点 y座標,第2点 x座標,第2点 y

座標第3点 x座標,第3点 y座標半径,色

塗り潰した三角形は,filledTrigonColor関数を用いて描きます.

stringColor関数(#include <SDL_gfxPrimitives.h>)

文字列を描画します.

int stringColor(SDL_Surface * dst, Sint16 x, Sint16 y, char *c, Uint32 color);

引数(第 1から):描画サーフェイス,描画(先頭)x座標,描画 y座標,描画文字列,色

stringRGBA関数も文字列を描画します.

一文字だけの描画は,characterColor関数を用いて描きます.

stringColor(window, 500, 100, “TEST”, 0x00ffffff);

trigonColor(window, 0, 0, 50, 0, 25, 25, 0xffff00ff);

pieColor(window, 100, 100, 50, 0, 90, 0x0000ffff);

circleColor(window, 200, 200, 10, 0x00ff00ff);

lineColor(window, 0, 0, 100, 100, 0xff0000ff));

Page 11: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

11

gfxPrimitivesSetFont関数(#include <SDL_gfxPrimitives.h>)

描画文字列のフォントを設定します.

void gfxPrimitivesSetFont(const void *fontdata, int cw, int ch);

引数(第 1から):フォントデータ(アドレス),フォント幅(バイト表現),フォント高さ

*fontdata(フォントデータ)は,SDL_gfxPrimitives_font.h 等をインクルードして参照しま

す(?).

フォント指定による文字(列)描画は,TrueType フォントを扱える SDL_ttf ライブラリを用

いる方が無難です.SDL_ttfライブラリを用いた文字列描画は3.2.4で説明します.

(2)SDL_FillRect関数を使った図形描画 コンパイルオプション:-lSDL

SDL の基本ライブラリには,SDL_gfx ライブラリのような充実した図形描画関数は用意されていま

せん.ただ,SDL_FillRect関数を工夫して使えば,ある程度の図形描画は可能です.

SDL_FillRect関数(#include <SDL.h>)

与えられた矩形領域と色で高速にサーフェイスを塗り潰します.

int SDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color);

引数(第 1から):塗り潰すサーフェイス,矩形領域(x, y, w, h)を示す構造体のアドレス,色

*dstrect(矩形領域)は,矩形範囲開始(左上端)の x座標,矩形範囲開始の y座標,幅(x),

高さ(y)を SDL_Rect構造体のアドレスで渡して指定します.

例えば,次のようなサンプルでは,点をいくつも描画して線を描いています.

3.2.4 文字(列)描画 コンパイルオプション:-lSDL_ttf

文字(列)をウィンドウに表示することは,ゲームといわず,すべてのソフトウェアに必須です.そ

の際,フォントを指定して文字を表示したいでしょう.3.2.3で説明した SDL_gfx ライブラリに

よる文字描画ではフォント指定が難しい面があります.

そこで,SDL_ttf ライブラリを用いて,フォント指定による文字描画を行います.ttf とは,

TrueTypeFont(以下,TTF)のことで,拡大縮小してもジャギー(ギザギザ)が生じず解像度に依存

SDL_FillRect(window, &a, 0xff0000ff);

int i;

SDL_Rect a={0, 0, 1, 1};

for(i=0; i<=100; i++){

a.x=i; a.y=i;

SDL_FillRect(window, &a, 0xff0000ff);

}

gfxPrimitivesSetFont(&gfxPrimitivesFontdata, 10, 10);

Page 12: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

12

しない文字フォントです.Linuxでは,/usr/share/fontsディレクトリに*.ttfというファイルで TTFが

格納されています.フリーの TTF もあり,インターネットからダウンロードできます.どのようなフ

ォント(見た目)なのかや日本語フォントなのかを確認するには,TTFファイルをフォントビューアで

開いてみてください(Ubuntu では TTFファイルをダブルクリックするとビューアが開きます).

SDL_ttfライブラリには,ウィンドウに文字を描画するための関数が用意されています.以下,代表

的な関数を説明します.詳しくは,http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html を参照

して下さい.

(1)TTF使用の準備

TTFを利用するにはまず,SDL_ttf.hファイルをインクルードします.

次に,TTFを格納するための TTF_Font型ポインタを宣言します.

そして,TTF_Init関数で TTFを使用するための初期化を行います.

TTF_Init関数(#include <SDL_ttf.h>)

TTFを使用するための初期化を行います.

int TTF_Init();

返値として,初期化が成功した場合は 0を,失敗した場合は-1を返します.

(2)TTFの読み込み

TTFの読み込みには,TTF_OpenFont関数を用います.

TTF_OpenFont関数(#include <SDL_ttf.h>)

使用するフォント名とフォントサイズを指定してフォントを読み込みます.

TTF_Font *TTF_OpenFont(const char *file, int ptsize);

引数(第 1から):TTFファイル名,フォントサイズ(ポイント)

返値として,読み込みが成功した場合は指定したフォントが TTF_Font型変数に格納されます.

(3)文字の描画

さて,後は TTFで文字を描画するだけなのですが,この描画が少し面倒です.というのも,SDL_ttf

ライブラリを用いる文字描画では,文字が画像のように扱われるからです.画像関連プログラミングに

ついては3.4で説明しますが,文字描画のイメージとしては,文字を書き込んだ画像をウィンドウに

貼り付けるようなものです.ここでは,深いことは考えずに「こうやって文字を描画するんだ」と方法

を覚えることにしましょう.

文字の描画に用いるのは TTF_Render 関数です.ただ,TTF_Render に続けて,使用する文字コー

ドやフォントの荒っぽさに応じて,接尾語のような記述が足され,実際の関数名(12関数)を成します.

font = TTF_OpenFont("kochi-gothic-subst.ttf", 24);

TTF_Font* font;

TTF_Init();

#include <SDL/SDL_ttf.h>

Page 13: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

13

日本語を描画したい際は,文字コードがUTF8の TTF_Render関数を使うと良いでしょう(例えば,

TTF_RenderUTF8_Solid関数).フォントの荒っぽさという表現はピンと来ないでしょうから,プログ

ラムして描画された文字列を見て確かめてみて下さい(Solid が粗く,その他はなめらかになります.

描画速度は Soildが最も速くなります).

TTF_Render関数(#include <SDL_ttf.h>)

指定した文字列を,指定した色と読み込んだフォントで描画します.

SDL_Surface *TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg);

SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg,

SDL_Color bg);

SDL_Surface *TTF_RenderText_Blended(TTF_Font *font, const char *text, SDL_Color fg);

SDL_Surface *TTF_RenderUTF8_Solid(TTF_Font *font, const char *text, SDL_Color fg);

SDL_Surface *TTF_RenderUTF8_Shaded(TTF_Font *font, const char *text, SDL_Color fg,

SDL_Color bg);

SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font, const char *text, SDL_Color fg);

SDL_Surface *TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg);

SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font, const char *text, SDL_Color fg,

SDL_Color bg);

SDL_Surface *TTF_Render Text_Blended(TTF_Font *font, const char *text, SDL_Color fg);

SDL_Surface *TTF_RenderUNICODE_Solid(TTF_Font *font, const char *text, SDL_Color

fg);

SDL_Surface *TTF_RenderUNICODE_Shaded(TTF_Font *font, const char *text, SDL_Color

fg, SDL_Color bg);

SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font, const char *text,

SDL_Color fg);

引数(第 1から):読み込んだフォント,描画する文字列,文字色,背景色(*_Shadedのみ)

stringsという変数(任意の変数名で OKです)は,SDL_Surface型で宣言したもので,文字

列がサーフェイスに描画されます.

第 3,第 4 引数の色は SDL_Color 型の構造体で指定します.この構造体は以下のようなメン

バ変数から構成され,フォーマットに依存しない色表現が可能です.

返値として,文字列の描画さえたサーフェイスを返します..

typedef struct{

Uint8 r; // 赤 Uint8 g; // 緑 Uint8 b; // 青

Uint8 unused;

} SDL_Color;

SDL_Surface *window, *strings;

SDL_Color white = {0xFF, 0xFF, 0xFF}; // フォントの色を白に指定

strings = TTF_RenderUTF8_Blended(font, "Hello!", white);

Page 14: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

14

サーフェイスに描画をしても,ウィンドウに文字列は表示されません.それは,文字列が描画されたサ

ーフェイスが非可視だからです.そこで,描画された文字列を可視化,すなわち,ウィンドウに貼り付

けて表示する必要があります.サーフェイスの描画データをウィンドウに貼り付けるには,

SDL_BlitSurface関数を用います.この関数の詳しい使用法は,3.4.5で説明します.

次の例は,描画した文字列の描画始点(0, 0)からの縦横のサイズを見て文字列(画像)の範囲をコ

ピーし,ウィンドウに貼り付ける処理を示しています(図3.1).

図3.1 文字列描画とウィンドウへの貼り付け

(4)TTFの使用終了

TTF の使用を終了するために,読み込んだ TTF を閉じる TTF_CloseFont 関数と SDL_ttf ライブラ

リの使用を終了する TTF_Quit関数が用意されています.

TTF_CloseFont関数(#include <SDL_ttf.h>)

使用した(読み込んだ)TTFを閉じます.

void TTF_CloseFont(TTF_Font *font);

引数:読み込んだフォント(TTF_Font型)

TTF_Quit関数(#include <SDL_ttf.h>)

SDL_ttfライブラリの使用を終了します.

void TTF_Quit();

font = TTF_OpenFont("kochi-gothic-subst.ttf", 24);

TTF_CloseFont(font);

SDL_Rect src_rect = { 0, 0, strings->w, strings->h }; // コピー元

SDL_Rect dst_rect = { 0, 0 }; // コピー先

SDL_BlitSurface(strings, &src_rect, window, &dst_rect); // ウィンドウの左上

(0, 0)を基点に文字列を貼り付ける

Page 15: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

15

3.2.5 描画反映 コンパイルオプション:-lSDL

さて,3.2.3で図形描画について,3.2.4で文字列描画について述べましたが,図形描画関

数を記述しただけでは,サーフェイス(ウィンドウ)に図形が描画されません.これは,メモリ上の画

面データに対して描画データを書き込んだだけであって,描画データを画面(VRAM)に反映させるこ

と(関数)が必要になります.言い換えれば,メモリ上の画面データをディスプレイに対して更新する

必要があります.

描画データを画面に反映(更新)させる関数には,次のようなものがあります.

SDL_UpdateRect関数(#include <SDL.h>)

サーフェイスの矩形範囲を指定して描画データを画面に反映(更新)させます.

void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h);

引数(第 1 から):描画を更新するサーフェイス,矩形範囲開始(左上端)の x 座標,矩形範囲

開始の y座標,幅(x),高さ(y)

第2引数~第5引数をすべて 0とすると,サーフェイス全体の描画データを反映させます.

SDL_Flip 関数(#include <SDL.h>)

ダブルバッファをサポートするハードウェアにおいてスクリーンバッファを交換します.

int SDL_Flip(SDL_Surface *screen);

引数:スクリーンバッファを交換するサーフェイス

ダブルバッファがサポートされていないハードウェアにおいて,SDL_Flip (window)は

SDL_UpdateRect(window, 0, 0, 0, 0)と等価です.

失敗した場合は 0を,成功した場合は-1を返します.

ダブルバッファ,スクリーンバッファについては,3.4.8で説明します.

3.2.6 サーフェイスのロックと解放 コンパイルオプション:-lSDL

同時並行で複数のスレッド(処理)が行われている場合(つまり,マルチスレッドの場合),あるス

レッドがフレームバッファ(サーフェイスのメモリ上の画面データ)を更新している際,他のスレッド

も同じフレームバッファを更新することができます.これは,意図しない描画が行われてしまう可能性

があることを意味しています.そこで,フレームバッファを更新する際,他のスレッドがそのフレーム

バッファを更新できないようにするために,ロックという仕組み(排他制御,相互排除)が用意されて

います.

マルチスレッドプログラミングについては,3.6で説明します.

SDL_Flip (window);

SDL_UpdateRect(window, 0, 0, 200, 200);

Page 16: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

16

(1)ロック

SDL_gfxライブラリなど描画関数を実行(記述)する直前で,次のような関数を用いて設定します.

SDL_LockSurface関数(#include <SDL.h>)

サーフェイスの画面データの更新を禁止します.

int SDL_LockSurface(SDL_Surface *surface);

引数:ロックするサーフェイス

返値として,ロックに成功した場合は 0を,失敗した場合は-1を返します.

SDL_UnlockSurface関数(#include <SDL.h>)

サーフェイスへの画面データの更新禁止を解除します.

void SDL_UnlockSurface(SDL_Surface *surface);

引数:アンロック(ロックを解除)するサーフェイス

描画をロックしたら,描画処理が終わった時点ですぐにアンロックするようにしましょう

(SDL_UnlockSurface後に SDL_UpdateRectして,すぐに描画内容をウィンドウに反映しま

しょう).

サーフェイスに対して複数のロックを設定することができます.ただ,SDL_LockSurface と

SDL_UnlockSurface は一対になっていますので,ロックした数だけアンロックしなければ,

他のスレッドがそのサーフェイスに対して描画できないままになります.

(2)解放

不必要なサーフェイスをそのままにしておくと,メモリ領域を確保したままとなり,メモリの無駄遣

いになってしまいます.したがって,不必要なサーフェイスは解放(削除)すべきです.次のような関

数を用いてサーフェイスを解放します.

SDL_FreeSurface関数(#include <SDL.h>)

サーフェイスを解放します.

void SDL_FreeSurface(SDL_Surface *surface);

引数:解放するサーフェイス

SDL_LockSurface(window);

SDL_FreeSurface(window);

SDL_UnlockSurface(window);

Page 17: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

17

3.3 イベントドリブン・プログラミング

GUIベースのソフトウェアはイベントドリブンの形式を採用するものがほとんどです.イベントドリ

ブンとは,イベント(ユーザの操作,システム(OS)の割り込みなど)を検知し,検知したイベント

に対応する処理を行うプログラムの動作方法,または概念のことをいいます.

SDLによるゲーム開発でも避けては通れないイベントドリブン・プログラミング.イベントには多く

の種類がありますが,ここでは基本的なイベントである,マウス,キーボード,ジョイスティックに関

するイベントを習得しましょう.

3.3.1 イベントの取得方法 コンパイルオプション:-lSDL

SDLからイベントを取得するためには,SDL_Init(SDL_INIT_VIDEO);によりビデオを初期化する必

要があります.加えて,SDL_Event型構造体を宣言し,無限ループ内で SDL_PollEvent関数などによ

りイベントを所得(検知)します.イベントの種類は SDL_Event型構造体の typeメンバ(変数)に格

納されるので,この typeメンバを見て,イベントの種類ごとの処理を記述することになります.

イベントは頻繁に発生します.SDL は発生したイベントをイベントキューに格納していき,

SDL_PollEvent関数を介してイベントをイベントキューから取り出し,イベントごとの処理を実行しま

す.

基本的なイベントドリブン・プログラミングの流れは次のようになります.

SDL_PollEvent関数(#include <SDL.h>)

イベントを取得します(イベントキューに溜まっているイベントを取り出します).

int SDL_PollEvent(SDL_Event *event);

引数:イベント情報

返値として,イベントがある時は 1を,ない時は 0を返します.

同様のイベント取得を処理する SDL_PeepEvents 関数では,イベントキューに溜めることの

できるイベント数や取得対象イベントのフィルタリングなどを設定できます.

if(SDL_PollEvent(&event)){ }

SDL_Event event; // イベント情報が格納される構造体を宣言

SDL_Init(SDL_INIT_VIDEO); // ビデオの初期化

while(1){ // 無限ループ

// イベントを取得したら

if(SDL_PollEvent(&event)){

switch (event.type) {

// 以下にイベントごとの処理を記述

case SDL_MOUSEMOTION: // マウスが移動した時

....

}

}

}

Page 18: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

18

基本的には OSが各種イベントをイベントキューに溜める(送る)ことになりますが,プログラマが

意図的にイベントキューにイベントを溜めることもできます.

SDL_PushEvent関数(#include <SDL.h>)

イベントキューに意図的にイベントを送ります.

int SDL_PushEvent(SDL_Event *event);

引数:イベントキューに送るイベントのポインタ(イベントの種類は3.3.2を参照)

返値として,成功の時は 0を,失敗の時は-1を返します.

3.3.2 イベントの種類

SDL_Event型構造体の定義(メンバ)は次のようになります.

typeには,イベントの種類が格納されます.主なイベント種類を表3.1に示します.

SDL_Event quit_event = { SDL_QUIT }; // 対象のイベントを格納

if(SDL_PushEvent(&quit_event)==0){ } // 意図的にイベントをキューに送る

typedef union{

Uint8 type;

SDL_ActiveEvent active;

SDL_KeyboardEvent key;

SDL_MouseMotionEvent motion;

SDL_MouseButtonEvent button;

SDL_JoyAxisEvent jaxis;

SDL_JoyBallEvent jball;

SDL_JoyHatEvent jhat;

SDL_JoyButtonEvent jbutton;

SDL_ResizeEvent resize;

SDL_ExposeEvent expose;

SDL_QuitEvent quit;

SDL_UserEvent user;

SDL_SywWMEvent syswm;

} SDL_Event;

Page 19: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

19

表3.1 イベントの種類(typeの値とイベントの関係)

type イベント

active ウィンドウの見え方が変更されたら

key キーボードが押されたら

motion マウスが動いたら

button マウスのボタンが押されたら

jaxis ジョイスティックの軸に変化があったら

jball ジョイスティックのトラックボールが移動したら

jhat ジョイスティックの十字キーが押されたら

jbutton ジョイスティックのボタンが押されたら

resize ウィンドウサイズが変更されたら

expose ウィンドウの重なりを検出したら

quit イベント取得終了が要請されたら

user ユーザによって作成されたイベントを取得したら

SDL_Event 型構造体は union(共用体)となっており,データ領域を節約できるようになっていま

す.SDL_Event 型構造体のメンバとして更に構造体があります.各イベントの情報を格納する構造体

を表3.2に示します.

表3.2 各イベントの情報を格納する構造体

typeの値 構造体

SDL_ACTIVEEVENT SDL_ActiveEvent

SDL_KEYDOWN/UP SDL_KeyboardEvent

SDL_MOUSEMOTION SDL_MouseMotionEvent

SDL_MOUSEBUTTONDOWN/UP SDL_MouseButtonEvent

SDL_JOYAXISMOTION SDL_JoyAxisEvent

SDL_JOYBALLMOTION SDL_JoyBallEvent

SDL_JOYHATMOTION SDL_JoyHatEvent

SDL_JOYBUTTONDOWN/UP SDL_JoyButtonEvent

SDL_VIDEORESIZE SDL_ResizeEvent

SDL_VIDEOEXPOSE SDL_ExposeEvent

SDL_USEREVENT SDL_UserEvent

SDL_QUIT SDL_QuitEvent

これらのメンバ構造体は,イベントの type に応じて上書きされます.例えば,motion という type

のイベントを取得した場合,SDL_MouseMotionEvent 構造体により,SDL_Event 型構造体が上書き

されます.SDL_MouseMotionEvent構造体にも typeメンバが存在しているため,イベント種類の情報

は失われません.では,ここで SDL_MouseMotionEvent構造体の定義(メンバ)を見てみましょう.

Page 20: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

20

type には SDL_MOUSEMOTION,state には現在のボタンの状態,x と y にはマウスの x 座標と y

座標,xrelと yrelには x方向と y方向の相対的な動きが格納されます.

3.3.3 各イベントの処理方法

(1)ウィンドウイベント コンパイルオプション:-lSDL

SDL_ActiveEvent構造体は次のメンバから構成されます(表3.3).

表3.3 SDL_ActiveEvent構造体のメンバ

メンバ [型] 値

type [Uint8] SDL_ACTIVEEVENT

gain [Uint8] 無効の時は 0,有効の時は 1

state [Uint8] マウスのフォーカスを得たか失った時=SDL_APPMOUSEFOCUS

入力フォーカスを得たか失った時=SDL_APPINPUTFOCUS

ウィンドウがアイコン化された(gain=0)かアイコン化から復元された

(gain=1)時=SDL_APPACTIVE

ウィンドウイベントのプログラム例

ウィンドウ(アプリケーション)の状態取得は次の関数でも可能です.

SDL_Event event;

while(1){

if(SDL_PollEvent(&event)){

switch (event.type) {

// ウィンドウイベントの場合

case SDL_ACTIVEEVENT:

// 状態を表示

printf("State=%d¥n",event.active.state);

break;

}

}

}

typedef struct{

Uint8 type;

Uint8 state;

Uint16 x, y;

Sint16 xrel, yrel;

} SDL_MouseMotionEvent;

Page 21: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

21

SDL_GetAppState関数(#include <SDL.h>)

ウィンドウの状態を取得します.

Uint8 SDL_GetAppState(void);

(2)キーボードイベント コンパイルオプション:-lSDL

SDL_KeyboardEvent構造体は次のメンバから構成されます(表3.4).

表3.4 SDL_KeyboardEvent構造体のメンバ

メンバ [型] 値

type [Uint8] キーが押された時=SDL_KEYDOWN

キーが放された時=SDL_KEYUP

state [Uint8] キーの状態が「押されている」時=SDL_PRESSED

キーの状態が「放されている」時=SDL_RELEASED

keysym

[SDL_keysym]

SDL_keysym構造体

メンバ [型] 値

scanmode [Uint8] ハードウェア特有のスキャンコード

sym [SDLKey] SDL の仮想キーシンボル

mod [SDLMod] 現在のキーモディファイア

Unicode [Uint16] Unicodeに変換された文字

キーボードイベントで重要なのは,押された/放されたのがどのキーかを判別することです.キー判

別には,SDL_keysym構造体のsymメンバを見ます.symには,キーボードの各キーに対応するSDLKey

型のキー名が格納されています.代表的なキー名を表3.5に示します.

表3.5 SDLKey型の代表的なキー名

SDLKey(キー名) 対応キー

SDLK_BACKSPACE Back Spaceキー

SDLK_TAB Tabキー

SDLK_RETURN Return(Enter)キー

SDLK_ESCAPE Escキー

SDLK_SPACE スペースキー

SDLK_DELETE Deleteキー

SDLK_UP 上矢印キー(その他,DOWN, RIGHT, LEFTが下,右,左に対応)

SDLK_F1 F1キー(その他,F2,F3,…,F15)

SDLK_a Aキー(その他,b,c,d,…,z)

SDLK_0 0キー(その他,1,2,3,…,9)

SDLK_KP0 キーパッドの 0(その他,KP1,KP2,KP3,…,KP9)

if(SDL_GetAppState()==SDL_APPACTIVE){ }

Page 22: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

22

「Alt キーを押しながら○キー」や「Ctrl キーを押しながら○キー」といったキーの押し方をする

時があります.この「押しながら」を判別するために用意されているのが mod メンバであり,mod に

は SDLMod 型のキーモディファイアが格納されています.代表的なキーモディファイアを表3.6に

示します.

表3.6 SDLMod型の代表的なキー名

SDLMod(キー名) 対応キー

KMOD_NONE いずれのモディファイアにも当てはまらない

KMOD_RCTRL 右 Ctrlキー押下

KMOD_LCTRL 左 Ctrlキー押下

KMOD_RSHIFT 右 Shiftキー押下

KMOD_LSHIFT 左 Shiftキー押下

SDLK_RALT 右 Altキー押下

SDLK_LALT 左 Altキー押下

キーボードイベントのプログラム例

キーボードイベントに関して,以下のような関数が用意されています.

SDL_Event event;

while(1){

if(SDL_PollEvent(&event)){

switch (event.type) {

case SDL_KEYDOWN:

// 押されたキーの名前を表示

printf("The pressed key is %s.¥n",

SDL_GetKeyName(event.key.keysym.sym));

switch(event.key.keysym.sym){

// Escキーが押されたら終了

case SDLK_ESCAPE:

printf("ESCAPE key -> EXIT.¥n");

exit(-1);

break;

}

break;

}

}

}

Page 23: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

23

SDL_GetKeyName関数(#include <SDL.h>)

押されたキーの名前を取得します.

char *SDL_GetKeyName(SDLKey key);

引数:押されたキーの仮想シンボル

SDL_GetModState関数(#include <SDL.h>)

キーモディファイアの状態を取得します(どのキーが押下状態であるか).

SDLMod SDL_GetModState(void);

SDL_GetModState関数の返値と event.key.keysym.mod の値は同じですが,後者は値の更新

が若干遅れるようです.

SDL_EnableKeyRepeat関数(#include <SDL.h>)

キーリピートを設定します

int SDL_EnableKeyRepeat(int delay, int interval);

引数(第 1から):リピートが開始されるまでの時間(ミリ秒),リピート間隔の時間(ミリ秒)

キーリピートとは,キーを押したままの状態をどのように取り扱うかを表します.1 回だけキ

ーを押したようにイベント取得するか(キーリピート無効),何回もキーを押しているように

イベント取得するか(キーリピート有効)を設定します.

例えば,アクションゲームの開発において,矢印キーを押したままの状態でキャラクタを

ずっと移動させるには,キーリピートを有効にすることになるでしょう.

delay,intervalともに 0にすれば,キーリピートは無効になります.

(3)マウスイベント コンパイルオプション:-lSDL

マウスイベントは,マウスの移動に関する SDL_MouseMotionEvent構造体,マウスのボタン押下に

関する SDL_MouseButtonDown 構造体の2種類あり,それぞれ次のメンバから構成されます(表3.

7).

SDL_EnableKeyRepeat(1000,1000); // 1秒間隔のキーリピート設定,1秒後に開始

SDL_EnableKeyRepeat(0,0); // キーリピート無効を設定

if(SDL_GetModState()==KMOD_LSHIFT){ }

Printf(“%s”, SDL_GetKeyName(event.key.keysym.sym));

Page 24: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

24

表3.7 SDL_MouseMotionEvent構造体と SDL_MouseButtonDown構造体のメンバ

メンバ [型] 値

SDL_MouseMotionEvent構造体

type [Uint8] SDL_MOUSEMOTION

state [Uint8] 現在のボタンの状態

x, y [Uint16] 現在のマウスの x座標,y座標

xrel, yrel [Uint16] 相対的な移動量(x軸方向,y軸方向)

SDL_MouseButtonDown構造体

type [Uint8] マウスボタンが押された時=SDL_MOUSEBUTTONDOWN

マウスボタンが放された時=SDL_MOUSEBUTTONUP

button [Uint8] マウスボタンのインデックス値

左ボタン=SDL_BUTTON_LEFT

中央ボタン=SDL_BUTTON_MIDDLE

右ボタン=SDL_BUTTON_RIGHT

その他のボタンは,buttonメンバの値を printfで表示する等で確

認することができます.環境によって異なるかもしれませんが,

ホイールボタンについて,押下=2,上へスライド=4,下へスライ

ド=5になります.

state [Uint8] マウスボタンが押された時=SDL_PRESSED

マウスボタンが放された時=SDL_RELEASED

x, y [Uint16] ボタンが押された/放された時のマウスの x座標,y座標

マウスイベントのプログラム例

SDL_Event event;

while(1){

if(SDL_PollEvent(&event)){

switch (event.type) {

case SDL_MOUSEMOTION: // マウスが移動した時

printf("Mouse moved by %d,%d to (%d,%d)¥n",

event.motion.xrel, event.motion.yrel, event.motion.x, event.motion.y);

break;

case SDL_MOUSEBUTTONDOWN: // マウスボタンが押された時

printf("Mouse button %d pressed at (%d,%d)¥n",

event.button.button, event.button.x, event.button.y);

break;

}

}

}

Page 25: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

25

マウスの状態を取得する関数として,SDL_GetMouseState関数や SDL_GetRelativeMouseState関

数もありますが,上記のサンプルプログラムの方法でマウスの状態を取得できるため,割愛します.

(4)ジョイスティックイベント コンパイルオプション:-lSDL

アクションゲームやシューティングゲームなど,キーボードやマウスによる操作に向かないゲームも

あるでしょう.そんな時,ジョイスティックが活躍します.

まずは,ジョイスティックを特定・利用するための SDL_Joystick型構造体を宣言します(同時にイ

ベント処理するための SDL_Event型構造体も宣言しておきましょう).宣言後はこの構造体変数を引数

として,ジョイスティック処理関数を呼び出すことになります.

そして,SDL_Init 関数等で SDLを初期化します.シンボルキー(引数)には SDL_INIT_JOYSTICK

を指定します.ジョイスティックは一般的に,グラフィックス(GUI)を伴って処理されるので,ジョ

イスティックとともにグラフィックス関連の SDLも初期化することになるでしょう.

ジョイスティックに関係する関数を見ながら,処理の手順を習得しましょう.

SDL_NumJoysticks関数(#include <SDL.h>)

利用可能な(システムに接続された)ジョイスティックの数を取得します.

int SDL_NumJoysticks(void);

引数:なし

取得したジョイスティックの数が返値となります.

SDL_JoystickOpen関数(#include <SDL.h>)

ジョイスティックをオープンします.

SDL_Joystick *SDL_JoystickOpen(int index);

引数:システムにおけるジョイスティック番号(インデックス)

返値として,オープンが成功した場合は SDL_Joystick構造体,失敗した場合はNULLを返し

ます.

SDL_Joystick *joystick; // SDL_Joystick構造体

If( SDL_Init(SDL_INIT_VIDEO|SDL_INIT_JOYSTICK) < 0 ) { } // SDL 初期化(ビデ

オとジョイスティック)

SDL_Joystick *joystick;

// 接続されたジョイスティックがあるかチェック

if(SDL_NumJoysticks() > 0){

// ジョイスティックをオープンする

joystick = SDL_JoystickOpen(0);

}

Page 26: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

26

SDL_JoystickEventStat6e関数(#include <SDL.h>)

ジョイスティックイベントの検知を有効または無効にします.

int SDL_JoystickEventState(int state);

引数:ジョイスティックの状態

state(ジョイスティックの状態)には,SDL_QUERY,SDL_ENABLE,SDL_IGNORE が

あります.

ジョイスティックイベントを検知する場合は,SDL_ENABLE を渡します.

ジョイスティックイベントを検知しない場合は,SDL_IGNOREを渡します.

ジョイスティックイベントの現在の状態を知りたい場合は,SDL_QUERYを渡します.

返値として,現在(検知設定後)のジョイスティックの状態が返ります.

SDL_JoystickClose関数(#include <SDL.h>)

オープンされたジョイスティックをクローズします.

void SDL_JoystickClose(SDL_Joystick *joystick);

引数:クローズするジョイスティックの構造体

ジョイスティックイベントの処理の流れは,キーボードやマウスのイベントと同様に SDL_PollEvent

関数を用いてイベントを取得し,まず event.typeの値(表3.2)に応じて,以下の3つのジョイステ

ィックイベントに処理を振り分けます.

SDL_JOYAXISMOTION:方向キーまたはアナログキー(スティック)が押された時

SDL_JOYBUTTONDOWN:ボタンが押された時

SDL_JOYBUTTONUP:ボタンが離された時

次に,event.jaxis.axisや event.jbutton.buttonの値(どの軸なのか,どのボタンなのか)に応じて,

さらに処理を振り分けます.

SDL_JoystickClose(joystick);

SDL_JoystickEventState(SDL_ENABLE); // ジョイスティックイベントを検知できるよう

にする

Page 27: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

27

ジョイスティックイベントのプログラム例

while(1){

if(SDL_PollEvent(&event)){

switch (event.type) {

// 方向キーまたはアナログキー(スティック)が押された時

case SDL_JOYAXISMOTION:

printf("The axis ID of the operated key

is %d.¥n",event.jaxis.axis); // 操作された方向キーの方向軸を表示(0:アナログキー,

1:アナログキー,2:方向キー左右方向,3:方向キー上下方向)

// 方向軸に応じた処理

if(event.jaxis.axis==0){ }

else if(event.jaxis.axis==1){ }

else if(event.jaxis.axis==2){ }

else if(event.jaxis.axis==3){ }

printf("--- The axis value of the operated key

is %d.¥n",event.jaxis.value); // ジョイスティックの操作された方向キーの値を表示

(-32767(真左,真上)~32767(真右,真下))

break;

// ボタンが押された時

case SDL_JOYBUTTONDOWN:

printf("The ID of the pressed button is %d.¥n",

event.jbutton.button); // 押されたボタンの IDを表示(0から)

// ボタン IDに応じた処理

if(event.jbutton.button==0){ }

break;

// ボタンが離された時

case SDL_JOYBUTTONUP:

printf("The ID of the released button is %d.¥n",

event.jbutton.button); // 離されたボタンの IDを表示(0から)

// ボタン IDに応じた処理

if(event.jbutton.button==0){ }

break;

}

}

}

Page 28: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

28

ここで,ジョイスティックに関する情報を取得する関数をいくつか紹介します.

SDL_JoystickIndex関数(#include <SDL.h>)

ジョイスティックのインデックス番号を取得します.

int SDL_JoystickIndex(SDL_Joystick *joystick);

引数:番号を取得するジョイスティックの構造体

ジョイスティック番号が返値になります.

SDL_JoystickOpened 関数(#include <SDL.h>)

ジョイスティックがオープンされているか調べます.

int SDL_JoystickOpened(int index);

引数:ジョイスティック番号

返値として,既にオープンされている場合は 1を,オープンされていない場合は 0を返します.

SDL_JoystickNumButtons 関数(#include <SDL.h>)

既にオープンされているジョイスティックのボタン数を取得します.

int SDL_JoystickNumButtons(SDL_Joystick *joystick);

引数:ボタン数を取得するジョイスティックの構造体

ボタンの数が返値になります.

SDL_JoystickNumButtons 関数(#include <SDL.h>)

既にオープンされているジョイスティックのボタン数を取得します.

int SDL_JoystickNumButtons(SDL_Joystick *joystick);

引数:ボタン数を取得するジョイスティックの構造体

ボタンの数が返値になります.

SDL_JoystickGetButton関数(#include <SDL.h>)

指定されたジョイスティックの指定されたボタンの現在の情報を取得します.

Uint8 SDL_JoystickGetButton(SDL_Joystick *joystick, int button);

引数(第 1から):ジョイスティックの構造体,ボタン番号

返値として,ボタンが押されている時は 1 を,そうでない場合は 0 を返します.

SDL_JoystickNumAxes関数(#include <SDL.h>)

既にオープンされているジョイスティックの方向キーの軸の数を取得します.

int SDL_JoystickNumAxes(SDL_Joystick *joystick);

引数:軸の数を取得するジョイスティックの構造体

軸の数が返値になります.

SDL_JoystickGetAxis関数(#include <SDL.h>)

指定されたジョイスティックの方向キーの軸の現在の情報を取得します.

Sint16 SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis);

引数(第 1から):ジョイスティックの構造体,軸番号

通常,軸番号は X軸であれば 0,Y軸であれば 1で表現されます.

返値として,軸の現在の位置を表現する 16ビット符号付整数(-32768~32768)を返します.

ここで挙げた関数以外にも,ジョイスティックのトラックボールやハットスイッチに関する情報を取

得する関数があります.それらについては,

http://www.tacoworks.jp/software/SDLdoc-jp/html/joystick.html 等を参照してください.

Page 29: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

29

3.4 画像関連プログラミング

サーフェイス(ウィンドウ)に単純な図形を描画するだけでは,クオリティの高いゲームやユーザイ

ンタフェイスを提供できません.ここでは,サーフェイスについて説明した後,汎用的な画像ファイル

(BMP,JPEGや PNG等)の読み込み,表示,画像処理の方法,並びに,ゲームには必須といってい

いアニメーションやダブルバッファリングを実現する方法を説明します.

3.4.1 サーフェイス

サーフェイスは画像等の描画データを保存するフレームバッファ(メモリ領域)のことで,その実体

は画像データを構造体(SDL_Surface)に格納したものです.3.2.1(1)で説明した

SDL_SetVideoMode関数により,サーフェイスの幅や高さといった基本的なデータが SDL_Surface構

造体に格納され,サーフェイス(スクリーンバッファ)としてウィンドウを実体化することになります.

以下に SDL_Surface構造体を示します.表3.8に構造体のメンバを示します.

表3.8 SDL_Surface構造体のメンバ

メンバ [型] 値

flags [Uint32] サーフェイスのフラグ

format [SDL_PixelFormat] ピクセルフォーマット

w [int] サーフェイスの横幅

h [int] サーフェイスの高さ

pitch [Uint16] サーフェイスのスキャンライン(横幅)の長さ(バイト)

*pixels 実際のピクセルデータへのポインタ

clip_rect [SDL_Rect] SDL_SetClipRect関数により設定されるクリッピング矩形領域

flagsには,SDL_SetVideoMode関数の第4引数で指定したフラグがセットされます.

format(ピクセルフォーマット)については,3.4.2で説明します.

pitchには,サーフェイス1行分のバイト数がセットされます(wに依存します).

pixcelsは,メモリ上の描画データを直接操作する際に用います.

clip_rectの SDL_Rect型構造体は,以下のように矩形領域を定義します.

typedef struct SDL_Surface {

Uint32 flags; // 読込専用

SDL_PixelFormat *format; // 読込専用

int w, h; // 読込専用

Uint16 pitch; // 読込専用

void *pixels; // 読み書き

/* clipping information */

SDL_Rect clip_rect; //読込専用(クリッピング処理)

int refcount; // ほとんど読込(参照カウント)

} SDL_Surface;

Page 30: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

30

3.4.2 ピクセルフォーマット

サーフェイスはピクセルの集合です.SDL_Surface構造体にはサーフェイスのサイズなど概要データ

が格納されるのに対し,SDL_PixelFormat 構造体にはサーフェイスを構成するピクセル(の集合)の

データが格納されます.

以下に SDL_PixelFormat構造体を示します.表3.9に構造体のメンバを示します.

表3.8 SDL_PixcelFormat構造体のメンバ

メンバ [型] 値

*palette [SDL_Palette] SDL_Palette型構造体へのポインタまたはNULL

BitsPerPixel [Uint8] 各ピクセルを表現するためのビット数(通常,8, 16, 24か 32)

BytePerPixel [Uint8] 各ピクセルを表現するためのバイト数(通常,1, 2, 3か 4)

Rmask, … [Uint32] 各チャンネルの値を取得するのに使われるビットマスク

Rshift, … [Uint8] ピクセル値の各チャンネルの 2 進数の左シフト数

Rloss, … [Uint8] 各チャンネルで失われる精度

colorkey [Uint32] 透明ピクセルのピクセル値

alpha [Uint8] サーフェイス全体のα値

SDL_pallete構造体は 8ビットピクセルフォーマット用のカラーパレット(256色)で,SDL

がサーフェイス用に SDL_PixelFormat を確保するときに自動的に作られます.32 ビットの

ピクセルフォーマットは全く異なります.

colorkeyには,サーフェイスを重ねる際に前面のサーフェイスで透過させる色が格納されます.

ゲームキャラクタを背景に重ねて表示する処理に関係してきます.

alphaには,色を重ねる際の透明度であるα値が格納されます(α値は3.4.5で紹介).

typedef struct{

SDL_Palette *palette;

Uint8 BitsPerPixel;

Uint8 BytesPerPixel;

Uint32 Rmask, Gmask, Bmask, Amask;

Uint8 Rshift, Gshift, Bshift, Ashift;

Uint8 Rloss, Gloss, Bloss, Aloss;

Uint32 colorkey;

Uint8 alpha;

} SDL_PixelFormat;

typedef struct{

Sint16 x, y; // 矩形の左上端の座標と座標

Uint16 w, h; // 矩形の横幅と高さ

} SDL_Rect;

Page 31: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

31

ピクセルフォーマットに格納された情報を表示するプログラム例

ポインタを用いて構造体のメンバにアクセスするので,アロー演算子(->)を用います.

3.4.3 空のサーフェイス生成 コンパイルオプション:-lSDL

これまでは,ウィンドウとしてサーフェイスを生成しましたが,SDL_CreateRGBSurface 関数など

を用いて,何も描かれていない空のサーフェイスを生成することができます.

SDL_CreateRGBSurface関数(#include <SDL.h>)

空のサーフェイスを生成します.

SDL_Surface *SDL_CreateRGBSurface(Uint32 flags, int width, int height, int depth, Uint32

Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask);

引数(第 1 から):生成されるサーフェイスのタイプ,サーフェイスの横幅(ピクセル),高さ,

色深度,赤マスク,緑マスク,青マスク,αチャンネルマスク

flags に は , SDL_SWSURFACE , SDL_HWSURFACE , SDL_SRCCOLORKEY ,

SDL_SRCALPHAの1つ以上を指定します.複数指定時は,OR演算子でつなげます.

SDL_SWSURFACE:SDL はシステムメモリからサーフェイスを生成しようとします.

ピクセルレベルのアクセスのパフォーマンスを向上されますが,いくつかのハードウェア

による blit 転送の利点を得ることができなくなるかも知れません.

SDL_HWSURFACE:SDL はビデオメモリからサーフェイスを生成しようとします.こ

れにより SDL はビデオメモリ同士の bilt 転送 (しばしばアクセラレーションが利きま

す) の利点を得ることができます.

各マスクには,色指定ビット中のどのビットが R,G,B,またはαチャンネルかを fで指定し

ます.マスクは各マシンのエンディアン(Byte の並び)に依存します.すべて 0 を指定すれ

ば,マスクは考慮されません.

ビッグエンディアン:連続したデータを桁の大きい方から Byteごとに格納

リトルエンディアン:連続したデータを桁の大きい方から Byteごとに格納

返値として,サーフェイスの生成に成功した場合はそのサーフェイスを,失敗した場合は

NULLを返します.

ピクセルデータからサーフェイスを生成する SDL_CreateRGBSurfaceFrom 関数もあります

(説明は省略).

surface = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 32, 0xff000000,

0x00ff0000, 0x0000ff00, 0x000000ff);

printf("Colors = %d bpp ¥n", window->format->BitsPerPixel);

printf("Color Key = %d ¥n", window->format->colorkey);

printf("Alpha Value = %d ¥n", window->format->alpha);

Page 32: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

32

3.4.4 画像ファイルの読み込み

画像ファイルの読み込み(ロード)とは,用意したサーフェイス(メモリ領域)に画像ファイルの画

像データを格納することです.なお,画像データの格納のみですので,読み込んだからと言って,即座

にウィンドウに画像が表示されるわけではありません.

画像ファイルによって,読み込みに用いる関数が異なります.ここでは,BMP ファイルとそれ以外

のファイルに分けて関数を説明します.

(1)BMPファイルの読み込み コンパイルオプション:-lSDL

BMP ファイルはビットマップ画像の一つであり,基本的にデータ圧縮を行ないません.よって,デ

ータの欠損がなく鮮明な画像になりますが,ファイルサイズが大きくなってしまいます.

BMPファイルを読み込むには,SDL基本ライブラリの SDL_LoadBMP 関数を用います.

SDL_LoadBMP関数(#include <SDL.h>)

BMPファイルをサーフェイスに読み込みます.

SDL_Surface *SDL_LoadBMP(const char *file);

引数:画像ファイル名

返値として,生成が成功した場合は(画像の情報が格納された)SDL_Surface構造体を返しま

す.失敗した場合は NULLを返します.

読み込んだ画像を生成したウィンドウに代入しても,画像は表示されません.

サーフェイスの描画データを BMP ファイルに保存する SDL_SaveBMP 関数もあります.ゲ

ームでは例えば,プレイ画面スナップショットの保存等に使えそうです.

(2)BMP以外の画像ファイルの読み込み コンパイルオプション:-lSDL_image

BMPファイルはファイルサイズが大きいため,確保されるメモリ領域も大きくなり,それに伴い(特

に画像全体に対する)処理も重くなります.したがって,できるだけファイルサイズの小さい画像を使

うべきです.

BMP 以外の画像ファイルを読み込むには,SDL_image ライブラリの IMG_Load 関数を用います.

この関数で読み込める画像ファイルは,JPEG,PNG,GIF,BMP,XPM,LBM,PNM (PPM/PGM/PBM),

PCX,TGA,TIFFです.

IMG_Load関数(#include <SDL_image.h>)

画像ファイルをサーフェイスに読み込みます.

SDL_Surface *IMG_Load (const char *file);

引数:画像ファイル名

返値として,生成が成功した場合は(画像の情報が格納された)SDL_Surface構造体を返しま

す.失敗した場合は NULLを返します.

SDL_Surface *image;

image = IMG_Load (“a.jpg”); // a.jpgを imageサーフェイスに読み込む

SDL_Surface *image;

image = SDL_LoadBMP(“a.bmp”); // a.bmpを imageサーフェイスに読み込む

Page 33: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

33

3.4.5 画像ファイルの表示 コンパイルオプション:-lSDL_image

読み込んだ画像ファイルを表示するには,SDL_BlitSurface関数を用います.この関数は,サーフェ

イスからサーフェイスへデータを転送(コピー)します.つまり,画像データが格納されたサーフェイ

スを,(SDL_SetVideoMode関数で)目に見えるウィンドウとして生成したサーフェイスに転送するこ

とで,画像を表示します.なお,Blitとは,メモリ間(メインメモリと VRAM)で画像データをまとめ

て転送することを意味します.

SDL_BlitSurface関数(#include <SDL_image.h>)

転送元サーフェスから転送先サーフェスへ高速 blit 転送を行います.

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect

*dstrect);

引数(第 1 から):転送元サーフェイス,転送元の画像領域を表す SDL_Rect 型構造体,転送先

サーフェイス,転送先の画像を貼り付ける座標(左上端)を表す SDL_Rect 型構造体(ただし,

採用されるのは,x座標と y座標のみ)

返値として,転送に成功した場合は 0を,失敗した場合は-1を返します.-2の場合は画像の読

み込みからやり直す必要があります.

第 2 引数を NULL にすると,サーフェイス全体が転送領域になります.SDL_Rect 型構造体

により矩形領域が設定されていれば,その領域が転送領域になります.矩形領域は次のように,

SDL_Rect型構造体のメンバに値を代入することで設定されます.

第 4引数を NULLにすると,画像の転送先座標が(0, 0)になります.

データの転送先をウィンドウではなく他のサーフェイスにすることもできます.これは,ウィ

ンドウの描画更新時のちらつきを抑えるダブルバッファリングに応用できます.ダブルバッフ

ァリングについては3.4.8で説明します.

SDL_GetVideoSurface 関数を使うと,ウィンドウとして作成されたサーフェイスが指定され

ます.

画像をウィンドウに表示するには,SDL_Flip関数または SDL_UpdateRect関数で描画更新し

なければなりません.

SDL_Rect rect = {0, 0, 100, 100}; // 値代入の方法1

rect.x=10; rect.y=20; rect.w=50; rect.h=50; // 値代入の方法2

SDL_Rect src_rect = { 50, 50, 100, 100 }; // 転送元画像の領域を(50,50)-(150,150)

に設定

SDL_Rect dst_rect = { 200, 100 }; // 画像の転送先座標を(200, 100)に設定

SDL_BlitSurface(image, &src_rect, SDL_GetVideoSurface(), &dst_rect); //

imageサーフェイスの画像を,ウィンドウ(サーフェイス)に転送

SDL_Flip(SDL_GetVideoSurface()); // 描画更新

Page 34: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

34

3.4.6 画像の透過

ゲームでは,背景画像(背面)にキャラクタ画像(前面)を重ねて表示することが多々あります.基

本的に画像は矩形で表されますので,キャラクタ画像にはキャラクタ本体以外の部分,つまり,画面に

表示したくない,背景透過させたい部分がどうしても存在します.その際,キャラクタ本体だけが背景

画像の前面に表示され,キャラクタの周りは背景画像に対して透過させる必要があります.また,キャ

ラクタやメッセージウィンドウをいくらか透かして(半透明で)背景に重ねたい時もあるでしょう.

ここでは,3.4.2で紹介した透過ピクセルとα値による画像の透過処理を扱います.

(1)透過ピクセルの指定による透過 コンパイルオプション:-lSDL

サーフェイスに対して背景透過させる色を透過ピクセル(透明色)として指定し,背景透過を実現し

ます.したがって,サーフェイスに読み込む画像を作成する際に,背景透過させる色を予め決めておか

なければなりません.透過ピクセルの指定には,SDL_SetColorKey関数を用います.

SDL_SetColorKey関数(#include <SDL.h>)

blit 転送可能なサーフェイスの透明ピクセル(カラーキー)を指定します.

int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);

引数(第 1 から):透明ピクセルを指定するサーフェイス,重ね合わせのフラグ,透明ピクセル

透過を行いたい場合,flagには SDL_SRCCOLORKEYを記述します.

透過ピクセル値は,透過対象のサーフェイスのピクセルフォーマットで,SDL_MapRGB関数

などで透過する色を指定します.

返値として,通常は 0を,エラーの場合は-1を返します.

SDL_SetColorKey 関数は透過ピクセルを指定するだけなので,SDL_DisplayFormat 関数を用いてデ

ィスプレイフォーマットを変更し,透過を反映させる必要があります.

SDL_DisplayFormat関数(#include <SDL.h>)

サーフェスを画面表示するために,新しいピクセルフォーマットと色でサーフェイスを更新しま

す.

SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface);

引数:(ディスプレイフォーマットを)更新するサーフェイス

返値として,更新に成功した場合はサーフェイスを返します.更新に失敗するか,メモリを使

い切った場合は NULLを返します.

SDL_SetColorKey(image, SDL_SRCCOLORKEY, SDL_MapRGB(image->format, 255, 255,

255)); // 白を透過色に指定

image = SDL_DisplayFormat(image);

SDL_SetColorKey(image, SDL_SRCCOLORKEY, SDL_MapRGB(image->format, 255, 255,

255)); // 白を透過色に指定

Page 35: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

35

(2) 画像のαチャンネルの指定による透過

画像のαチャンネルとは,画像自体に埋め込まれた透過情報のことです.GIMP等のペイントソフト

でαチャンネルを指定して,背景透過画像を作成することができます.

画像のαチャンネルの指定による透過処理も SDL_SetColorKey関数を用いて行えます.背景透過画

像を読み込んだサーフェイスに対して,以下のように,透過色の引数に-1を指定すれば背景透過になり

ます.なお,この関数を用いなくても,背景透過画像がそのまま背景透過で表示されるようでもあるの

で,必要な際に用いてください..

画像のαチャンネルの指定による透過の場合,SDL_DisplayFormat 関数による透過色の反映

は必要ありません.

(3)α値の指定による透明化 コンパイルオプション:-lSDL

SDL_SetAlpha関数を持ちて,サーフェイス(全体)にα値という透明度を指定できます.

SDL_SetAlpha関数(#include <SDL.h>)

サーフェイスのα値を指定します.

int SDL_SetAlpha(SDL_Surface *surface, Uint32 flag, Uint8 alpha);

引数(第 1から):α値を指定するサーフェイス,重ね合わせのフラグ,α値

透明化を行いたい場合,flagには SDL_SRCALPHA を記述します.

alpha(α値)は 0~255までの値をとります.完全に透明にする際は 0を,不透明にする際は

255 を,半透明にするにはその度合いに合わせて 1~254 までの値を記述します.α値が 0 の

場合,SDL_BlitSurface関数などでウィンドウに画像を表示する処理をしたとしても,画像は

表示されません.

alphaは,色深度(bpp)が 32の場合には,各ピクセルに指定することができます.つまりピ

クセル値には R,G,Bに加えα値を含めることができます.

返値として,通常は 0を,エラーの場合は-1を返します.

(4)GIPMを用いた背景透過画像の作成

GIMPで背景透過画像を作成するには,新規作成または画像を開いて次の操作をします.

I. メニュー「レイヤー」→「透明部分」→「アルファチャンネルの追加」

II. 背景部分を削除する

新規画像の場合は,画像全体を選択して削除(または切り取り)する.そして,背景を考慮し

て画像を描いていく.

既存画像の場合は,背景にあたる部分を単色で塗り潰す.そして,ツールバー「Fuzzy Select

Tool」で背景部分を選択する.選択された背景部分が意図通りであれば,選択部分を削除する.

III. 画像(αチャンネルを保持できる形式で)を保存する.

SDL_SetColorKey(image, SDL_SRCCOLORKEY, -1); // 画像自体のαチャンネルを

透過色に指定

SDL_SetAlpha(image, SDL_SRCALPHA, 128);

Page 36: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

36

3.4.7 アニメーション

アニメーションは,ゲームにおける視覚的な要素として重要です.アニメーションのないゲームは見

た目の魅力に欠けるだけでなく,操作の分かりにくい(フィードバックに乏しい)ゲームにもなります.

アニメーションは一定時間ごとに画像を切り替えて表示することで実現している,と一般的に想像で

きます.では,“一定間隔ごとに” 画像を切り替えて表示するにはどうすればよいでしょうか?例えば,

For 文によるループでカウンタの値に応じた画像を表示することはできますが,必ずしも“一定間隔ご

と”にはなりません(マシン自体の性能や稼働プロセスによる CPUの負荷などに依存します).

そこで登場するのが,OS により一定間隔ごとに発生するタイマ割り込みです.タイマ割り込みを用

いることで,正確な画像の切り替え表示,つまり,タイミングの正確なアニメーションを実現できます.

タイマ割り込み処理はイベントドリブン・プログラミングに属します.タイマ割り込みの発生をイベ

ントとして受けて,適切な処理を行うからです.タイマ割り込み処理の基本を理解し,アニメーション

の実現方法を習得しましょう.

(1)タイマの作成・設定 コンパイルオプション:-lSDL

SDL では,SDL_AddTimer 関数によりタイマを作成・設定できます.複数のタイマを作成可能で,

作成されたタイマには,SDL_TimerID型のタイマ IDが割り振られます.

ここで忘れてはいけないのが,SDL_Init関数で SDL_INIT_TIMERサブシステムを指定する必要が

あることです.これを忘れると,コンパイルは通りますが,タイマを利用できません.加えて,無限ル

ープを用意していないと,タイマが起動・実行される前に即座にプログラム自体が終了してしまい,割

り込みが発生しません.

SDL_AddTimer関数(#include <SDL.h>)

指定ミリ秒経過した後にコールバック関数を呼び出すタイマを作成(追加)・設定します.

SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback callback, void

*param);

引数(第 1から):タイマ割り込みの間隔(ミリ秒),コールバック関数,コールバック関数に渡

す引数

例えば,1秒(1000 ミリ秒)間隔で,callbackfunc というコールバック関数を呼び出す場合は次の

ようになります.

*param には,コールバック関数へ渡す引数をアドレスとして指定します.引数を渡さない場

合はNULLにします.

SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); // ビデオ,タイマを利用するためのサ

ブシステムを初期化

// SDL_Init(SDL_INIT_ EVERYTHING); でも良い

SDL_TimerID timer_id; // タイマ IDを格納する変数

timer_id = SDL_AddTimer(1000, callbackfunc, NULL);

// 無限ループ

while(1){

}

Page 37: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

37

SDL_AddTimer関数とコールバック関数の例

コールバック変数には構造体も渡すことができます.つまり,構造体に複数の変数を持たせる

ことで,複数の変数をコールバック関数へ渡すことができます.

返値として,作成したタイマ IDを返します.エラーの場合は NULLを返します.

さて,コールバック関数は次のような特徴を有しています.

コールバック関数名は自分で決めることができます.

受け取る引数は,整数型(Uint32型)の intervalと void型ポインタ(汎用ポインタ)の*param

です.void型ポインタを採用することで,どのような型の引数でも関数に渡すことができます.

intervalには,コールバック関数が呼び出されたタイミングが自動的に格納されます.

*paramには,SDL_AddTimer関数で指定された第 3引数(ポインタのアドレス)が示す値が

格納されます.

*paramは void型ポインタで受け取っているので,実際の引数の型にキャスト演算子で型変換

して,新たな変数として格納します.

返値は,このコールバック関数が次に呼び出されるタイミング(Uint32型)が格納され返されま

す.

Uint32 callbackfunc(Uint32 interval, void *param){

printf("Callback Function 1 - 1sec Passed¥n");

return interval;

}

Uint32 callbackfunc2(Uint32 interval, void *param){

int *times = (int*)param; // 受け取った引数(パラメータ)をキャスト

演算子で intのポインタ型に変換し、変数 timesに格納

printf("Callback Function 2 - 2sec Passed (%d times)¥n", *times);

return interval;

}

int main(int argc, char* argv[]) {

SDL_TimerID timer_id1, timer_id2; // タイマ IDを格納する変数

int times;

timer_id1=SDL_AddTimer(1000, callbackfunc, NULL); // 1秒ごとにコー

ルバック関数を呼び出す(引数なし)

timer_id2=SDL_AddTimer(2000, callbackfunc2, &times); // 2秒ご

とにコールバック関数を呼び出す(int型変数のアドレスを引数として渡す)

}

Page 38: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

38

(2)タイマの削除 コンパイルオプション:-lSDL

SDL_AddTimer 関数で作成したタイマが使用されなくなったら,SDL_RemoveTimer 関数でタイマ

を削除しましょう.

SDL_RemoveTimer関数(#include <SDL.h>)

SDL_AddTimer関数で作成したタイマを削除します.

SDL_bool SDL_RemoveTimer(SDL_TimerID id);

引数:削除するタイマの ID

返値として,削除が成功した場合は 1を,失敗した場合は 0を返します.

(3)SDL_GetTicks関数を用いる方法 コンパイルオプション:-lSDL

SDL_GetTicks関数を用いて,割り込みのような処理ができます.この関数は,SDLライブラリ初期

化からの経過時間を取得します.OS が定期的に割り込みを発生させてコールバック関数を呼び出して

くれるのではなく,OS から取得した経過時間に応じてイベントループ内(無限ループ内)で処理を行

う(処理を記述する)ことで,割り込み処理を実現することになります.

SDL_GetTicks関数(#include <SDL.h>)

SDLライブラリ初期化からの経過ミリ秒数を取得します.

Uint32 SDL_GetTicks(void);

返値として,関数を呼び出した際の SDLライブラリ初期化からの経過ミリ秒数を返します.

(4)アニメーション処理の流れ

アニメーションには,キャラクタ等の動作パタン(フレームまたはコマ)を描いたアニメーション用

の画像が必要です.一般的には,図3.2のようにキャラクタの動作パタンを 1枚の画像に描くことに

なります.

図3.2 アニメーション用の画像

int Passed_time = SDL_GetTicks(); // 経過時間を整数型変数に格納

SDL_RemoveTimer(timer_id1); // timer_id1という IDをもつタイマを削除

Page 39: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

39

基本的な(大まかな)アニメーション処理の流れを以下に示します.

I. 描いたアニメーション用の画像(キャラクタ)や背景画像をサーフェイスに読み込む

(IMG_Load関数等).

II. タイマを起動させる(SDL_AddTimer関数).

III. 無限ループに入る.

IV. タイマで設定した時間間隔でコールバック関数が呼び出される.

i. 既に貼り付けられているキャラクタ画像があれば消去する.なお,背景画像にキャラク

タ画像を重ねて表示するアニメーションの場合,背景画像の描き直しが必要になる.

ii. 表示すべきキャラクタの動作パタンの画像領域を抽出し,ウィンドウ等のサーフェイス

の特定座標に貼り付ける(SDL_BlitSurface関数).

iii. キャラクタ画像を貼り付けたサーフェイスを画面に表示する(SDL_Flip関数等).

iv. 次に抽出する画像領域(領域番号など)を格納する.

V. 割り込みを待ってⅣへ戻る.

3.4.8 ダブルバッファリング

アニメーションするキャラクタが画面に縦横無尽に移動するゲームでは,キャラクタ表示の際に画面

のちらつきが気になることがあります.このような画面のちらつきは,複数のキャラクタ(の画像)を

一体ずつウィンドウに貼り付けて表示することが主な発生原因です.2,3体のキャラクタならあまり

気にならないかもしれませんが,数十,数百というキャラクタであれば,ちらつき対策が必須となりま

す(例えば,弾幕シューティングゲームでは顕著でしょう).

画面のちらつき対策として有効なのが,ダブルバッファリングです.ダブルバッファリングとは,キ

ャラクタを一体ずつ表示するのはなく,非可視(見えない)フレームバッファにすべてのキャラクタ(背

景等も含めて)を貼り付けて(描いて)から,ウィンドウ(可視サーフェイス)に表示する方法です(図

3.3).

画像を読み込んだサーフェイス(非可視フレームバッファ)のデータを SDL_BlitSurface関数でウィ

ンドウサーフェイスに転送(Blit)し,SDL_Flip 関数等でウィンドウに表示(反映)する手法も,あ

る種のダブルバッファリングと言えます.

図3.3 ダブルバッファリング

Page 40: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

40

ここでは,ダブルバッファリング専用の非可視サーフェイス(フレームバッファ)を用意した方法を

紹介します.まず,SDL_Surface型の変数を用意し,SDL_CreateRGBSurface関数等で非可視サーフ

ェイスを作成します.

次に,SDL_BlitSurface関数を用いて,画像を読み込んだサーフェイスから非可視サーフェイスへ画

像を貼り付けます.このような処理は,別の処理の裏側で前もって行っておくと効率的です.以下の例

では, 背景画像を非可視サーフェイスに貼り付けた後,複数のキャラクタ画像をさらに貼り付けてい

ます.

そして,あるタイミングで非可視サーフェイスの内容をウィンドウサーフェイスに転送し,画面に表

示します.

上記でソフトウェア的なダブルバッファリング処理を説明しましたが,SDLにはハードウェアによる

ダブルバッファリングが備わっています.ハードウェア SDL_SetVideoMode 関数でウィンドウを生成

する際,第 4引数の flagsに SDL_HWSURFACE | SDL_DOUBLEBUFを指定すると,ウィンドウサ

ーフェイスが表示用(表)とバッファリング用(裏)の 2枚生成されます.画像をウィンドウサーフェ

イスに転送しても表示用には転送されず、バッファリング用に転送されます.すべての画像を転送し終

わったら、SDL_Flip 関数で明示的にウィンドウに画像を表示します.こうすることで,ウィンドウの

表と裏を瞬時に変えて,描画時のちらつきを無くします.なお,ダブルバッファリングにおける高速転

送はフルスクリーン時(SDL_FULLSCREEN)にしか使用できません。

window = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_FULLSCREEN |

SDL_DOUBLEBUF);

SDL_BlitSurface(buffer, NULL, window, NULL); // bufferを windowに貼り付

ける

SDL_Flip(window); // 画面(ウィンドウ)に画像を表示(反映)

SDL_BlitSurface(image1, NULL, buffer, NULL); // 背景画像を bufferに貼り付ける

for(i=0; i<= 600; i=i+40){

dst_rect.x=i;

for(j=0; j<=440; j=j+40){

dst_rect.y=j;

SDL_BlitSurface(image2, &src_rect, buffer, &dst_rect); //

キャラクタ画像を bufferに貼り付ける

}

}

SDL_Surface *buffer;

buffer = SDL_CreateRGBSurface(SDL_HWSURFACE, 640, 480, 32, 0, 0, 0, 0);

Page 41: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

41

3.5 サウンドプログラミング

ゲームには BGMや効果音といったサウンド(オーディオ)は欠かせません.サウンド処理には,SDL

(基本)ライブラリと SDL_mixer ライブラリを使う方法がありますが,ここでは後者によるサウンド

プログラミングについて説明します.

3.5.1 SDL_mixerライブラリによるサウンドプログラミング

SDL_mixer ライブラリで扱えるサウンドは,8 ビット/16 ビット,符号あり/符号ありの WAV フ

ァイル,MP3,OGGやMIDI等ファイルです.

サウンドプログラミングをする前に,コンピュータにサウンドカードが接続され機能しているか確認

して下さい.また,スピーカやヘッドフォンが装備/接続されているかも確認して下さい.音量が小さ

く設定されていると,スピーカからサウンドが聞こえず,プログラムのバグと間違えてしまうこともあ

ります.

(1)初期化 コンパイルオプション:-lSDL_mixer

SDL を初期化する SDL_Init 関数の引数に SDL_INIT_AUDIO を指定することで,サウンドを扱え

るようになります.

次に,サウンドライブラリの初期化,つまり指定された条件でオーディオデバイスを開くために,

Mix_OpenAudio関数を用います.

Mix_OpenAudio関数(#include <SDL_mixer.h>)

指定された条件でオーディオデバイスを開いてサウンドを初期化します.

int Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize);

引数(第 1 から):読み込みたいサウンドのサンプリング周波数,サウンドデータのフォーマッ

ト,チャンネル数,音楽再生に使うメモリサイズ(Byte)

frequency,format,channelsは表3.9に示した数値を指定できます.

frequency には,デフォルト値である MIX_DEFAULT_FREQUENCY(22050Hz)を指定す

ることもできます.

format には,デフォルト値である MIX_DEFAULT_FORMAT(AUDIO_S16SYS)を指定す

ることもできます.

chunksize の値を小さくすると音飛びが発生しますが,大きすぎると他の処理に影響が及びま

す.1024Byteがデフォルト値のようです.

返値として,成功した場合は 0を,失敗した場合は-1を返します.

Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024);

if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) { }

Page 42: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

42

表3.9 オーディオデバイスを開く際の条件

メンバ [型] 値

frequency [int] サンプリング周波数(Hz:秒間のサンプル数).一般的な値は 11025,22050,

44100で,大きい値にするほど高音質になります.

format [Uint16] オーディオデータのフォーマット

符号なし 8ビット = AUDIO_U8

符号あり 8ビット = AUDIO_S8

符号なし 16 ビット (リトルエンディアン)= AUDIO_U16 または

AUDIO_U16LSB

符号あり 16 ビット(リトルエンディアン) = AUDIO_S16 または

AUDIO_S16LSB

符号なし 16ビット (ビッグエンディアン)= AUDIO_U16MSB

符号あり 16ビット(ビッグエンディアン) = AUDIO_S16MSB

システムのエンディアンに依存 = AUDIO_U16SYS または

AUDIO_S16SYS

channels [int] チャンネル数

モノラル = 1

ステレオ=2

chunksize [int] Chunkに対し,音楽再生に使うメモリサイズ(Byte)

(2)サウンドの読み込み

サウンドの読み込みには,Music 系と Chunk 系の2種類があります.Music 系では,サウンドファ

イルのデータをメモリに全て読み込まず,ファイルからデコードしながらの再生になります.よって,

メモリを節約できるため,ゲームの BGM といったある程度長いサウンド用に用いられます.一方,

Chunk 系では,すべてのサウンドデータをメモリに読み込んで再生します.よって,高い即応性が要

求される効果音に用いられます.

Music 型読み込みでは,Mix_Music 型ポインタを宣言します.そして,Mix_LoadMUS 関数を用い

て,用意したサウンドを読み込みます.

Mix_LoadMUS関数(#include <SDL_mixer.h>)

サウンドファイルを読み込みます.

Mix_Music *Mix_LoadMUS(const char *file);

引数:読み込むサウンドファイル名

サウンドファイルをメモリに全て読み込まず,ファイルからデコードしながらの再生になりま

す.よって,メモリを節約できます.

返値として,失敗した場合はNULLを返します.

Mix_Music *music;

music = Mix_LoadMUS(“test.wav”); // test.wavを Music系で読み込み

Page 43: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

43

Chunk型読み込みでは,Mix_Chunk型ポインタを宣言します.そして,Mix_LoadWAV関数を用い

て,用意したサウンドを読み込みます.

Mix_LoadWAV 関数(#include <SDL_mixer.h>)

サウンドファイルを読み込みます.

Mix_Chunk *Mix_LoadWAV(const char *fname);

引数:読み込むサウンドファイル名

返値として,失敗した場合はNULLを返します?

(3)サウンドの再生・制御

Music 系サウンドの再生や停止,巻き戻しや音量調節を行う関数を以下に示します.なお,Chunk

系サウンドは再生と停止を行う関数のみ示します.

Mix_PlayMusic関数(#include <SDL_mixer.h>)

読み込んだMusic系サウンドを再生します.

int Mix_PlayMusic(Mix_Music *music, int loops);

引数(第 1から):読み込んだサウンド,再生のループ回数

loopsに-1を指定すると,無限に再生されます.

返値として,成功した場合は 0を,失敗した場合は-1を返します.

Mix_HaltMusic関数(#include <SDL_mixer.h>)

再生中のMusic系サウンドを停止します.

int Mix_HaltMusic();

返値として,成功した場合は 0を返します?

Mix_PauseMusic関数(#include <SDL_mixer.h>)

再生中のMusic系サウンドを一時停止します.

void Mix_PauseMusic();

Mix_ResumeMusic関数(#include <SDL_mixer.h>)

Music系サウンドの一時停止を解除します(一時停止中のサウンドを再開).

void Mix_ResumeMusic();

Mix_ResumeMusic(); // 再生中のサウンドを一時停止

Mix_PauseMusic(); // 再生中のサウンドを一時停止

Mix_HaltMusic(); // 再生中のサウンドを停止

Mix_PlayMusic(music, -1); //サウンドを無限(繰り返し)再生

Mix_Chunk *music;

music = Mix_LoadWAV(“test.wav”); // test.wavを Chunk系で読み込み

Page 44: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

44

Mix_RewindMusic関数(#include <SDL_mixer.h>)

再生または一時停止中のMusic系サウンドを最初に巻き戻します.

void Mix_RewindMusic();

Mix_PlayingMusic関数(#include <SDL_mixer.h>)

Music系サウンドが再生状態かチェックします.

int Mix_PlayingMusic();

返値として,再生中であれば 1を,そうでなければ 0を返します.

Mix_PausedMusic関数(#include <SDL_mixer.h>)

Music系サウンドが一時停止状態かチェックします.

int Mix_PausedMusic();

返値として,一時停止中であれば 1を,そうでなければ 0を返します.

Mix_VolumeMusic関数(#include <SDL_mixer.h>)

Music系サウンドの音量を調整します.

int Mix_VolumeMusic(int volume);

引数:音量(0~128)

volumeに-1を指定すれば,返値として現在の音量が返ってきます.

Mix_PlayChannel関数(#include <SDL_mixer.h>)

読み込んだ Chunk系サウンドを再生します.

int Mix_PlayChannel (int channel, Mix_Chunk *chunk, int loops);

引数(第 1から):再生するチャンネル ID,読み込んだサウンド,再生のループ回数

ここでいうチャンネルとは,“モノラルかステレオか”ではなくサウンドを再生させるための

窓口(割り当て)のようなものです.よって,Chunk系サウンドでは,チャンネル IDを指定

して,チャンネルごとに再生や停止を行えます.チャンネルに-1を指定すると,サブシステム

がチャンネル IDを選びます.

loopsに-1を指定すると,無限に再生されます.

返値として,成功した場合は 0を,失敗した場合は-1を返します.

Mix_VolumeMusic(100);

if(Mix_PausedMusic()==1) printf(“Paused...¥n”);

if(Mix_PlayingMusic()==1) printf(“Playing...¥n”);

Mix_RewindMusic(); //サウンドを最初に巻き戻し

Mix_PlayChannel(1, music, 0); // チャンネル 1のサウンドを 1回のみ再生

Page 45: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

45

Mix_HaltChannel関数(#include <SDL_mixer.h>)

読み込んだ Chunk系サウンドを停止します.

int Mix_HaltChannel(int channel)

引数:停止するチャンネル ID

channelに-1を指定すると,すべてのチャンネルのサウンドが停止します.

返値として,成功した場合は 0を,失敗した場合は-1を返します.

(3)サウンドの終了

使い終わったサウンドは解放する必要があります.

Mix_FreeMusic関数(#include <SDL_mixer.h>)

指定した(読み込み済みの)Music系サウンドデータを解放します.

void Mix_FreeMusic(Mix_Music *music);

Mix_FreeChunk関数(#include <SDL_mixer.h>)

指定した(読み込み済みの)Chunk系サウンドデータを解放します.

void Mix_FreeChunk(Mix_Chunk *music);

引数:解放するサウンドデータ

また,サウンドのサブシステム自体を使わなくなった場合は,それを閉じる必要もあります.

Mix_CloseAudio関数(#include <SDL_mixer.h>)

サウンドのサブシステムを閉じます.

void Mix_CloseAudio();

その他の SDL_mixerライブラリの関数については,

http://jcatki.no-ip.org:8080/SDL_mixer/SDL_mixer_frame.html 等を参照してください.

Mix_HaltChannel(1); // チャンネル 1のサウンドを停止

Mix_CloseAudio(); //サウンドサブシステムを閉じる

Mix_FreeChunk(music); // 指定した Chunk系サウンドデータを解放

Mix_FreeMusic(music); // 指定した Music系サウンドデータを解放

Page 46: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

46

3.6 マルチスレッドプログラミング

3.6.1 マルチスレッドとは

スレッドとは,一連の処理の流れのことで,特に,プロセス内での並行処理(または,そのプログラ

ム)のことです.処理の流れが一本道(1プロセス)であるものをシングルスレッド,複数の処理を平

行して行うもの(1プロセス内に複数のスレッドがある)をマルチスレッドと呼びます.ソフトウェア

実験等のプログラムの授業で,皆さんが今まで作成してきたものは,主にシングルスレッドのプログラ

ムだったのではないでしょうか.マルチスレッドの概念を図3.4に示します.

図3.4 マルチスレッド

マルチスレッドには,次のような特徴があり,軽量プロセスとも言われ,ゲーム開発でも有効利用で

きます(詳しくは,3年生の“オペレーティングシステム”の授業で扱います).

1つのプロセスに割り当てられたメモリ領域を複数のスレッドで共有する.

CPUが処理するスレッドを切り替える際,オーバーヘッド(負担)が少ない.

ゲーム開発におけるマルチスレッドの利用例としては,時間のかかる処理をする時などが挙げられま

す.例えば,アクションゲームでは,複数のキャラクタの動きを計算してアニメーションさせるといっ

た複雑な処理をしている最中でも,ユーザからの入力を受け付ける必要があります.このような場合に,

ユーザからの処理を受け付けるスレッドと,キャラクタごとにアニメーション処理を行うスレッドを平

行して動かせば,OS が複数のスレッドを適切に切り替えて処理してくれるため,プログラムがフリー

ズするようなことを極力防ぐことができ,全体としてスムーズなゲーム進行を実現できます.

3.6.2 注意点

マルチスレッドでは複数のスレッドでメモリ領域を共有しますが,このために注意しなければいけな

いことがあります.例えば,複数のスレッドが共有変数(グローバル変数)を読み書きする場合,ある

スレッドが変数の値を意図せず書き換えたりする場合が考えられます.また,複数のスレッドが,ある

ファイルを読み書きする場合も同様のことが起こりえます.これと同様の懸念は,サーフェイスのロッ

クと解除(3.2.5)でも触れました.

そこで SDLでは,マルチスレッドによる意図しない結果を防ぐために,相互排除(Mutex),セマフ

ォ,条件変数といった機能が用意されています(これらについても詳しくは,“オペレーティングシス

テム”の授業で).一般的にマルチスレッドのプログラムを書く場合,並列アクセスやデータの整合性

について以下のことに注意する必要があります.

独立したスレッドでは SDLのビデオ,イベントの関数を呼ばない.

独立したスレッドからはいかなるライブラリ関数も使用しない.

独立したスレッドからメモリ管理を行わない.

Page 47: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

47

複数のスレッドからアクセスされる可能性があるグローバル変数はロックする.

決してスレッドを終了させない.フラグをセットしてスレッドが終了するのを待つこと.

コードの相互作用について考えられる全ての可能性を吟味すること.

とはいえ,あまり気にしすぎていたら,プログラミングは進まないので,とりあえずプログラムを書い

て実行させてみましょう.

3.6.3 マルチスレッドプログラムの基本的な流れ

(1)スレッドの作成・実行 コンパイルオプション:-lSDL

スレッドを作成するために,まず SDL_Thread型ポインタを宣言します.そして,SDL_CreateThread

関数でスレッドを作成・実行します.

SDL_CreateThread関数(#include <SDL.h> SDL.hに SDL_thread.hが包含されています)

親の属性を共有した新しいスレッドを作成します(親プロセスに内包された新しいスレッドを作

成します)

SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data);

引数(第 1から):スレッド関数名,スレッド関数に渡すデータ(引数)

作成されたスレッドが返値として,SDL_Thread型変数に割り当てられ実体化されます.

作成されたスレッドはスレッド関数の終了とともに破棄され終了します.

スレッド関数は事前に定義されていなければなりません(タイマのコールバック関数と同様で

す)

さて,スレッド処理関数は次のような特徴を有しています(コールバック関数とよく似ています).

スレッド関数名は自分で決めることができます.

受け取る引数は,void型ポインタ(汎用ポインタ)の*dataです.void型ポインタを採用するこ

とで,どのような型の引数でも関数に渡すことができます.

*dataには,SDL_CreateThread 関数で指定された第 2引数(ポインタのアドレス)が示す値

が格納されます.

*dataは void型ポインタで受け取っているので,実際の引数の型にキャスト演算子で型変換し

て,新たな変数として格納します.

SDL_Thread *thr; // スレッド thrを宣言

thr = SDL_CreateThread(thread, NULL); // threadをスレッド関数と

してスレッドを作成・実行(引数なし)

Page 48: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

48

スレッドが作成・実行できたら,SDL_WaitThread 関数を使って,スレッドが終了するのを待つこと

を明示します.

SDL_WaitThread関数(#include <SDL.h>)

スレッドが終了するのを待ちます.

void SDL_WaitThread(SDL_Thread *thread, int *status);

引数(第 1から):終了を待つスレッド,スレッドの状態(通常は NULL)

スレッドが終了するまで待つということは,スレッド処理関数を呼び出した関数の処理(例え

ば,メイン関数)が一時停止することを意味します.

(2)スレッドの強制終了 コンパイルオプション:-lSDL

スレッドはスレッド処理関数の終了とともに終了しますが,SDL_KillThread関数を使って強制的に

終了することもできます.ただし,強制終了は推奨されません.

SDL_CreateThread関数(#include <SDL.h> SDL.hに SDL_thread.hが包まれています)

スレッドを強制終了させます.

void SDL_KillThread(SDL_Thread *thread);

引数:強制終了させるスレッド

(3)マルチスレッドプログラムの例 コンパイルオプション:-lSDL

ここで簡単なマルチスレッドプログラムの例を見てみましょう.次のプログラム(次ページ)では,

1~50と 51~100の 2つの加算処理を2つのスレッドに分けて実行しています.

SDL_KillThread(thr); // thrを強制終了

SDL_WaitThread(thr, NULL); // thrの終了を待つ

Page 49: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

49

#include "SDL/SDL.h"

int global_data=0; // 共有変数(グローバル変数)

// スレッド実行する関数1

int thread1(void* data){

int i, temp;

// 共有変数に 1〜50まで加算する処理

for(i = 1; i <= 50; i++){

temp = global_data; // tempに共有変数を格納して計算

SDL_Delay(10); // 処理に 10msかかることを意図的に設定

temp = temp + i; // 加算処理

global_data = temp; // 計算結果を共有変数に格納

}

return 0;

}

// スレッド実行する関数2

int thread2(void* data){

int i, temp;

// 共有変数に 51〜100まで加算する処理

for(i = 51; i <= 100; i++){

temp = global_data;

SDL_Delay(10);

temp = temp + i;

global_data = temp;

}

return 0;

}

// メイン関数

int main(int args,char *argp[]){

SDL_Thread *thr1, *thr2; // 2つのスレッドを用いる

thr1 = SDL_CreateThread(thread1, NULL); // スレッド thr1 を作成し、

スレッド関数 thread1を実行(引数なし)

thr2 = SDL_CreateThread(thread2, NULL); // スレッド thr2 を作成し、

スレッド関数 thread2を実行(引数なし)

SDL_WaitThread(thr1, NULL); // thr1の処理終了を待つ

SDL_WaitThread(thr2, NULL); // thr2の処理終了を待つ

printf("global_data = %d (=1+2+...100?)¥n", global_data); // 共有変数

(加算結果)の値表示

return 0;

}

Page 50: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

50

3.6.4 相互排除

さて,3.6.3のプログラム例の計算結果はどうなったでしょうか?正解の 5050でしたか?

実行結果はおそらく,正解の 5050にはならないのではないでしょう.このような意図しない結果は,

2つのスレッドがそれぞれ共有変数の値を一時的な変数に格納して加算処理し,その処理が終わった時

点で共有変数に加算結果を格納しており,一方のスレッドの加算処理の途中で,もう一方のスレッドに

加算処理が切り替わることで発生します.つまり,複数のスレッドが共有変数を自由に読み書きできる

ことが原因です.

そこで登場するのが,3.6.2でも挙げた相互排除(Mutex),セマフォ,条件変数といった仕組

みです.ここでは,1つのスレッドが一時的に共有変数の独占的なアクセス権をもてる,相互排除に焦

点を当てて説明します.独占的なアクセス権をもてることを“(共有変数を)ロックする(ロックを掛

ける)”,そのアクセス権を解放(返還)することを“アンロックする(ロックを外す)”と言います.

(1)Mutexの作成

相互排除(Mutext)を使うために,まず SDL_Mutex 型ポインタを宣言します.そして,

SDL_CreateMutex関数でMutexを作成します.

SDL_CreateMutex関数(#include <SDL.h> SDL.hに SDL_mutex.hが含まれています)

新しい(ロックされていない)Mutexを作成します.

SDL_mutex *SDL_CreateMutex(void);

Mutexを作成したら,SDL_CreateThread関数の第 2引数に作成したMutexを指定します.これに

より,第 1引数で指定されているスレッド関数内で共有変数にロックすることができるようになります.

(2)ロック/アンロック

スレッド内(スレッド以外でも)で共有変数をロックまたはアンロックするために,関数が用意され

ています.

SDL_mutexP関数(#include <SDL.h>)

Mutexにより共有変数をロックします.

int SDL_mutexP(SDL_mutex *mutex);

引数:作成したMutex

既に他のスレッドによって,ロックされていた場合,そのスレッドによってアンロックされる

まで処理を待つことになります(共有変数を読み書きできません).

返値として,ロックに成功した場合は 0を,失敗した場合は-1を返します.

SDL_mutexP(mutex);

thr = SDL_CreateThread(thread, mutex); // スレッドを作成し,スレッド関数を

実行(引数として Mutexを渡す)

SDL_mutex *mutex; // スレッド thrを宣言

mtx = SDL_CreateMutex(); // Mutexを作成

Page 51: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

51

SDL_mutexV関数(#include <SDL.h>)

Mutexにより共有変数をアンロックします

int SDL_mutexV(SDL_mutex *mutex);

引数:作成したMutex

返値として,アンロックに成功した場合は 0を,失敗した場合は-1を返します.

3.6.3のプログラム例に相互排除を取り入れて,正確な答えを計算できるようにしたものが次の

プログラム(次のページ)になります.

SDL_mutexV(mutex);

Page 52: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

52

#include "SDL/SDL.h"

int global_data=0; // 共有変数(グローバル変数)

int thread1(void* args){

int i, temp;

SDL_mutex *mtx = (SDL_mutex *)args;

for(i = 1; i <= 50; i++){

SDL_mutexP(mtx); //ロックして共有変数のアクセスを独占する

temp = global_data;

SDL_Delay(10);

temp = temp + i; global_data = temp;

SDL_mutexV(mtx); // アンロック

}

return 0;

}

int thread2(void* args){

int i, temp;

SDL_mutex *mtx = (SDL_mutex *)args;

for(i = 51; i <= 100; i++){

SDL_mutexP(mtx); //ロック

temp = global_data;

SDL_Delay(10);

temp = temp + i; global_data = temp;

SDL_mutexV(mtx); //アンロック

}

return 0;

}

int main(int args,char *argp[]){

SDL_Thread *thr1, *thr2; // 2つのスレッドを用いる

SDL_mutex *mtx; // 相互排除(Mutex)を用いる

mtx = SDL_CreateMutex(); // Mutexを生成

thr1 = SDL_CreateThread(thread1, mtx); // スレッド thr1 を作成し、

スレッド関数 thread1を実行(引数として Mutexを渡す)

thr2 = SDL_CreateThread(thread2, mtx); // スレッド thr2 を作成し、

スレッド関数 thread2を実行(引数として Mutexを渡す)

SDL_WaitThread(thr1, NULL); SDL_WaitThread(thr2, NULL);

SDL_DestroyMutex(mtx); // Mutexを破棄

printf("global_data = %d (=1+2+...100?)¥n", global_data);

return 0;

}

Page 53: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

53

3.7 OpenGLプログラミング

3.7.1 OpenGLとは

OpenGL(Open Graphics Library)とは,2次元・3次元の CG(Computer Graphics)を用いるソ

フトウェアを容易に開発するためのライブラリです.プラットフォームに依存することなくCGを扱え,

高速に動作させられることから,広く普及しています.

SDLから OpenGLを呼び出して利用することが可能です.その際,SDLがウィンドウやイベントの

処理を,OpenGLが CG処理を担当するという形で,処理担当が独立します.これで困ることはありま

せんが,GLUT(OpenGL Utilitiy Toolkit)というライブラリでは,OpenGLに加えて,ウィンドウや

イベントの制御もでき,ゲーム開発の一元化につながります.加えて,3 次元物体の扱いが容易である

ことも GLUTの利点です.

GLUTについては各自で独学してもらうこととして,ここでは,SDLから OpenGLを呼び出す方法

について紹介します.

3.7.2 OpenGLプログラムの基本的な流れ

(1)ヘッダファイルのインクルード

SDLから OpenGLを利用するには以下のヘッダをインクルードします.

(2)OpenGL用ビデオモード(OpenGLの属性)の設定とサーフェイスの作成

まず,OpenGL を利用するために,ビデオモード(OpenGL の属性)の設定をします.設定には

SDL_GL_SetAttribute 関数を用います.ビデオの初期化(サーフェイスの生成)前に設定する必要が

あります.

SDL_GL_SetAttribute関数(#include <SDL.h>)

OpenGL用ビデオモードを設定します.

int SDL_GL_SetAttribute(SDL_GLattr attr, int value);

引数(第 1から):OpenGLの属性,OpenGLの属性にセットする値

SDL_GL_RED_SIZE属性は,赤色(R)に割り当てられる色深度(ビット数)です.

一般的に,5ビットを指定します.GREEN,BLUEも同様.

SDL_GL_GREEN_SIZE属性は,緑色(G)に割り当てられる色深度(ビット数)です.

SDL_GL_BLUE_SIZE属性は,青色(B)に割り当てられる色深度(ビット数)です.

SDL_GL_DEPTH_SIZE属性は,深度バッファ(Zバッファ)に割り当てられる深度(奥行き)

SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );

SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5);

SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5);

SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );

SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );

#include < SDL/SDL_opengl.h >

Page 54: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

54

のビット数です.

一般的に,16または 24ビットを指定します.

深度バッファは 3次元物体を描画する際,奥にある物体の描画を避ける際に利用されます.

SDL_GL_DOUBLEBUFFER 属性は,ダブルバッファリングを使用するかどうかを指定しま

す.

使用する場合は 1を,使用しない場合は 0を指定します.

返値として,設定に成功した場合は 0を,失敗した場合は-1を返します.

その他指定できる属性として,SDL_GL_ALPHA_SIZE(αチャンネルのビット数)や

SDL_GL_BUFFER_SIZE(フレームバッファのビット数)があります.

次に,SDL_SetVideoMode関数で,OpenGL用ビデオモード,すなわち OpenGLで CGを描画する

サーフェイスを生成します.その際,SDL_SetVideoMode関数の第 4引数に SDL_OPENGLを指定し

ます.

OpenGL用ビデオモード(サーフェイス)を生成する時,ダブルバッファを有効にするために,

第 4 引数に SDL_DOUBLEBUF の指定は必要ありません.SDL_GL_SetAttribute 関数の中

で SDL_GL_DOUBLEBUFFER 属性を指定することで有効または無効になります.

(3)OpenGL関数の実行

OpenGL 関数は,gl から始まる関数名をもっています.OpenGL 関数はかなりの数があるので,こ

こでは基本的な関数だけを紹介します.

OpenGL関数の詳細は,

http://www.opengl.org/sdk/docs/man/

http://www.komoto.org/opengl/

http://wiki.livedoor.jp/mikk_ni3_92/

http://wisdom.sakura.ne.jp/system/opengl/index.html

http://www.is.oit.ac.jp/~whashimo/server/~whashimo/Article/OpenGL/

http://www.wakayama-u.ac.jp/~tokoi/opengl/libglut.html

等を参照して下さい.

glClearColor関数(#include <SDL_opengl.h>)

glClear関数でウィンドウを塗りつぶす際の色を指定します.

void glClearColor(GLclampf R, GLclampf G, GLclampf B, GLclampf A);

引数(第 1から):赤色(R)の成分の強さ,緑色(G)の成分の強さ,青色(B)の成分の強さ,

αチャンネル(A)の成分の強さ

各引数に指定する値は,0.0~1.0の floatと等価の値になります.値として,0.0が最も弱く(透

明),1.0が最も強く(不透明)なります.

window = SDL_SetVideoMode( 640, 480, 32, SDL_OPENGL );

glClearColor(0.0, 0.0, 1.0, 1.0);

Page 55: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

55

glClear関数(#include <SDL_opengl.h>)

ウィンドウを塗り潰します.

void glClear(GLbitfield mask);

引数:塗り潰すバッファ

mask には,4つの定数である GL_COLOR_BUFFER_BIT(色を格納するカラーバッファ),

GL_DEPTH_BUFFER_BIT(奥行きを踏まえ隠線/隠面消去等に用いるデプスバッファ),

GL_ACCUM_BUFFER_BIT(アキュムレーションバッファ),GL_STENCIL_BUFFER_BIT

(ステンシルバッファ)から1つ以上を指定します.

複数指定する場合は,ORを意味する“|”でつなげます.

単にウィンドウを単色で塗り潰す際は,GL_COLOR_BUFFER_BITを指定します.

glColor関数(#include <SDL_opengl.h>)

図形描画の際の描画色を指定します.

void glColor3b(GLbyte red, GLbyte green, GLbyte blue);

void glColor3d(GLdouble red, GLdouble green, GLdouble blue);

void glColor3f(GLfloat red, GLfloat green, GLfloat blue);

void glColor3i(GLint red, GLint green, GLint blue);

void glColor3s(GLshort red, GLshort green, GLshort blue);

void glColor3ub(GLubyte red, GLubyte green, GLubyte blue);

void glColor3ui(GLuint red, GLuint green, GLuint blue);

void glColor3us(GLushort red, GLushort green, GLushort blue);

void glColor4b(GLbyte red, GLbyte gree ,GLbyte blue, GLbyte alpha);

void glColor4d(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha);

void glColor4f(GLfloat red, GLfloat green,GLfloat blue, GLfloat alpha);

void glColor4i(GLint red, GLint green,GLint blue, GLint alpha);

void glColor4s(GLshort red, GLshort green,GLshort blue, GLshort alpha);

void glColor4ub(GLubyte red, GLubyte green,GLubyte blue, GLubyte alpha);

void glColor4ui(GLuint red, GLuint green,GLuint blue, GLuint alpha);

void glColor4us(GLushort red, GLushort green,GLushort blue, GLushort alpha);

void glColor3bv(const GLbyte *v);

void glColor3dv(const GLdouble *v);

void glColor3fv(const GLfloat *v);

void glColor3iv(const GLint *v);

void glColor3sv(const GLshort *v);

void glColor3ubv(const GLubyte *v);

void glColor3uiv(const GLuint *v);

void glColor3usv(const GLushort *v);

void glColor4bv(const GLbyte *v);

void glColor4dv(const GLdouble *v);

glClear(GL_COLOR_BUFFER_BIT);

Page 56: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

56

void glColor4fv(const GLfloat *v);

void glColor4iv(const GLint *v);

void glColor4sv(const GLshort *v);

void glColor4ubv(const GLubyte *v);

void glColor4uiv(const GLuint *v);

void glColor4usv(const GLushort *v);

glColor関数の使用例

glColor 関数には,実際には様々な接尾語がついて使用されます.接尾語の命名規則は以下の

ようになります.

数字は,RGBで色指定する際は 3,RGBとαチャンネルを指定する際は 4になります.

d,f,i,s,ui,us,ub,bは引数の型で,d から double,float,int,short,unsigned int,

unsigned short,sunsigned char,charを表します.

vが付けば,色の値を表す配列へのポインタを引数として渡すことになります.

glBegin関数(#include <SDL_opengl.h>)

図形描画のために,図形の各頂点の座標値を設定する範囲を開始します.

void glBegin(GLnum mode);

引数:頂点で構成される図形(プリミティブ)の形式

modeには,表3.10のいずれかの定数を指定します.

表3.10 頂点で構成される図形の形式を指定するための定数

定数 図形の形式

GL_POINTS 各頂点を単独の点として扱う.N個の点を描画する.

GL_LINES 2つの頂点を結ぶ線を描画する.

GL_LINE_STRIP 最初の頂点から最後の頂点まで,連続した線を描画する.

GL_LINE_LOOP 全ての頂点を線で連結して描画する.

GL_TRIANGLES 3つの頂点から成る三角形を描画する.

GL_TRIANGLES_STRIP 連結した三角形のグループを描画する(一辺を共有しながら帯状

に三角形を描画する).

GL_TRIANGLE_FAN 最初の頂点を軸に,連結した三角形のグループを描画する(一辺

を共有しながら扇状に三角形を描画する).

GL_QUADS 4つの頂点から成る四角形を描画する.

GL_QUAD_STRIP 連結した四角形のグループを描画する(一辺を共有しながら帯状

に四角形を描画する).

GL_POLYUGON 凸ポリゴン(多角形)を描画する.

glColor3d(1.0, 0.0, 0.0); // 赤

glColor3ub(0 , 0 , 0xFF); // 青

glBegin(GL_LINES);

Page 57: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

57

glVertex関数(#include <SDL_opengl.h>)

図形描画のために,図形の各頂点の座標値を設定します.

void glVertex2d(GLdouble x , GLdouble y);

void glVertex2f(GLfloat x , GLfloat y);

void glVertex2i(GLint x , GLint y);

void glVertex2s(GLshort x , GLshort y);

void glVertex3d(GLdouble x , GLdouble y , GLdouble z);

void glVertex3f(GLfloat x , GLfloat y , GLfloat z);

void glVertex3i(GLint x , GLint y , GLint z);

void glVertex3s(GLshort x, GLshort y, GLshort z);

void glVertex4d(GLdouble x, GLdouble y, Ldouble z, GLdouble w);

void glVertex4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w);

void glVertex4i(GLint x, GLint y, Lint z, GLint w);

void glVertex4s(GLshort x, GLshort y, GLshort z, GLshort w);

void glVertex2dv(const GLdouble *v);

void glVertex2fv(const GLfloat *v);

void glVertex2iv(const GLint *v);

void glVertex2sv(const GLshort *v);

void glVertex3dv(const GLdouble *v);

void glVertex3fv(const GLfloat *v);

void glVertex3iv(const GLint *v);

void glVertex3sv(const GLshort *v);

void glVertex4dv(const GLdouble *v);

void glVertex4fv(const GLfloat *v);

void glVertex4iv(const GLint *v);

void glVertex4sv(const GLshort *v);

glVertex関数の使用例

glColor 関数のように,glVertex 関数にも,実際には様々な接尾語がついて使用されます.接

尾語の命名規則は以下のようになります.

数字は,指定する頂点の座標の数となります.2次元座標であれば 2(x, y)を,3次元で

あれば 3(x, y, z)となります.同次座標系で座標を与える場合は,数字が 4(x, y, z, w)

になります.

同次座標系は,行列演算で図形処理する際に都合が良く,移動倍率を表す座標にもな

ります(x/w, y/w, z/w).ちなみに,2次元座標を与える場合,内部では z=0.0になっ

ています.

通常 wは 1.0となります.

glVertex2f(0.0, 0.0); // 1頂点(0, 0)

glVertex2f(10.0, 10.0); // 次の1頂点(10, 0)

Page 58: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

58

d,f,i,sは引数の型で,dから double,float,int,shortを表します.

v が付けば,頂点の位置を表す配列へのポインタを引数として渡すこと(参照渡し)にな

ります.

glRect関数(#include <SDL_opengl.h>)

対角線を形成する2頂点を与えて四角形を描画します.

void glRectd(GLdouble x1, GLdouble y1,GLdouble x2, GLdouble y2);

void glRectf(GLfloat x1, GLfloat y1,GLfloat x2, GLfloat y2 );

void glRecti(GLint x1, GLint y1,GLint x2, GLint y2 );

void glRects(GLshort x1, GLshort y1,GLshort x2, GLshort y2 );

void glRectdv(const GLdouble *v1, const GLdouble *v2);

void glRectfv(const GLfloat *v1, const GLfloat *v2);

void glRectiv(const GLint *v1, const GLint *v2);

void glRectsv(const GLshort *v1, const GLshort *v2);

glRect関数の使用例

glRect関数にも,実際には様々な接尾語がついて使用されます.接尾語の命名規則は省略しま

す.

接尾語に v が付かない場合,第 1 引数から,ある頂点の x 座標,y 座標,もう一つの x 座標,

y座標となり,2頂点を結ぶ対角線をもつ四角形を描画します.

OpenGLには,物体の平行移動や拡大縮小,回転といったアフィン変換を行う関数が用意されていま

す.従来,アフィン変換は物体の各座標を行列で表現し,行列演算により変換後の物体を求めるもので

す.OpenGLでは,行列を用意して行列演算しなくても物体のアフィン変換が可能です.

glTranslate関数(#include <SDL_opengl.h>)

座標軸を指定して,物体を平行移動させる.

void glTranslated(GLdouble x , GLdouble y , GLdouble z);

void glTranslatef(GLfloat x , GLfloat y , GLfloat z);

引数(第 1から):x軸に対する平行移動量,y軸に対する平行移動量,z軸に対する平行移動量

平行移動量には単位はありません.適当な数値を与えて,どれくらい平行移動するか確認して

ください.

glTranslated (0.1 , 0.0 , 0.0); // x軸に+0.1平行移動

glRectf(-0.8 , 0.8 , 0.8 , -0.8);

Page 59: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

59

glScale関数(#include <SDL_opengl.h>)

座標軸を指定して,物体を拡大縮小させる.

void glScaled(GLdouble x , GLdouble y , GLdouble z);

void glScalef(GLfloat x , GLfloat y , GLfloat z);

引数(第 1から):x軸に対する拡縮量(拡縮係数),y軸に対する拡縮量,z軸に対する拡縮量

引数の値が 1.0 より大きければ拡大,1.0 より小さければ縮小になります.-1.0 の場合は反転

(線対称変換)になります.

glRotate関数(#include <SDL_opengl.h>)

座標軸を指定して,物体を回転移動させる.

void glRotated(GLdouble angle,GLdouble x , GLdouble y , GLdouble z);

void glRotatef(GLfloat angle,GLfloat x , GLfloat y , GLfloat z);

引数(第 1から):回転角(度数),x軸に対する回転方向ベクトル,y軸に対する回転方向ベク

トル,z軸に対する回転方向ベクトル

回転させない軸には 0.0を指定します.

複数の軸で同時に回転させることも可能です.

glEnd関数(#include <SDL_opengl.h>)

図形描画のために,図形の各頂点の座標値を設定する範囲を終了します.

void glEnd(void);

では,ここで図形描画のプログラム例(一部抜粋)をいくつか見てみたいと思います.

<線で四角形を描画する>

glRotatef(5.0, 1.0, 0.0, 0.0); // x軸に対して+5度回転

glScaled(1.5, 1.0, 1.0); // x軸に対して 1.5倍に拡大

glScaled(1.0, 0.5, 1.0); // y軸に対して 0.5倍に縮小

glBegin(GL_LINE_LOOP);

glVertex2d(-0.9, -0.9);

glVertex2d(0.9, -0.9);

glVertex2d(0.9, 0.9);

glVertex2d(-0.9, 0.9);

glEnd();

glEnd();

Page 60: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

60

<2つの三角形を描画する>

<赤色でポリゴンを描画する>

<グラデーションで2つの三角形を描画する>

glBegin(GL_TRIANGLES);

// 頂点ごとに色を設定し,グラデーションにする

glColor3d(0.0 , 0.0 , 1.0); // 以降の頂点に青色を設定

glVertex2f(0.0 , 0.0);

glColor3d(0 , 1.0 , 0.0); // 以降の頂点に緑色を設定

glVertex2f(-1.0 , 0.9);

glVertex2f(1.0 , 0.9);

glColor3ub(0 , 0 , 0xFF); // 以降の頂点に青色を設定

glVertex2f(0.0 , 0);

glColor3ub(0xFF , 0 , 0); // 以降の頂点に赤色を設定

glVertex2f(-1.0 , -0.9);

glVertex2f(1.0 , -0.9);

glEnd();

glColor3d(1.0, 0.0, 0.0); // glBeginと glEndで囲まれた図形に対して赤色を設定

glBegin(GL_POLYGON);

glVertex2d(-0.9, -0.9);

glVertex2d(0.0, 0.0);

glVertex2d(0.9, -0.9);

glVertex2d(0.9, 0.9);

glVertex2d(-0.9, 0.9);

glEnd();

glBegin(GL_TRIANGLES);

glVertex2f(0.0 , 0.0);

glVertex2f(-1.0 , 0.9);

glVertex2f(1.0 , 0.9);

glVertex2f(0.0 , 0);

glVertex2f(-1.0 , -0.9);

glVertex2f(1.0 , -0.9);

glEnd();

Page 61: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

61

glFlush関数(#include <SDL_opengl.h>)

蓄積された OpenGL命令を全て実行します.

void glFlush(void);

OpenGL命令は逐次実行されるわけではなく,ある程度蓄積され一括で実行されます.そこで,

明示的に蓄積された OpenGL命令を一括実行するために,gFlush関数が必要です.

gFlush関数を多用すると,処理が遅くなります.

(4)画面更新(描画反映)

SDL_GL_SetAttribute 関数の中で SDL_GL_DOUBLEBUFFER 属性が指定され,ダブルバッファ

がサポートされている場合,描画を画面に反映させるためには,SDL_GL_SwapBuffers 関数により

OpenGL のバッファを入れ替える必要があります.

SDL_GL_SwapBuffers関数(#include <SDL.h>)

OpenGL のフレームバッファを入れ替えます/表示を更新します.

void SDL_GL_SwapBuffers(void );

3.7.3 3次元 CG

OpenGLでは,球体や立方体など 3次元物体を直接描画(モデリング)する関数は提供されていませ

ん(GLUT には単純な 3 次元物体を直接描画する関数が提供されています).となると,四角形などの

単純な図形を貼り合わせて 3次元物体を構成することになります.いわゆる,ポリゴンを扱うというこ

とになります.ここでは,単純な例として,四角形を合わせて,立方体を構成することを考えてみます.

まず,立方体を構成するには 6面の四角形が必要になるので,glBegin関数の引数に GL_QUADSを

指定して,四角形(立方体)の頂点を与えていきます.その際,指定する頂点のデータ数は,6 面×4

頂点×3座標となります.頂点のデータは例えば,次のように配列に格納して与えることができます(勿

論,他の良い方法はあるでしょう).

glFlush();

SDL_GL_SwapBuffers();

Page 62: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

62

このように配列として与えた頂点データを,glVertex 関数で描画していきます.例えば次のように,

forループで描画することができます.

うまくポリゴンデータを記述(格納)することができれば,複雑な 3次元物体をモデリング・表示す

ることが可能になります.

glBegin(GL_QUADS); // 四角形の開始

for (i = 0; i < 6; i++) {

glColor3d(i*0.2, 0.0, 0.0); // 各面の頂点を赤系の色に設定

for (j = 0; j < 4; j++){

glVertex3f(vertices[i][j][0], vertices[i][j][1], vertices[i][j][2]);

}

}

glEnd(); // 図形終了

float vertices[6][4][3] = {

// 立方体の頂点データ

// 正面

{{-0.5f, 0.5f, -0.5f}, {-0.5f, -0.5f, -0.5f}, {0.5f, -0.5f, -0.5f},

{0.5f, 0.5f, -0.5f}},

// 右側面

{{0.5f, 0.5f, -0.5f}, {0.5f, -0.5f, -0.5f}, {0.5f, -0.5f, 0.5f}, {0.5f,

0.5f, 0.5f}},

// 裏面

{{0.5f, 0.5f, 0.5f}, {0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f,

0.5f, 0.5f}},

// 左側面

{{-0.5f, 0.5f, 0.5f}, {-0.5f, -0.5f, 0.5f}, {-0.5f, -0.5f, -0.5f},

{-0.5f, 0.5f, -0.5f}},

// 上面

{{-0.5f, 0.5f, 0.5f}, {-0.5f, 0.5f, -0.5f}, {0.5f, 0.5f, -0.5f}, {0.5f,

0.5f, 0.5f}},

// 底面

{{-0.5f, -0.5f, -0.5f}, {-0.5f, -0.5f, 0.5f}, {0.5f, -0.5f, 0.5f}, {0.5f,

-0.5f, -0.5f}}

};

Page 63: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

63

3.7.4 テクスチャ

これまで扱ってきた物体は質感に乏しいものでした.ゲームのリアリティを高めたい時など,複雑か

つ微細な3次元物体をモデリングすることも重要ですが,物体の質感も重要な要素となります.そこで,

ポリゴンに質感を与えるために,画像をポリゴンに貼り付けてみましょう.例えば,岩をモデリングし

た際に,単なる灰色や茶色で着色するのではなく,実際にデジカメなどで撮影した岩の表面の画像を貼

り付けることができれば,より岩らしく感じられるでしょう.このような技術をテクスチャマッピング

といいます.

OpenGLのテクスチャには 1次元と 2次元の 2種類が存在しますが,ここでは 2次元テクスチャの扱

い方を関数とともに紹介します.

(1)テクスチャ画像を読み込む

テクスチャ画像には,“2の n乗×2の n乗ピクセルの画像”というサイズの制約があります(縦横の

ピクセル数は異なって構いません).例えば,256×256ピクセルの画像を用意してください.

そして,IMG_Load関数や SDL_LoadBMP関数でテクスチャ画像をサーフェイスに読み込みます.

ただ,単にサーフェイスに読み込むだけではテクスチャ画像として扱えません.OpenGLでは,個々の

ピクセルのバイトが RGBA の順番で並んだ 32 ビットのサーフェイスがテクスチャ用に要求されます.

そこで,画像をテクスチャ用サーフェイスに変換して貼り付けるために,SDL_CreateRGBSurface 関

数(3.4.3)で変換用のサーフェイスを生成することになります.

例えば,色深度が 32ビット,ビッグエンディアンのマシンでテクスチャ用サーフェイスを生成して,

画像を貼り付ける場合な,次のようになります.

(2)テクスチャを有効にする

次に読み込んだテクスチャを有効にします.2 次元テクスチャを有効にするには glEnable 関数に引

数 GL_TEXTURE_2D を指定します.

glEnable関数(#include <SDL_opengl.h>)

機能を指定してテクスチャを有効にします.

void glEnable( GLenum cap);

引数:GL の機能を示すシンボル定数

2次元のテクスチャ処理を実行するには,cpaに GL_TEXTURE_2Dを指定します.

capには多くの定数があります.http://www.opengl.org/sdk/docs/man/等で確認できます.

テクスチャを無効にするには,glDisable関数を用います.

texture_surface = IMG_Load("texture.png"); // 画像の読み込み

texture_surface2 = SDL_CreateRGBSurface(SDL_SWSURFACE, texture_surface->w,

texture_surface->h, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); //

空のサーフェイスを生成

SDL_BlitSurface(texture_surface, NULL, texture_surface2, NULL);

glEnable(GL_TEXTURE_2D); // 2次元テクスチャを有効にする

Page 64: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

64

(3)テクスチャの生成

次に,実際にポリゴンに描画するテクスチャの名前を glGenTextures関数で生成します.テクスチャ

名はテクスチャの実体を生成するために必要です.

glGenTextures関数(#include <SDL_opengl.h>)

テクスチャの名前を生成します.

void glGenTextures(GLsizei n , GLuint * textures);

引数(第 1から):テクスチャ名の生成数,テクスチャ名を格納する配列(ポインタ)

OpenGLでは,テクスチャ名は数値(整数値)として扱われます.

テクスチャ名を生成したら,glBindTexture 関数でテクスチャをバインドします.ここでいうバイン

ドとは,テクスチャの実体を生成することであり,指定したテクスチャを有効にすることでもあります.

glBindTexture関数(#include <SDL_opengl.h>)

テクスチャの実体を生成します.

void glBindTexture(GLenum target , GLuint texture);

引数(第 1から):テクスチャの次元,実体を生成するテクスチャ名

テクスチャの次元には,GL_TEXTURE_1D,GL_TEXTURE_2D,GL_TEXTURE_3D また

は GL_TEXTURE_CUBE_MAPを指定します.

必要に応じて,glTexParameter関数でテクスチャのパラメータを設定します.

glTexParameter関数(#include <SDL_opengl.h>)

テクスチャのパラメータを設定します.

void glTexParameterf( GLenum target, GLenum pname, GLfloat param);

void glTexParameteri( GLenum target, GLenum pname, GLint param);

void glTexParameterfv( GLenum target, GLenum pname, const GLfloat *params);

void glTexParameteriv( GLenum target, GLenum pname, const GLint *params);

引数(第 1から):設定対象のテクスチャの次元,設定するパラメータ名,設定の値

パラメータ名,値は http://www.opengl.org/sdk/docs/man/ 等を参照のこと.

(3)画像データとテクスチャの関連づけ

テクスチャの準備が整ったら,次に,画像データ(サーフェイス)とテクスチャを関連づけます.こ

れにより,サーフェイスの画像データがテクスチャにコピーされたことになります.2 次元のテクスチ

ャであれば glTexImage2D関数を用います.

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glBindTexture(GL_TEXTURE_2D, texture); // 2次元テクスチャを生成

glGenTextures(1, &texture); // ある1つのテクスチャ名(整数値)を生成

Page 65: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

65

glTexImage2D関数(#include <SDL_opengl.h>)

画像データ(サーフェイス)とテクスチャを関連づけます.

void glTexImage2D(GLenum target, GLint level, GLint components, GLsizei width, GLsizei

height, GLint border , GLenum format , GLenum type , const GLvoid *pixels);

引数(第 1から):テクスチャの次元,0を基準とした詳細レベル番号,テクスチャの色深度(Byte

指定),テクスチャの横幅(2nピクセル),高さ(2nピクセル),テクスチャの境界幅,ピクセル

データのフォーマット,テクスチャに読み込む画像(ピクセル)データ

targetには,GL_TEXTURE_2Dや GL_PROXY_TEXTURE_2D等の定数を指定します.

levelは,1テクスチャに 1画像の場合は 0を指定します.テクスチャに解像度の小さな画像(ミ

ップマップ)を並べて描画する場合に 1以上を指定します(ミップマップはここでは扱いませ

ん).

componentsはテクスチャの色深度であり Byte指定ですので,1, 2, 3または 4を指定します.

上の例のように,サーフェイスの BytesPerPixelを指定してもよいでしょう.GL_RGBAとっ

た定数も指定できるようです.

borderには,境界が存在しない場合は 0を,存在する場合は 1を指定します.

formatには,GL_UNSIGNED_BYTE,GL_BYTE, GL_SHORT,GL_UNSIGNED_SHORT,

GL_INT,GL_UNSIGNED_INT,GL_FLOAT, GL_BITMAP 等を指定します.

GL_UNSIGNED_BYTE は,*pixels が GLubyte(unsigned char と等価)であること

を示します.

(4)テクスチャのポリゴンへの描画

これまでいくつかの段階を踏んできましたが,いよいよ,ポリゴンにテクスチャを描画します(貼り

付けます).テクスチャのポリゴンへの描画は,ポリゴンの頂点とテクスチャ画像の四隅を対応付ける

ことで実行されます.その対応付けは glTexCoord 関数を用いて,glVertex 関数で頂点を描画する直前

に,その頂点に対してテクスチャ画像の四隅の1つを指定することで行えます

glTexCoord関数(#include <SDL_opengl.h>)

テクスチャの四隅とポリゴンの頂点を対応づけます.

void glTexCoord1d(GLdouble s);

void glTexCoord1f(GLfloat s);

void glTexCoord1i(GLint s);

void glTexCoord1s(GLshort s);

void glTexCoord2d(GLdouble s, GLdouble t);

void glTexCoord2f(GLfloat s, GLfloat t);

void glTexCoord2i(GLint s, GLint t);

void glTexCoord2s(GLshort s, GLshort t);

void glTexCoord3d(GLdouble s, GLdouble t, GLdouble r);

glTexImage2D(GL_TEXTURE_2D, 0, texture_surface2->format->BytesPerPixel,

texture_surface2->w, texture_surface2->h, 0, texture_format, GL_UNSIGNED_BYTE,

texture_surface2->pixels); // 画像データをテクスチャに貼り付ける

Page 66: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

66

void glTexCoord3f(GLfloat s, GLfloat t, GLfloat r);

void glTexCoord3i(GLint s, GLint t, GLint r);

void glTexCoord3s(GLshort s, GLshort t, GLshort r);

void glTexCoord4d(GLdouble s, GLdouble t, GLdouble r, GLdouble q);

void glTexCoord4f(GLfloat s, GLfloat t, GLfloat r, GLfloat q);

void glTexCoord4i(GLint s, GLint t, GLint r, GLint q);

void glTexCoord4s(GLshort s, GLshort t, GLshort r, GLshort q);

void glTexCoord1dv(const GLdouble *v);

void glTexCoord1fv(const GLfloat *v);

void glTexCoord1iv(const GLint *v);

void glTexCoord1sv(const GLshort *v);

void glTexCoord2dv(const GLdouble *v);

void glTexCoord2fv(const GLfloat *v);

void glTexCoord2iv(const GLint *v);

void glTexCoord2sv(const GLshort *v);

void glTexCoord3dv(const GLdouble *v);

void glTexCoord3fv(const GLfloat *v);

void glTexCoord3iv(const GLint *v);

void glTexCoord3sv(const GLshort *v);

void glTexCoord4dv(const GLdouble *v);

void glTexCoord4fv(const GLfloat *v);

void glTexCoord4iv(const GLint *v);

void glTexCoord4sv(const GLshort *v);

glTexCoord関数で指定される座標は,画像に対する相対的な座標です.テクスチャ画像は,(s,

t, r, q)に配置されます.

四角形のポリゴンに画像を描画する場合,指定する数値と画像には図3.5のような対応

があります.

glBegin(GL_QUADS); // 四角形の開始

// 以下の四角形に(管理番号が指す)テクスチャを描画する

glTexCoord2f(0.0f, 0.0f); // 画像の左上と次の頂点を対応させる

glVertex3f(-0.5f, 0.5f, 0.0f);

glTexCoord2f(0.0f, 1.0f); // 画像の左下と次の頂点を対応させる

glVertex3f(-0.5f, -0.5f, 0.0f);

glTexCoord2f(1.0f, 1.0f); // 画像の右下と次の頂点を対応させる

glVertex3f(0.5f, -0.5f, 0.0f);

glTexCoord2f(1.0f, 0.0f); // 画像の右上と次の頂点を対応させる

glVertex3f(0.5f, 0.5f, 0.0f);

glEnd(); // 図形終了

Page 67: SDL プログラミングnetadm.iss.tokushima-u.ac.jp/soft/dev/SDL.pdfソフトウェア実験(ユーザインタフェイス) プログラミング資料 3 2.SDLのインストール

ソフトウェア実験(ユーザインタフェイス) プログラミング資料

67

図3.5 テクスチャとポリゴンの位置関係

(3)テクスチャの削除

テクスチャ(実体)が不要になれば,メモリの節約という点でも削除(解放,破棄)すべきです.テ

クスチャの削除には glDeleteTextures関数を用います.

glDeleteTextures関数(#include <SDL_opengl.h>)

テクスチャの実体を削除します.

void glDeleteTextures(GLsizei n , const GLuint * textures);

引数(第 1から):削除するテクスチャ数,テクスチャ名を格納した配列(ポインタ)

さて,ここまで OpenGL の基本的な部分を概観してきました.OpenGL のほんの一部分ですが,そ

の可能性を感じることができたのではないかと思います.OpenGLは高度な CGを取り扱える強力で奥

の深いライブラリです.もし,3次元 CGを駆使したゲームを OpenGLで開発したい!と思ったら,と

にかく独学してください.ベクトルと行列,座標系,射影変換,光源や反射,隠線/隠面処理など,理

解すべきことはたくさんあります(ソフトウェア実験の1回の授業ではとてもすべてを扱えません).

<おわりに> ソフトウェア実験は,2011 年度から X-Window プログラミングから SDL プログラミングに変わりまし

た.残念ながら,「十分な時間を掛けてこの教材を作成した」とは言えませんので,誤りや分かりにくい点,重要であるが言及できていない点が含まれていると思いますが,大目に見てください.

SDL によるネットワークプログラミングはここでは扱いませんでしたが,ソフトウェア実験のグループワークであるネットワーク対戦型ゲーム開発には欠かせません.GUI プログラミングと合わせてしっかり勉強しましょう.

glDeleteTextures(1, &texture); // テクスチャを削除する