お知らせ

DXライブラリでデスクトップマスコットを作ろう 第4回

まいどどうも(o´・∀・)o 久々に連載再開します。

前回までで、
・ウィンドウを作ることと、
・ウィンドウに絵を表示すること
ができました。

ちなみに、この連載での最終的な成果物は↓これです。
(紫色のチビ忍者が、パソコンの画面の中でシュタタタタ…と、勝手に動くのを想像してください。)
DXライブラリでデスクトップ忍者

 

 

 

 

 

 

 

(1)デスクトップマスコットの仕組み

さて、この連載でのデスクトップマスコットは、
いったいどういうからくりで動くものなのかというと…

・透明なウィンドウ(目に見えないウィンドウ)に、
・マスコットキャラの絵を表示して、
・その絵を動かす

これだけです。シンプルですね。

順に説明しますよ。

まず、透明なウィンドウ
これは、DXライブラリにその機能があります。便利ですなぁ。
(具体的なやり方は、この回のおしまいにお見せしますよ。)

次に、マスコットキャラの絵を表示する
これはもうできますね。
DxLib::LoadGraph()して、DxLib::DrawGraph()するのでしたね。

最後、絵を動かす
さあ、これです。絵を動かすことがこの連載の本質であり、その第一歩が、この第4回なのです。

(2)絵を動かす仕組み

まずは、以下をご覧あれ(GIFアニメです)。

走る忍者GIFアニメさて、この絵はどうやって動いている(ように見える)のでしょうか。
もちろんパラパラ漫画と同じ原理です。
高速に絵を切り替えてるだけです。

(ここでは3枚のパターンの絵をいい感じにパラパラさせてます)

デスクトップマスコットも同じです。
つまり、高速に絵を切り替えるということを、プログラムでやればいいのです。

(3)メインループという考え方

パラパラ漫画は、たくさんの紙に、それぞれの瞬間の絵を描きますね。
ところが、デスクトップマスコットは、ウィンドウというひとつの紙しかありません。

ではどうするかというと、
ひとつの紙に、絵を出したら、すぐにその絵を消して、次の絵をだす…といったことをすればよいのです。

描画 ⇒ クリア ⇒ 描画 ⇒ クリア…。
というイメージですね。

実際には、どのタイミングでどの絵を、どの位置に出すべきなのかといった、
演算(計算)をする必要もあるので、

演算 ⇒ 描画 ⇒ クリア ⇒ 演算 ⇒ 描画 ⇒ クリア…。
があるべき姿となります。

疑似コードで表すと、こうなります。

[cpp]
int WINAPI WinMain( /*省略*/ )
{
// 無限ループ処理
while( /*省略*/ )
{
// 演算
Update() ;

// 描画
Draw() ;

// ウィンドウの中を全部クリア
Clear() ;
}

// プログラム終了
return 0 ;
}
[/cpp]

Clear()は、ウィンドウの中を全て空っぽにします。
一部分だけを消す、といったことはしません。
つまり、毎回、ウィンドウの中を全部描きなおすのです。

こういう無限ループのことを、メインループとかゲームループとか言いますが、
デスクトップマスコットのプログラムというのは、
このメインループの中のUpdate()とDraw()の中身を作ることに他なりません。

自分だけのロジックを、メインループの中にひたすらプログラミングしてゆく…。
この感覚は、イベントドリヴンなWindowフォームアプリや、
Post/Getがトリガーとなるブラウザアプリの開発しかやったことが無い人には、
なかなか刺激的な体験になると思います。

(4)ついでにダブルバッファリング

さて、メインループの中で描いたり消したりを繰り返すことで、ある問題が起こります。
メインループというのは、1秒間に何十回という超高速なペースで繰り返されるものですが、
実際にやると、チカチカと点滅して見えてしまうのです。人間の眼は優秀です。

ではどうするかというと、
見えない紙に先に下書きして、見せたいタイミングになったら、見える紙に複写する。
こういう手法を取ります。

これをダブルバッファリングと呼びます。

この連載の中では、
見えない紙のことをウラ画面と呼びます。
見える紙のことをオモテ画面と呼びます。
描画とクリアを繰り返すのはウラ画面で行い、描画後の瞬間だけ、オモテ画面に反映するのです。

疑似コードで表すと、↓こうなります。

[cpp]
int WINAPI WinMain( /*省略*/ )
{
// 無限ループ処理
while( /*省略*/ )
{
// 演算
Update() ;

// ウラ画面に描画
DrawUra() ;

// オモテ画面に複写
CopyOmote() ;

// ウラ画面の中を全部クリア
ClearUra() ;
}

// プログラム終了
return 0 ;
}
[/cpp]

こうすることで、オモテ画面には常に絵が見えている状態になり、チカチカしなくなるのです。

(5)今回の成果コード

色んなパターンの絵を切り替える、ということはさておきで、
先ほどまでの疑似コードをちゃんとコーディングするとこうなります。
ウィンドウの透明化の方法も書いておきますよ。

[cpp]
#include <DxLib.h> // DXライブラリを使います

//**************************************************************
// 定数宣言
//**************************************************************
// 描画スクリーン(ウラ・オモテ画面)のサイズ
static const int SCREEN_SIZE_W = 200 ; // 幅px
static const int SCREEN_SIZE_H = 200 ; // 高px

//**************************************************************
// WinMain関数(プログラムはここから実行されます)
//**************************************************************
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 1.DXライブラリの使用前の準備
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ウィンドウを透明に設定します
DxLib::ChangeWindowMode( TRUE ) ; // ウィンドウモードにします
DxLib::SetGraphMode( SCREEN_SIZE_W, SCREEN_SIZE_H, 32 ) ; // 描画スクリーンのサイズを指定します
DxLib::SetWindowStyleMode( 2 ) ; // ウィンドウのスタイルを枠無しにします
DxLib::SetUseBackBufferTransColorFlag( TRUE ) ; // ウィンドウを透明にします

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 2.DXライブラリの初期処理(ウィンドウを生成します)
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//————————————
// 2-1.ウィンドウの生成
//————————————
if( DxLib::DxLib_Init() != 0 )
{
// 失敗したら直ちに終了します
return -1 ;
}

//————————————
// 2-2.画像を描画する画面の設定
//————————————
// オモテ画面を作ってハンドルを得ます ※ウラ画面はDXライブラリが最初から用意しています
const int omoteHandle = DxLib::MakeRGB8ColorSoftImage( SCREEN_SIZE_W, SCREEN_SIZE_H ) ;
// ウラ画面(みえない画面)に描画するように設定しておきます
DxLib::SetDrawScreen( DX_SCREEN_BACK ) ;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 3.デスクトップマスコットの初期処理
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// マスコットの画像をメモリにロードしてハンドルを得ます
const int graphHandle = DxLib::LoadGraph( "GRAPH/TEST_GRAPH.png", TRUE ) ;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 4.メインループ
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Windowsからのメッセージ処理を行いながら永遠にループします
while( DxLib::ProcessMessage() == 0 )
{
// 演算
// まだやることないでゴザル(o´・∀・)

// ウラ画面(見えない画面)に描画
DxLib::DrawGraph( 0, 0, graphHandle, TRUE ) ;

// ウラ画面(見えない画面)の内容をオモテ画面(見える画面)に複写します
DxLib::GetDrawScreenSoftImage( 0, 0, SCREEN_SIZE_W, SCREEN_SIZE_H, omoteHandle ) ;
// オモテ画面(見える画面)の内容をウインドウに反映します
DxLib::UpdateLayerdWindowForSoftImage( omoteHandle ) ;

// ウラ画面(見えない画面)の中を全部クリアします
DxLib::ClearDrawScreen() ;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 5.デスクトップマスコットの終了処理
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// マスコットの画像データをメモリから解放します
DxLib::DeleteGraph( graphHandle ) ;
// オモテ画面の領域をメモリから解放します ※ウラ画面はDXライブラリが勝手に破棄してくれます
DxLib::DeleteGraph( omoteHandle ) ;

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// 6.DXライブラリの終了処理
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ウィンドウを破棄してDXライブラリが確保したメモリ領域などをすべて解放します
DxLib::DxLib_End() ;

// プログラム終了します
return 0 ;
}
[/cpp]

知らない関数ばっかり出てると思いますが、関数を覚える必要はないです。
大事なのは何をやってるのかわかる、ということです。
全部日本語でコメント書いてるからわかるでしょう。

ちょっとだけ解説すると、ウラ画面とオモテ画面をどちらも200*200というサイズで作っています。
そして、ループで、ウラ⇒オモテに転写してウィンドウ内の表示を更新しています。
転写するためにはウラとオモテがぴったり同じサイズでなければなりません。

実行すると↓こうなります。
実行結果サンプル

前回の絵を流用してるので(一枚しかないので)、パラパラしているようには見えませんが、
実際には目にもとまらぬ速度でパラパラしているのです。

 

 

 

 

次回へ続くドンヾ(´・∀・`)