過去ログ 2
過去ログは下にいくほど新しい記事となっております。
※注意:このサイトにあるプログラムのソースコードや、その他の情報はすべて無保証です。やばいと思ったら実行しないでください。コンパイル、実行などによって生じた損害に対する責任は負いませんので、ご了承ください。
たまにWindowsを立ち上げると
18-4-2009
勝手にアップデートし、勝手に再起動するので困る。
SDL - 昨日の続き
前回、画像読み込みをしたSDL_Surfaceを解放する処理を忘れていました。
SDL_Surfaceの解放処理はSDL_FreeSurface()です。
void SDL_FreeSurface(SDL_Surface* surface);
#include <SDL/SDL.h> #include <SDL/SDL_image.h> int main(){ SDL_Surface *image; SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; SDL_Rect rect_img = {0,0,200,200}; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); screen = SDL_GetVideoSurface(); SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); SDL_Delay(3000); SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
今回は、画像を動かすことを考えてみましょう。左から
右へ
ループを使用して、X座標を少しずつずらしていきます。
#include <SDL/SDL.h> #include <SDL/SDL_image.h> int main(){ SDL_Surface *image; SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; SDL_Rect rect_img = {0,0,200,200}; int x; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); screen = SDL_GetVideoSurface(); for(x = 0; x < 180; x++){ rect_scr.x = x; SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); SDL_Delay(20); // スリープを入れるのを忘れずに } SDL_Delay(3000); SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
※SDL_Rect構造体は以下のとおりです。
typedef struct SDL_Rect { Sint16 x, y; Uint16 w, h; } SDL_Rect;
上のソースコードをコンパイルして実行すると、
となり、前回描画したものがクリアされないまま移動します。
毎回画面を黒で塗りつぶし、その後画像を配置するようにします。
塗りつぶしにはSDL_FillRect()を使用します。
int SDL_FillRect(SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color);
*dstは塗りつぶすサーフェス、*dstrectは塗りつぶす範囲、colorは塗りつぶす色を指定します。
colorはSDL_MapRGB()、またはSDL_MapRGBA()の返り値を指定します。
Uint32 SDL_MapRGB(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b); Uint32 SDL_MapRGBA(SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
SDL_MapRGB()とSDL_MapRGBA()の違いはアルファチャンネル(透明度)の指定ができるかどうかです。特に今回は透明度の指定は不要なので、SDL_MapRGB()を使用します。
SDL_PixelFormat構造体を下に示しますが、今回は特に使用しません。
typedef struct SDL_PixelFormat { SDL_Palette *palette; Uint8 BitsPerPixel; Uint8 BytesPerPixel; Uint8 Rloss; Uint8 Gloss; Uint8 Bloss; Uint8 Aloss; Uint8 Rshift; Uint8 Gshift; Uint8 Bshift; Uint8 Ashift; Uint32 Rmask; Uint32 Gmask; Uint32 Bmask; Uint32 Amask; /* RGB color key information */ Uint32 colorkey; /* Alpha value information (per-surface alpha) */ Uint8 alpha; } SDL_PixelFormat;
r,g,bは0から255の値を指定します。(r,g,b)=(0,0,0)で黒です。
#include <SDL/SDL.h> #include <SDL/SDL_image.h> int main(){ SDL_Surface *image; SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; // 画像複写用 SDL_Rect rect_scr2 = {0,0,300,300}; // 塗りつぶし用 SDL_Rect rect_img = {0,0,200,200}; int x; Uint32 color; SDL_PixelFormat fmt; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); screen = SDL_GetVideoSurface(); // 色作成 color = SDL_MapRGB(&fmt, 0, 0, 0); for(x = 0; x < 180; x++){ rect_scr.x = x; SDL_FillRect(screen, &rect_scr2, color); SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); SDL_Delay(20); // スリープを入れるのを忘れずに } SDL_Delay(3000); SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
実行すると、きちんと移動するようになります。
-- 続く --
今日のg++
今日のg++はお休みです。
夜のペンギンは好きですか?
※コメント、トラックバックはできません。(ブログじゃないから)
日曜日です
19-4-2009
Fedoraは
まだ入れないですよ。
SDL - 昨日の続き
いいかげん、3秒ウエイトプログラムにも飽きたので、ウインドウを閉じたときに終了するようにしましょう。
ちょっと古いソースコードをもってきます。
#include <SDL/SDL.h> #include <SDL/SDL_image.h> int main(){ SDL_Surface *image; SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; SDL_Rect rect_img = {0,0,200,200}; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); screen = SDL_GetVideoSurface(); SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); SDL_Delay(3000); SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
これに処理を追加します。
イベント
イベントとは、キーを押したり、マウスボタンをクリックしたり、ウインドウを閉じたりというあらゆる動作のこと。
今回、ウインドウを閉じたときにプログラムが終了するようにしたいので、ウインドウクローズのイベントをプログラムが感知し、その時にプログラムが終了するようにすればいいのです。
イベント共用体
typedef union SDL_Event { 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_SysWMEvent syswm; } SDL_Event;
メンバとなっている構造体の先頭1バイトはイベントタイプとなっているので、タイプを調べる場合はSDL_Eventのtypeを調べればよいこととなります。
(※ 共用体は同じアドレスを参照するため、例えば「activate」の先頭1バイトは「type」と同じになります。)
イベントタイプには以下の値がセットされます。
typedef enum { SDL_NOEVENT = 0, /* Unused (do not remove) */ SDL_ACTIVEEVENT, /* Application loses/gains visibility */ SDL_KEYDOWN, /* Keys pressed */ SDL_KEYUP, /* Keys released */ SDL_MOUSEMOTION, /* Mouse moved */ SDL_MOUSEBUTTONDOWN, /* Mouse button pressed */ SDL_MOUSEBUTTONUP, /* Mouse button released */ SDL_JOYAXISMOTION, /* Joystick axis motion */ SDL_JOYBALLMOTION, /* Joystick trackball motion */ SDL_JOYHATMOTION, /* Joystick hat position change */ SDL_JOYBUTTONDOWN, /* Joystick button pressed */ SDL_JOYBUTTONUP, /* Joystick button released */ SDL_QUIT, /* User-requested quit */ SDL_SYSWMEVENT, /* System specific event */ SDL_EVENT_RESERVEDA, /* Reserved for future use.. */ SDL_EVENT_RESERVEDB, /* Reserved for future use.. */ SDL_VIDEORESIZE, /* User resized video mode */ SDL_VIDEOEXPOSE, /* Screen needs to be redrawn */ SDL_EVENT_RESERVED2, /* Reserved for future use.. */ SDL_EVENT_RESERVED3, /* Reserved for future use.. */ SDL_EVENT_RESERVED4, /* Reserved for future use.. */ SDL_EVENT_RESERVED5, /* Reserved for future use.. */ SDL_EVENT_RESERVED6, /* Reserved for future use.. */ SDL_EVENT_RESERVED7, /* Reserved for future use.. */ /* Events SDL_USEREVENT through SDL_MAXEVENTS-1 are for your use */ SDL_USEREVENT = 24, /* This last event is only for bounding internal arrays It is the number of bits in the event mask datatype -- Uint32 */ SDL_NUMEVENTS = 32 } SDL_EventType;
ウインドウを閉じる(ユーザからの終了要請)はSDL_QUITになります。
イベントのチェック
SDL_PollEvent()を使用して、イベントのチェックとイベント共用体へのセットを行います。
int SDL_PollEvent(SDL_Event *event);
SDL_Eventは上で示した共用体です。この関数はイベントがあれば1が、なければ0が返ります。
イベント待ちループの作成
以下のようなループを作成します。
- イベントをチェックする。
- イベントが存在する場合はイベントタイプをチェックする。タイプがSDL_QUITであれば終了する。
- 繰り返す。
処理を入れると次のようになります。
#include <SDL/SDL.h> #include <SDL/SDL_image.h> int main(){ SDL_Surface *image; SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; SDL_Rect rect_img = {0,0,200,200}; SDL_Event event; int endflag = 0; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); screen = SDL_GetVideoSurface(); SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); while(!endflag){ while(SDL_PollEvent(&event)){ switch(event.type){ case SDL_QUIT: endflag = 1; break; default: break; } } } SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
ウインドウの「閉じるボタン」を押すと終了します。
キーを押したときにも終了するようにするには、SDL_KEYDOWNを追加します。
// 該当部分抜粋 while(!endflag){ while(SDL_PollEvent(&event)){ switch(event.type){ case SDL_QUIT: case SDL_KEYDOWN: endflag = 1; break; default: break; } } }
CPU使用率
さて、今作成したプログラムのCPU使用率を観察してみましょう。
CPU使用率がすごいことになっています。(a.outが実行しているプログラムです。)
(50%近くになっているのはデュアルコアであるためだと思われる。CPUが一つなら、90%以上いっているんじゃないかな?)
原因はイベント待ちループが無限に高速で回りつづけているからであり、CPUが休む暇がないからです。
この状態を続けていると、ノートPCだとファンが高速で回転をしてCPUの冷却をはじめ、PCの健康上よくないので、意図的にスリープを入れます。
// 該当部分抜粋 while(!endflag){ while(SDL_PollEvent(&event)){ switch(event.type){ case SDL_QUIT: case SDL_KEYDOWN: endflag = 1; break; default: break; } } SDL_Delay(1); }
もちろんもっと長いスリープを入れてもいいですが、これだけでも大きく改善されます。
画像移動プログラムに適用すると
昨日作った画像移動プログラムにそのままイベント処理をセットしてみます。
#include <SDL/SDL.h> #include <SDL/SDL_image.h> int main(){ SDL_Surface *image; SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; // 画像複写用 SDL_Rect rect_scr2 = {0,0,300,300}; // 塗りつぶし用 SDL_Rect rect_img = {0,0,200,200}; int x; Uint32 color; SDL_PixelFormat fmt; SDL_Event event; int endflag = 0; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); screen = SDL_GetVideoSurface(); // 色作成 color = SDL_MapRGB(&fmt, 0, 0, 0); for(x = 0; x < 180; x++){ rect_scr.x = x; SDL_FillRect(screen, &rect_scr2, color); SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); SDL_Delay(20); } while(!endflag){ while(SDL_PollEvent(&event)){ switch(event.type){ case SDL_QUIT: case SDL_KEYDOWN: endflag = 1; break; default: break; } } SDL_Delay(1); } SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
するとちょっと困った問題が発生します。
画像が移動している間はキーを押しても閉じるボタンを押しても、プログラムが終了しません。
理由は、画像が移動している間はイベント待ちのループに処理が到達していないため、イベント処理ができないためです。
ではどうするのか?
と、続きを書いてみたら結構長くなったので、次回に続く。
今日のg++
継承をやります。重要なところなので、ゆっくりやりましょう。
継承は、今あるクラスを拡張して新たなクラスを作成することです。
メンバを増やしたり、現在あるメソッドを上書きしたりします。まずは、メンバを増やしてみましょう。
#include <stdio.h> class myclass{ public: int a; }; int main(){ myclass *c = new myclass(); c->a = 100; printf("%d",c->a); delete c; return 0; }
クラスのメンバ(int型)に100をセットし、それを表示しています。
特に問題はないはずです。
次に、このmyclassを継承して、myclass2を作ります。継承する場合は、次のように書きます。
class myclass2 : public myclass { // 以下省略
これで、myclass2はmyclassを継承する、ということを示します。
class myclass{ public: int a; }; class myclass2 : public myclass{ public: int b; }
これで、myclassを継承した、myclass2ができました。
#include <stdio.h> class myclass{ public: int a; }; class myclass2 : public myclass{ public: int b; }; int main(){ myclass2 *c = new myclass2(); c->b = 100; printf("b: %d\n",c->b); c->a = 50; printf("a: %d\n",c->a); delete c; return 0; }
-- 続く --
※コメント、トラックバックはできません。(ブログじゃないから)
こんばんは
20-4-2009
トップページからのリンクで辿れないページやイメージを削除しました。
1ファイルで1MB以上のpngファイルとかあったので、ちょっとビックリ(そんなの使っていたかな?)。
事故(誤削除)を発見した方は、メールフォームから知らせていただけると助かります。
SDL 昨日の続き
前回の問題を解決する為に、マルチスレッドを使います。複数の処理を同時に走らせるテクニックです。
スレッドを作成するのはSDL_CreateThread()でできます。
SDL_Thread *SDL_CreateThread(int (*fn)(void *), void *data);
*fnは同時に走らせる関数のポインタです。*dataは*fnの引数になります。
スレッドは関数が終了すれば自然消滅しますが、強制終了させるにはSDL_KillThread()を使います。
void SDL_KillThread(SDL_Thread *thread);
「強制終了」なので、あまり使用しない方がいいでしょう。
マルチスレッドにしてみると
描画処理を別の関数に移動させ、別スレッドで処理をさせます。
#include <SDL/SDL.h> #include <SDL/SDL_image.h> SDL_Surface *image; SDL_mutex *mtx; int draw(void* dummy){ SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; // 画像複写用 SDL_Rect rect_scr2 = {0,0,300,300}; // 塗りつぶし用 SDL_Rect rect_img = {0,0,200,200}; int x; Uint32 color; SDL_PixelFormat fmt; screen = SDL_GetVideoSurface(); // 色作成 color = SDL_MapRGB(&fmt, 0, 0, 0); for(x = 0; x < 180; x++){ rect_scr.x = x; SDL_mutexP(mtx); SDL_FillRect(screen, &rect_scr2, color); SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); SDL_mutexV(mtx); SDL_Delay(20); } return 0; } int main(){ SDL_Event event; SDL_Thread *thread; int endflag = 0; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ mtx = SDL_CreateMutex(); SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); thread = SDL_CreateThread(draw, NULL); while(!endflag){ while(SDL_PollEvent(&event)){ switch(event.type){ case SDL_QUIT: case SDL_KEYDOWN: endflag = 1; break; default: break; } } SDL_Delay(1); } SDL_mutexP(mtx); SDL_KillThread(thread); SDL_mutexV(mtx); SDL_DestroyMutex(mtx); SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
斜体で書いた部分はまだ記述していない内容です。これから簡単に記述します(詳細はまた後日)。
mutexとは
スレッドの進行を制御する為のツールです。
このプログラムでは「キー入力待ち→終了」と「描画」の2つの処理が同時に動いています。
なので、まさに描画をしている最中にスレッドの終了とサーフェスの解放が起こらないようにするために、「描画中は終了しない」ためにmutexによる制御を行っています。
mutex関係で上のプログラムで使用している関数は以下のとおりです。
- SDL_CreateMutex() : mutexの作成
- SDL_DestroyMutex() : mutexの削除
- SDL_mutexP() : mutexのロック
- SDL_mutexV() : mutexの解放
SDL_mutex *SDL_CreateMutex(void); void SDL_DestroyMutex(SDL_mutex *mutex); int SDL_mutexP(SDL_mutex *mutex); int SDL_mutexV(SDL_mutex *mutex);
まず、SDL_CreateMutex()でSDL_mutexを作成し、これを複数のスレッドで共有します。(上のプログラムではグローバル変数になっているので、共有できます。)
次にSDL_mutexP()とSDL_mutexV()を使って進行を制御します。SDL_mutexP()でmutexのロックが行われた場合、別スレッドはSDL_mutexP()でmutexのロックができなくなります(その部分で処理が停止します)。SDL_mutexV()によってmutexが解放されたのち、別のスレッドはSDL_mutexP()のロックが可能になり、処理を進めることができます。
これで自由にプログラム終了できるようになりました。
mutexは難しいので
使わないバージョン。こちらの方がいいかな?
#include <SDL/SDL.h> #include <SDL/SDL_image.h> SDL_Surface *image; int endflag = 0; int draw(void* dummy){ SDL_Surface *screen; SDL_Rect rect_scr = {0,0,300,300}; // 画像複写用 SDL_Rect rect_scr2 = {0,0,300,300}; // 塗りつぶし用 SDL_Rect rect_img = {0,0,200,200}; int x; Uint32 color; SDL_PixelFormat fmt; screen = SDL_GetVideoSurface(); // 色作成 color = SDL_MapRGB(&fmt, 0, 0, 0); for(x = 0; x < 180 && endflag == 0; x++){ rect_scr.x = x; SDL_FillRect(screen, &rect_scr2, color); SDL_BlitSurface(image, &rect_img, screen, &rect_scr); SDL_Flip(screen); SDL_Delay(20); } return 0; } int main(){ SDL_Event event; SDL_Thread *thread; int status; SDL_Init(SDL_INIT_EVERYTHING); /* SDL起動 */ SDL_WM_SetCaption("ペンギン", "ぺんぎん"); SDL_SetVideoMode(300, 300, 32, SDL_HWSURFACE); image = IMG_Load("penguin.png"); thread = SDL_CreateThread(draw, NULL); while(!endflag){ while(SDL_PollEvent(&event)){ switch(event.type){ case SDL_QUIT: case SDL_KEYDOWN: endflag = 1; break; default: break; } } SDL_Delay(1); } SDL_WaitThread(thread, &status); SDL_FreeSurface(image); SDL_Quit(); /* SDL終了 */ return 0; }
なお、SDL_WaitThread()はスレッド終了まで待機する関数。
void SDL_WaitThread(SDL_Thread *thread, int *status);
-- 続く --
今日のg++
継承の続きです。
前回作ったのがこんなものでした。
#include <stdio.h> class myclass{ public: int a; }; class myclass2 : public myclass{ public: int b; }; int main(){ myclass2 *c = new myclass2(); c->b = 100; printf("b: %d\n",c->b); c->a = 50; printf("a: %d\n",c->a); delete c; return 0; }
次に、メソッドを追加してみます。
#include <stdio.h> class myclass{ public: int a; int func(){ return 1; } }; class myclass2 : public myclass{ public: int b; int func2(){ return 2; } }; int main(){ myclass2 *c = new myclass2(); c->b = 100; printf("b: %d %d\n",c->b,c->func2); c->a = 50; printf("a: %d %d\n",c->a,c->func2); delete c; return 0; }
これも特に説明はいいでしょう。
アクセス権2 (protected)
次のソースコードで、func2から変数cにアクセスできます(cはpublicであるため)。
#include <stdio.h> class myclass{ public: int a; int c; int func(){ return 1; } }; class myclass2 : public myclass{ public: int b; int func2(){ c = 1; return 2; } }; int main(){ myclass2 *c = new myclass2(); c->b = 100; printf("b: %d %d\n",c->b,c->func2); c->a = 50; printf("a: %d %d\n",c->a,c->func2); delete c; return 0; }
もちろん、cはpublicなので、クラス外部からアクセス可能です。
cがprivateの場合は、継承先、外部からのアクセスができなくなります。
#include <stdio.h> class myclass{ private: int c; public: int a; int func(){ return 1; } }; class myclass2 : public myclass{ public: int b; int func2(){ c = 1; // エラー(cはmyclassのprivate) return 2; } }; int main(){ myclass2 *c = new myclass2(); c->b = 100; printf("b: %d %d\n",c->b,c->func2); c->a = 50; printf("a: %d %d\n",c->a,c->func); delete c; return 0; }
継承先と自分自身のクラスからのアクセスのみ許可したい場合は、アクセス権をprotectedにします。
class myclass{ protected: int c; public: int a; int func(){ return 1; } }; class myclass2 : public myclass{ public: int b; int func2(){ c = 1; // OK return 2; } };
-- 続く --
※コメント、トラックバックはできません。(ブログじゃないから)
Copyright (c) 2008, 2009 greencap