ホーム > デベロッパ > BREW プログラミング入門 > 画面に描画をしてみよう > - 3 / 3 -

画面に描画をしてみよう - 3 / 3 -

図形を動かしてみよう

もぞもぞ動く図形

図形を描けるようになったら、今度は図形に動きをつけてみましょう。
ここでは、図形を動かすサンプルとして Shape アプレットを作成してみます。

今回はユーザーのキー入力により動かすのではなく、時間とともに自動的に変化するような動きをつけてみます。このような処理にはタイマーを使うとよいでしょう。

BREW は、一定時間が過ぎたときに通知してくれるタイマーを備えています。タイマーの設定は ISHELL_SetTimer() 関数により行います。

「BREW API リファレンス」で調べると、次のような関数であることが分かります。


int ISHELL_SetTimer 
( 
    IShell * pIShell,  // IShell オブジェクト
    int32 dwMSecs,     // タイマーで通知してもらう時間をミリ秒で指定
    PFNNOTIFY pfn,     // 通知を受け取る関数 (コールバック関数)
    void * pUser       // ユーザー データ
) 


PFNNOTIFY についても調べてみますと、通知を受け取る関数は次のようなプロトタイプを持つ必要があることが分かります。

void TimerCallback(void * pData)

これを使えば図形に動きをつけられそうです。

でもちょっとまってください。ここで重要な問題が出てきます。タイマーで通知されるたびに図形を動かすとして、その図形の座標はどこに記憶しておけばよいのでしょうか。

BREW には、いままで述べてこなかった重要な制限事項があります。「BREW ではグローバル変数は使えない」のです。また、 C 言語の静的変数 ( static 変数) も使うことはできません。これは BREW というシステムの構造に根ざす、避けられない制限です。

*BREW プログラミングに C++ を使う場合、静的メンバ変数を使うこともできません。

独自のアプレット構造体

ではどうやってグローバルなデータを記憶したらよいのでしょう。

BREW では、グローバルなデータはアプレット構造体に格納することになっています。これまでアプレット構造体として AEEApplet しか使用してきませんでしたが、アプレット構造体は開発者が任意に定義できるのです。

今回作成する図形移動アプレットでは、次のようにアプレット構造体を定義します。


// 独自のアプレット構造体
typedef struct
{
    AEEApplet a;        // 先頭のメンバは必ず AEEApplet 型とすること。

    int x0, y0, r0;     // 図形 0 の中心と半径
    int x1, y1, r1;     // 図形 1 の中心と半径
    int x2, y2, r2;     // 図形 2 の中心と半径
} ShapeApplet;


このように、「アプレット構造体の先頭のメンバは必ず AEEApplet 型にしないといけません」。この条件さえ満たしていれば、あとは自由にメンバ変数を加えることができます。

独自にアプレット構造体を定義した場合は、 AEEClsCreateInstance() 関数の実装を少し変更しなければなりません。

まず、アプレット構造体を作成する AEEClsCreateInstance() 関数で、 AEEApplet_New() 関数を呼び出すとき、独自アプレット構造体のサイズを指定する必要があります。アプレット構造体に必要なメモリを確保する必要があるからです。

また通常は、 AEEApplet_New() を呼び出した後で、アプレット構造体の初期化を行うことになるでしょう。




//
//  この BREW モジュールでサポートされている
//  BREW アプレットを作成する関数です。
//
int AEEClsCreateInstance(AEECLSID ClsId,IShell * pIShell,IModule * po,void ** ppObj)
{
    *ppObj = NULL;
    
    if (ClsId == AEECLSID_SHAPE) {
        if (AEEApplet_New(sizeof(ShapeApplet), ClsId, pIShell, po,
            (IApplet**)ppObj, (AEEHANDLER)Shape_HandleEvent, NULL) == TRUE) {


            // アプレット構造体の初期化を行う
            ShapeApplet* app = *ppObj;


            app->x0 = 20 + RandInt(60);
            app->y0 = 20 + RandInt(60);
            app->x1 = 20 + RandInt(60);
            app->y1 = 20 + RandInt(60);
            app->x2 = 20 + RandInt(60);
            app->y2 = 20 + RandInt(60);
            app->r0 =  4 + RandInt(10);
            app->r1 =  4 + RandInt(10);
            app->r2 =  4 + RandInt(10);

            return (AEE_SUCCESS);
        }
    }
    return (EFAILED);
}



*このコードの RandInt() は乱数を返す関数で、独自に定義した関数です。詳細はソースコードをダウンロードして確認してください。

このように、独自のアプレット構造体を定義すれば、イベント ハンドラに渡される IApplet* 引数は ShapeApplet* に変換できます。

//
//  Shape アプレットのイベント ハンドラ
//
static boolean Shape_HandleEvent(IApplet * pi, AEEEvent eCode, 
                                 uint16 wParam, uint32 dwParam)
{  
    switch (eCode) 
    {
    case EVT_APP_START:                        
        Shape_OnAppStart((ShapeApplet*) pi);
        return TRUE;
            
    case EVT_APP_STOP:
        return TRUE;
        
    default:
        break;
    }
    return FALSE;
}


それでは、 Shape アプレットの残りのコードを示すことで、ソースコードを完成させましょう。


//
//  Shape アプリが起動すると呼び出されます。
//
static void Shape_OnAppStart(ShapeApplet* app)
{
    // 描画とタイマー設定を行う
    Shape_OnTimer(app);
}


//
//  タイマーで呼び出される関数
//
static void Shape_OnTimer(ShapeApplet* app)
{
    IGraphics* graphic;


    // 図形を動かす
    app->x0 +=  RandInt(3) - 1;
    app->y0 +=  RandInt(3) - 1;
    app->x1 +=  RandInt(3) - 1;
    app->y1 +=  RandInt(3) - 1;
    app->x2 +=  RandInt(3) - 1;
    app->y2 +=  RandInt(3) - 1;

    app->r0    =  ABS(app->r0 + RandInt(3) - 1);
    app->r1    =  ABS(app->r1 + RandInt(3) - 1);
    app->r2    =  ABS(app->r2 + RandInt(3) - 1);

    // IGraphics オブジェクトの取得
    ISHELL_CreateInstance(app->a.m_pIShell, AEECLSID_GRAPHICS, &graphic);

    // 描画を行う
    Shape_OnDraw(app, graphic);

    // IGraphics オブジェクトの解放
    IGRAPHICS_Release(graphic);        

    // 再度タイマーを設定 (100ミリ秒後)
    ISHELL_SetTimer(app->a.m_pIShell, 100, Shape_OnTimer, app);
}

//
//  描画ルーチン
//
static void Shape_OnDraw(ShapeApplet* app, IGraphics* graphic)
{
    AEECircle c0, c1, c2;
    AEETriangle tri;

    // 背景を白でクリア
    IGRAPHICS_SetBackground(graphic, 255, 255, 255);
    IGRAPHICS_ClearViewport(graphic);

    // 三角形の描画
    tri.x0  = app->x0;
    tri.y0  = app->y0;
    tri.x1  = app->x1;
    tri.y1  = app->y1;
    tri.x2  = app->x2;
    tri.y2  = app->y2;

    IGRAPHICS_SetFillMode(graphic, FALSE);
    IGRAPHICS_SetColor(graphic, 0, 0, 0, 0);
    IGRAPHICS_DrawTriangle(graphic, &tri);

    // 円の描画
    c0.cx   = app->x0;
    c0.cy   = app->y0;
    c0.r    = app->r0;

    c1.cx   = app->x1;
    c1.cy   = app->y1;
    c1.r    = app->r1;

    c2.cx   = app->x2;
    c2.cy   = app->y2;
    c2.r    = app->r2;

    IGRAPHICS_SetFillMode(graphic, TRUE);
    IGRAPHICS_SetFillColor(graphic, 255, 0, 0, 0);
    IGRAPHICS_DrawCircle(graphic, &c0);

    IGRAPHICS_SetFillColor(graphic, 0, 255, 0, 0);
    IGRAPHICS_DrawCircle(graphic, &c1);

    IGRAPHICS_SetFillColor(graphic, 0, 0, 255, 0);
    IGRAPHICS_DrawCircle(graphic, &c2);

    // 画面ビットマップへの描画を実際に表示する
    IGRAPHICS_Update(graphic);
}