Aqualeadウィジェットチュートリアル


目次

1. 概要
2. ボタンウィジェットの表示
3. カーソルの表示
4. ウィジェットの操作
5. ポインティングデバイスによる操作
6. カーソルの変更
7. モーション
8. 終わりに

第1章 概要

Aqualeadのウィジェットとはメニューを作るためのクラスの総称です。

ウィジェットはゲーム中のメニューを作るためにに作られており、 そのため標準の見た目などが存在しません。 その代わり、Aqualeadの全ての描画機能を使ってメニューを構築する事が出来るため、 大半のメニューを実現する事が出来ます。

ウィジェットはプログラムで作る事も可能ですが、データ定義とスクリプトで作る事も出来ます。 その場合はプログラマはメニューを作る作業はほとんど必要なくなります。 その為のGUIツールは現在構築中です。

このチュートリアルでは基本となるボタンウィジェットとカーソルを扱います。 このほかリストを扱うためのウィジェットや、このチュートリアルでは扱わないいろいろな機能がありますが、それはまた別のチュートリアルで扱います。

今回のチュートリアル作るメニューは以下のようになります。

シンプルなメニューですが、これの応用で大半のメニューを作る事が可能です。

第2章 ボタンウィジェットの表示

まずはボタンウィジェットを生成し、表示させてみます。

前述の通り、ボタンウィジェットの表示と言ってもボタンウィジェット自体は表示部分は持っていないので、 今回はスプライトとテキストを組み合わせて表示部分を作ります。

各ウィジェットは全てグループノードなので、 表示用のノードはこのウィジェットにAddNodeまたはAddReleaseNodeで登録します。

また、今回の例ではテキスト文字列以外は同一のボタンを三つ作るので、 Clone関数を使用しています。 ウィジェット関連は設定箇所が多く、また似たような設定の物を複数作る事が多いためClone関数を使うと便利です。

ソースは以下のようになります。 この時点ではまだ操作できません。

#include "Aqualead.h"

void ALInit()
{
    ALStream::SetBasePath( "..\\..\\..\\..\\Data\\" );
}

void ALMain()
{
    ALButtonWidget *bw1 = ALButtonWidget::Create();                     (1)

    ALSprite *sp1 = ALSprite::Create();
    sp1->LoadTexture( "ButtonBase" );
    bw1->AddReleaseNode( sp1 );                                         (2)
    sp1->Show();                                                        (3)

    ALText *tx1 = ALText::CreateRelease( ALFont::Create( "MainFont.aft" ) );
    tx1->SetColor( ALColor( 1.0f, 0.3f, 0.5f ) );
    tx1->AddDrawPrio( -1 );                                             (4)
    tx1->Show();
    tx1->SetY( 16 );
    tx1->SetWidth( 200 );
    tx1->SetAlign( ALTEXT_ALIGN_CENTER );
    bw1->AddReleaseNode( tx1 );

    bw1->Show();                                                        (5)
    bw1->SetPos( 220, 100 );                                            (6)
    ALButtonWidget *bw2 = bw1->Clone();                                 (7)
    bw2->SetY( 164 );
    ALButtonWidget *bw3 = bw1->Clone();
    bw3->SetY( 228 );

    tx1->AddString( "最初から" );
    ALText *tx2 = ( ALText * )bw2->FindNodeByType( ALNODE_TYPE_TEXT );  (8)
    tx2->AddString( "続きから" );
    ALText *tx3 = ( ALText * )bw3->FindNodeByType( ALNODE_TYPE_TEXT );
    tx3->AddString( "オプション" );

    while( !ALSystem::IsTerminated() ){
        ALYield();
    };

    bw3->Destroy();
    bw2->Destroy();
    bw1->Destroy();

}

(1)

ボタンウィジェットを生成します。引数にはスキンやオーナーを指定する事が出来ますが、これは後述します。

(2)

スプライトをボタンウィジェットを追加します。AddReleaseNodeを使用すると解放をウィジェットに任せる事が出来ます。

なお、ウィジェットにノードを追加すると、自動的に親がそのウィジェットになります。

(3)

追加したスプライトを表示します。このようにウィジェットに追加するノードも明示的に表示する必要があります。

(4)

テキストのプライオリティを少し上げて、スプライトより手前に表示します。

ウィジェットでは複数の描画ノードの組み合わせを使う事が多いですが、このように明示的にプライオリティの設定をしないと予期せぬ表示になる事があるので注意して下さい。

(5)

ウィジェット自体を表示します。

(6)

ウィジェットの表示値を設定します。基準位置は表示用ノードの設定次第になります。今回はスプライト・テキスト共に左上を原点としているので、左上が基準座標です。

(7)

先ほど設定したウィジェットのクローンを生成します。グループ内のスプライト、テキストも同時にクローンされます。

(8)

クローンしたウィジェットからテキストノードを検索して取得します。

今回はテキストノードはウィジェット内に一つなので種別で検索しましたが、複数存在する場合はノードにIDをつけFindNode関数で検索する必要があります。

第3章 カーソルの表示

次はカーソルを表示します。 Aqualeadでは選択中のボタンを示すために、矢印のようなアイコンなどをカーソルとして使う事が出来ます。 もちろん、カーソルを使わずに選択中のボタンの表示を変えて選択中にしたり、 ボタンの表示変更とカーソルの両方を使う事も可能です。

カーソルを使うためには、ウィジェットのオーナーという概念を使う必要があります。 ウィジェットにはグループや親子関係とは別の階層構造があります。 単純なメニュー以外では、ウィジェットにはオーナーを指定し、各ウィジェットはオーナーが所有するという形を取ります。

所有されたウィジェットはオーナー削除時に自動削除されます。オーナーを削除後に、手動で所有されたウィジェットを削除すると多重削除エラーが発生するので注意して下さい。

オーナーにはALContainerWidgetかそのサブクラスを指定します。 これらのウィジェットは別のウィジェットを所有し、階層構造を持つために使用します。 各ウィジェットはオーナーの中で一つだけフォーカスを持つ事が出来、 フォーカスを持つウィジェットが選択中となり、カーソルを表示が可能になります。

カーソルはこのオーナーとなるウィジェットに設定をし、 そこに含まれるウィジェットがそのカーソルを使用します。

また、カーソルを使う場合は、通常ウィジェットのサイズが必要になります。 カーソルの位置やサイズは、カーソルのあるウィジェットのサイズに合わせて変わります。 そのため、適切なサイズ指定をしないとカーソルが正しく表示されません。

とりあえずは今回はデバッグ用に用意されているデフォルトのカーソルを使用します。

#include "Aqualead.h"

void ALInit()
{
    ALStream::SetBasePath( "..\\..\\..\\..\\Data\\" );
}

void ALMain()
{
    ALContainerWidget *cw = ALContainerWidget::Create();                    (1)
    ALWidget::SetDefaultOwner( cw );                                        (2)
    cw->Release();                                                          (3)

    ALButtonWidget *bw1 = ALButtonWidget::Create(); 
    bw1->SetHeight( 50 );                                                   (4)
    ALSprite *sp1 = ALSprite::Create();
    sp1->LoadTexture( "ButtonBase" );
    bw1->AddReleaseNode( sp1 ); 
    sp1->Show();

    ALText *tx1 = ALText::CreateRelease( ALFont::Create( "MainFont.aft" ) );
    tx1->SetColor( ALColor( 1.0f, 0.3f, 0.5f ) );
    tx1->AddDrawPrio( -1 ); 
    tx1->Show();
    tx1->SetY( 16 );
    tx1->SetWidth( 200 );
    tx1->SetAlign( ALTEXT_ALIGN_CENTER );
    bw1->AddReleaseNode( tx1 );

    bw1->Show();
    bw1->SetPos( 220, 100 );
    ALButtonWidget *bw2 = bw1->Clone();
    bw2->SetY( 164 );
    ALButtonWidget *bw3 = bw1->Clone();
    bw3->SetY( 228 );

    tx1->AddString( "最初から" );
    ALText *tx2 = ( ALText * )bw2->FindNodeByType( ALNODE_TYPE_TEXT );
    tx2->AddString( "続きから" );
    ALText *tx3 = ( ALText * )bw3->FindNodeByType( ALNODE_TYPE_TEXT );
    tx3->AddString( "オプション" );

    cw->UseCursor();                                                        (5)

    while( !ALSystem::IsTerminated() ){
        ALYield();
    };

    ALWidget::SetDefaultOwner( NULL );                                      (6)

}

(1)

コンテナウィジェットを生成します。これをボタンウィジェットのオーナーにします。

(2)

生成したコンテナウィジェットを、ウィジェットのデフォルトオーナーとして設定します。以降指定がなければこのウィジェットがオーナーとなります。

(3)

オーナーに設定した時点で参照カウントが増えるので、ここで減らしておくとオーナー解除時にDestoryが自動実行されます。

(4)

ウィジェットの縦幅を設定します。今回は横幅は使用しないので、縦のみ設定します。

(5)

カーソルを生成します。なお、引数でfalseを設定するとカーソルを削除できます。

(6)

デフォルトオーナーを解除します。すでにReleaseが実行されているので、オーナーのDestroyも実行されます。

それに伴い、オーナーに所有されている他のボタンウィジェットも同時にDestroyされます。

第4章 ウィジェットの操作

それでは、このボタンウィジェットを操作できるようにしましょう。 Aqualeadではパッド入力と、マウス等のポインティングデバイスのクリック両方に対応しています。 まずはパッド入力を行います。

パッド入力の場合、上下左右を押された場合にどのウィジェットにフォーカスを移すかを設定します。

決定はWidgetに設定されているDecisionKeyを使います。 デフォルトではALPAD_Aですが、これは変更も可能です。

決定ボタンが押されると、ウィジェットは決定状態になり、設定があればコールバックを呼び出します。 このコールバック内ではどのウィジェットが決定されたか、どのような理由で決定されたか、 どのボタンで決定されたか等の情報が取得できます。

このほか、ショートカットキーの設定が出来ます。 ショートカットキーは決定の他のキーを使い、 そのキーが押されると現在の選択に関わらずそのウィジェットが決定されます。

#include "Aqualead.h"

void ALInit()
{
    ALStream::SetBasePath( "..\\..\\..\\..\\Data\\" );
}

static void WidgetDecisionFunc( ALWidget *self );

void ALMain()
{
    ALContainerWidget *cw = ALContainerWidget::Create();
    ALWidget::SetDefaultOwner( cw );
    cw->Release();

    ALButtonWidget *bw1 = ALButtonWidget::Create();
    bw1->SetHeight( 50 );
    ALSprite *sp1 = ALSprite::Create();
    sp1->LoadTexture( "ButtonBase" );
    bw1->AddReleaseNode( sp1 ); 
    sp1->Show();
    bw1->SetAddDecisionFunc( WidgetDecisionFunc );                          (1)

    ALText *tx1 = ALText::CreateRelease( ALFont::Create( "MainFont.aft" ) );
    tx1->SetColor( ALColor( 1.0f, 0.3f, 0.5f ) );
    tx1->AddDrawPrio( -1 ); 
    tx1->Show();
    tx1->SetY( 16 );
    tx1->SetWidth( 200 );
    tx1->SetAlign( ALTEXT_ALIGN_CENTER );
    bw1->AddReleaseNode( tx1 );

    bw1->Show();
    bw1->SetPos( 220, 100 );
    ALButtonWidget *bw2 = bw1->Clone();
    bw2->SetY( 164 );
    ALButtonWidget *bw3 = bw1->Clone();
    bw3->SetY( 228 );

    tx1->AddString( "最初から" );
    ALText *tx2 = ( ALText * )bw2->FindNodeByType( ALNODE_TYPE_TEXT );
    tx2->AddString( "続きから" );
    ALText *tx3 = ( ALText * )bw3->FindNodeByType( ALNODE_TYPE_TEXT );
    tx3->AddString( "終了" );

    bw1->SetTag( 1 );                                                       (2)
    bw2->SetTag( 2 );
    bw3->SetTag( 3 );

    bw1->SetDownWidget( bw2 );                                              (3)
    bw2->SetUpWidget( bw1 );
    bw2->SetDownWidget( bw3 );
    bw3->SetUpWidget( bw2 );

    bw3->AddShortCutButton( ALPAD_B );                                      (4)

    cw->UseCursor();

    while( !ALSystem::IsTerminated() ){
        ALYield();
    };

    ALWidget::SetDefaultOwner( NULL );
}

void WidgetDecisionFunc( ALWidget *self )                                   (5)
{
    if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_BUTTON_DECISION ) != 0 ){              (6)
        ALPrintf("Button Click ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_SHORTCUT_DECISION ) != 0 ){            (7)
        ALPrintf("ShortCut Click ");
    };

    if( ( self->GetActionReason() & ALPAD_A ) != 0 ){                       (8)
        ALPrintf("A ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_BUTTON_MASK ) == ALPAD_B ){            (9)
        ALPrintf("B ");
    };

    ALPrintf("Decision %d %x\n", self->GetTag(), self->GetActionReason() ); (10)
};

(1)

決定時に呼び出されるコールバック関数を登録します。

Cloneを実行するとコールバック呼び出しもコピーされるので、全てのボタンウィジェットで共有されます。

(2)

ウィジェットの区別用にタグを設定します。

これはアプリケーションで自由に使用できる32ビットの数値です。UserDataとは別物です。

(3)

上下左右を押したときに、どのウィジェットに移動するかをそれぞれ設定します。

なお、bw1のUpWidgetにbw3を、bw3のDownWidgetにbw1を指定する事で上下ループになります。

(4)

ショートカットボタンを設定します。このショートカットが押されると、そのウィジェットにフォーカスが移り決定状態になります。

ショートカットボタンは複数割り当てる事も出来ます。

また、決定まで行わず、フォーカスのみ移動に設定する事も可能です。

(5)

決定状態になったときに呼び出されるコールバックです。

一度決定状態になったときにはClearDecisionを呼び出したり、フォーカスを移さない限りは決定状態が維持され、続けてこのコールバックが呼ばれる事はありません。

(6)

ActionReasonにより、どのような理由でコールバックが発生したかを取得が出来ます。

この場合は、通常の決定ボタンで決定が行われた時の処理です。

(7)

この場合は、ショートカットボタンで決定が行われた時の処理です。

(8)

ActionReasonにはボタンの種別も格納されています。

必要があれば、このようにボタン種別での判定も可能です。

(9)

ALWIDGET_ACTION_REASON_BUTTON_MASKを使う事により、ボタン部分のみ取り出す事も出来ます。

(10)

Tagを使う事により、どのウィジェットが決定されたかを区別する事が出来ます。

第5章 ポインティングデバイスによる操作

ウィジェットはパッドだけではなく、マウス等のポインティングデバイスで操作する事も可能です。

ポインティングデバイスで操作する場合は、ウィジェットにクリック範囲となるコリジョンを設定します。 通常、コリジョンは矩形の2DRectCollisionを使用しますが、 それ以外の形状のコリジョンや複数コリジョンの使用も可能なので、複雑な形状をクリック範囲に設定することも可能です。

ボタンウィジェットのクリックは、選択中のウィジェットをクリックすると決定になりますが、 選択をされていないウィジェットをクリックするとフォーカスがそのウィジェットに移るだけになります。 ウィジェット毎の設定により、これを常にクリックで決定に変更する事も可能です。

もしウィジェットが重なり、クリック範囲が重なった場合は表示の優先順位に従い、手前のものが選択されます。

#include "Aqualead.h"

void ALInit()
{
    ALStream::SetBasePath( "..\\..\\..\\..\\Data\\" );
}

static void WidgetDecisionFunc( ALWidget *self );

void ALMain()
{
    ALContainerWidget *cw = ALContainerWidget::Create();
    ALWidget::SetDefaultOwner( cw );
    cw->Release();

    ALButtonWidget *bw1 = ALButtonWidget::Create();
    bw1->SetHeight( 50 );
    ALSprite *sp1 = ALSprite::Create();
    sp1->LoadTexture( "ButtonBase" );
    bw1->AddReleaseNode( sp1 ); 
    sp1->Show();
    bw1->SetAddDecisionFunc( WidgetDecisionFunc );
    bw1->AddCollision( 
        AL2DRectCollision::Create( 0, sp1->GetViewRect() ) );               (1)

    ALText *tx1 = ALText::CreateRelease( ALFont::Create( "MainFont.aft" ) );
    tx1->SetColor( ALColor( 1.0f, 0.3f, 0.5f ) );
    tx1->AddDrawPrio( -1 ); 
    tx1->Show();
    tx1->SetY( 16 );
    tx1->SetWidth( 200 );
    tx1->SetAlign( ALTEXT_ALIGN_CENTER );
    bw1->AddReleaseNode( tx1 );

    bw1->Show();
    bw1->SetPos( 220, 100 );
    ALButtonWidget *bw2 = bw1->Clone();
    bw2->SetY( 164 );
    ALButtonWidget *bw3 = bw1->Clone();
    bw3->SetY( 228 );

    tx1->AddString( "最初から" );
    ALText *tx2 = ( ALText * )bw2->FindNodeByType( ALNODE_TYPE_TEXT );
    tx2->AddString( "続きから" );
    ALText *tx3 = ( ALText * )bw3->FindNodeByType( ALNODE_TYPE_TEXT );
    tx3->AddString( "終了" );

    bw1->SetTag( 1 );
    bw2->SetTag( 2 );
    bw3->SetTag( 3 );

    bw1->SetDownWidget( bw2 );
    bw2->SetUpWidget( bw1 );
    bw2->SetDownWidget( bw3 );
    bw3->SetUpWidget( bw2 );

    bw3->AddShortCutButton( ALPAD_B );

    bw3->SetDirectDecision();                                           (2)

    cw->UseCursor();

    while( !ALSystem::IsTerminated() ){
        ALYield();
    };

    ALWidget::SetDefaultOwner( NULL );
}

void WidgetDecisionFunc( ALWidget *self )
{
    if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_BUTTON_DECISION ) != 0 ){
        ALPrintf("Button Click ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_SHORTCUT_DECISION ) != 0 ){
        ALPrintf("ShortCut Click ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_CLICK_DECISION ) != 0 ){           (3)
        ALPrintf("Mouse Click ");
    };

    if( ( self->GetActionReason() & ALPAD_A ) != 0 ){
        ALPrintf("A ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_BUTTON_MASK ) == ALPAD_B ){
        ALPrintf("B ");
    };

    ALPrintf("Decision %d %x\n", self->GetTag(), self->GetActionReason() );
};

(1)

クリック用の矩形コリジョンを設定します。第1引数であるアトリビュートは内部で自動設定されるので、0を設定しておきます。

ViewRectはそのノードの描画範囲を矩形で取得します。

(2)

このボタンのみ、クリックでそのまま決定する設定に変更します。

パッド入力を受け付けず、カーソルを作らない場合は全てこの設定にした方がいいでしょう。

(3)

クリック時のActionReasonを判定します。

なお、クリック時はボタン種別には何も値が入りません。

第6章 カーソルの変更

今まではデフォルトのカーソルを使用していましたが、これは独自のカーソルに変更することも可能です。

カーソルは見た目だけではなく、動きなども設定が出来ます。 動きはフォーカス変更時に発生するコールバック関数で設定します。 移動時のコールバック関数の設定をしなければ、現在のようにフォーカスの対象に瞬時に移動します。

対象のウィジェットのサイズも取得できるので、ウィジェットを囲むようなカーソルも実装が可能です。

#include "Aqualead.h"

void ALInit()
{
    ALStream::SetBasePath( "..\\..\\..\\..\\Data\\" );
}

static void WidgetDecisionFunc( ALWidget *self );
static void CursorInitFunc( ALFuncWidgetCursor *self );
static void CursorChangeFunc( ALFuncWidgetCursor *self, bool instantly );

void ALMain()
{
    ALContainerWidget *cw = ALContainerWidget::Create();
    ALWidget::SetDefaultOwner( cw );
    cw->Release();

    ALButtonWidget *bw1 = ALButtonWidget::Create();
    bw1->SetHeight( 50 );
    ALSprite *sp1 = ALSprite::Create();
    sp1->LoadTexture( "ButtonBase" );
    bw1->AddReleaseNode( sp1 ); 
    sp1->Show();
    bw1->SetAddDecisionFunc( WidgetDecisionFunc );
    bw1->AddCollision( 
        AL2DRectCollision::Create( 0, sp1->GetViewRect() ) );

    ALText *tx1 = ALText::CreateRelease( ALFont::Create( "MainFont.aft" ) );
    tx1->SetColor( ALColor( 1.0f, 0.3f, 0.5f ) );
    tx1->AddDrawPrio( -1 ); 
    tx1->Show();
    tx1->SetY( 16 );
    tx1->SetWidth( 200 );
    tx1->SetAlign( ALTEXT_ALIGN_CENTER );
    bw1->AddReleaseNode( tx1 );

    bw1->Show();
    bw1->SetPos( 220, 100 );
    ALButtonWidget *bw2 = bw1->Clone();
    bw2->SetY( 164 );
    ALButtonWidget *bw3 = bw1->Clone();
    bw3->SetY( 228 );

    tx1->AddString( "最初から" );
    ALText *tx2 = ( ALText * )bw2->FindNodeByType( ALNODE_TYPE_TEXT );
    tx2->AddString( "続きから" );
    ALText *tx3 = ( ALText * )bw3->FindNodeByType( ALNODE_TYPE_TEXT );
    tx3->AddString( "終了" );

    bw1->SetTag( 1 );
    bw2->SetTag( 2 );
    bw3->SetTag( 3 );

    bw1->SetDownWidget( bw2 );
    bw2->SetUpWidget( bw1 );
    bw2->SetDownWidget( bw3 );
    bw3->SetUpWidget( bw2 );

    bw3->AddShortCutButton( ALPAD_B );

    bw3->SetDirectDecision();

    ALFuncWidgetCursor *wc = 
        ALFuncWidgetCursor::Create( CursorInitFunc, CursorChangeFunc );     (1)

    cw->SetCursor( wc );                                                    (2)

    while( !ALSystem::IsTerminated() ){
        ALYield();
    };

    ALWidget::SetDefaultOwner( NULL );
}

void WidgetDecisionFunc( ALWidget *self )
{
    if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_BUTTON_DECISION ) != 0 ){
        ALPrintf("Button Click ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_SHORTCUT_DECISION ) != 0 ){
        ALPrintf("ShortCut Click ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_CLICK_DECISION ) != 0 ){
        ALPrintf("Mouse Click ");
    };

    if( ( self->GetActionReason() & ALPAD_A ) != 0 ){
        ALPrintf("A ");
    } else if( ( self->GetActionReason() 
            & ALWIDGET_ACTION_REASON_BUTTON_MASK ) == ALPAD_B ){
        ALPrintf("B ");
    };

    ALPrintf("Decision %d %x\n", self->GetTag(), self->GetActionReason() );
};

void CursorInitFunc( ALFuncWidgetCursor *self )                             (3)
{
    ALSprite *sp = ALSprite::Create( "Cursor" );
    sp->Show();
    sp->SetCenter( 32, 16 );                                                (4)
    self->AddReleaseNode( sp );                                             (5)
    ALController *cnt = ALIPMoveController::Create(                         (6)
        self->FindProp("Position"), ALIP_TYPE_QUADRATIC_REVERSE, ALVector( 0, 0, 0 ), 8 );
    cnt->Sleep();                                                           (7)
    self->AddController( cnt );
}

void CursorChangeFunc( ALFuncWidgetCursor *self, bool instantly )           (8)
{
    const ALRect & r = self->GetTargetRect();                               (9)
    if( instantly ){
        self->SetPos( r.Left, ( float )( r.Top+r.GetHeight()/2 ) );         (10)
    } else {
        ALIPMoveController *mc = 
            ( ALIPMoveController * )( self->FindController( ALCONTROLLER_TYPE_IP_MOVE ) );
        mc->Restart(                                                        (11)
            ALVector( r.Left, ( float )( r.Top+r.GetHeight()/2 ), 0 ), false ); 
    };
}

(1)

コールバック関数を使用したカーソルを生成します。

コールバック関数には初期化、位置更新、終了時の3種類のコールバックが設定できます。

今回は終了時のコールバックは使用していません。

(2)

UseCursorの代わりに、SetCursorで生成したカーソルを渡します。

(3)

初期化用コールバックです。カーソルが新規に生成されるときに呼び出されます。

このコールバックを使用せず、ALFuncWidgetCursor生成時に直接記述しても構いません。

(4)

カーソルの中心位置を指定します。

元ファイルはbmpなので中心位置は原点ですが、これを右端で上下中央を原点に変更します。

(5)

スプライトをカーソルに登録します。AddReleaseNodeを使用しているので、カーソル破棄時に一緒に破棄されます。

(6)

カーソル移動用のコントローラを使用します。座標を移動させるため、対象はPositionプロパティになります。

補間方法は2次関数の反転で、最初に急加速してゆっくり止まる動きになります。移動時間は8フレームです。

(7)

コントローラはまだ使用しないので、いったんスリープしておきます。

(8)

カーソル移動時のコールバックです。

カーソルを常に選択位置へ瞬時に移動する場合はこのコールバックは必要ありません。

引数はカーソル自身の他、瞬時に位置を変えるか、補間するかのフラグがあります。

(9)

ターゲットとなるウィジェットの範囲を取得します。カーソルはこの位置に合わせて移動させる必要があります。

(10)

instantlyフラグが立っているときは、初期化時やショートカットクリック等で瞬時に移動する必要があるので、座標を直接更新します。

(11)

instantlyフラグが立っていないときは、あらかじめ生成した補間コントローラを使って座標を移動させます。

第7章 モーション

次はウィジェットにモーションをつけてみます。

モーションはツールで作ったモーションデータを読む方法もありますが、 今回は動的モーションを使って、プログラムでモーションを作ります。

動的モーションはプログラムでモーションを生成する物です。 メモリや速度的には非効率ですが、ちょっとした動きを作るには便利です。

ウィジェットには各種ステート毎に呼び出すモーションIDを設定できます。 ステートとは、通常状態、選択状態、決定状態等です。 今回はその3種のみを使います。 その設定があると、ステート変更時に自動的にモーションが再生されます。

なお、今回はメイン関数以外の変更はないので、メイン関数以外のソースは省略します。

void ALMain()
{
    ALContainerWidget *cw = ALContainerWidget::Create();
    ALWidget::SetDefaultOwner( cw );
    cw->Release();

    ALButtonWidget *bw1 = ALButtonWidget::Create();
    bw1->SetHeight( 50 );
    ALSprite *sp1 = ALSprite::Create();
    sp1->LoadTexture( "ButtonBase" );
    bw1->AddReleaseNode( sp1 ); 
    sp1->Show();
    bw1->SetAddDecisionFunc( WidgetDecisionFunc );
    bw1->AddCollision( 
        AL2DRectCollision::Create( 0, sp1->GetViewRect() ) );

    ALProp cpr = bw1->FindProp("Color");                            (1)
    bw1->BeginDynamicMotion();                                      (2)
    bw1->AddMotionKey( cpr, 0 );                                    (3)
    bw1->AddMotionKey( cpr, 60 );
    bw1->SetColor( ALColor( 0.8f, 0.8f, 0.8f ) );                   (4)
    bw1->AddMotionKey( cpr, 30 );
    bw1->SetMotionIPType( cpr, ALIP_TYPE_SIN );                     (5)
    bw1->SetMotionID( 2 );                                          (6)

    bw1->NewDynamicMotion();                                        (7)
    for( Uint32 i=0; i<5; i++ ){
        bw1->SetColor( ALColor( 0.8f, 0.8f, 0.8f ) );
        bw1->AddMotionKey( cpr, i*4 );
        bw1->SetColor( ALColor::White );
        bw1->AddMotionKey( cpr, i*4+2 );
    };
    bw1->SetLoopMotion( false );                                    (8)
    bw1->SetMotionID( 3 );

    bw1->NewDynamicMotion();
    bw1->SetColor( ALColor::White );
    bw1->AddMotionKey( cpr, 0 );                                    (9)
    bw1->SetMotionID( 1 );

    bw1->SetNormalMotionID( 1 );                                    (10)
    bw1->SetFocusMotionID( 2 );
    bw1->SetDecisionMotionID( 3 );

    ALText *tx1 = ALText::CreateRelease( ALFont::Create( "MainFont.aft" ) );
    tx1->SetColor( ALColor( 1.0f, 0.3f, 0.5f ) );
    tx1->AddDrawPrio( -1 ); 
    tx1->Show();
    tx1->SetY( 16 );
    tx1->SetWidth( 200 );
    tx1->SetAlign( ALTEXT_ALIGN_CENTER );
    bw1->AddReleaseNode( tx1 );

    bw1->Show();
    bw1->SetPos( 220, 100 );
    ALButtonWidget *bw2 = bw1->Clone();
    bw2->SetY( 164 );
    ALButtonWidget *bw3 = bw1->Clone();
    bw3->SetY( 228 );

    tx1->AddString( "最初から" );
    ALText *tx2 = ( ALText * )bw2->FindNodeByType( ALNODE_TYPE_TEXT );
    tx2->AddString( "続きから" );
    ALText *tx3 = ( ALText * )bw3->FindNodeByType( ALNODE_TYPE_TEXT );
    tx3->AddString( "終了" );

    bw1->SetTag( 1 );
    bw2->SetTag( 2 );
    bw3->SetTag( 3 );

    bw1->SetDownWidget( bw2 );
    bw2->SetUpWidget( bw1 );
    bw2->SetDownWidget( bw3 );
    bw3->SetUpWidget( bw2 );

    bw3->AddShortCutButton( ALPAD_B );

    bw3->SetDirectDecision();

    ALFuncWidgetCursor *wc = 
        ALFuncWidgetCursor::Create( CursorInitFunc, CursorChangeFunc );

    cw->SetCursor( wc );

    while( !ALSystem::IsTerminated() ){
        ALYield();
    };

    ALWidget::SetDefaultOwner( NULL );
}

(1)

モーション対象のプロパティを取得します。今回は色のみを使用しますが、このほか座標、テクスチャ等複数の要素を組み合わせる事も可能です。

(2)

動的モーションを開始します。なお、終了は特にありません。

(3)

現在の色をフレーム0にキーとして設定します。

(4)

キーに設定するのは現在の値なので、明滅用に少し暗くした色を登録します。

(5)

モーションはデフォルト状態では設定した値の切り替えになるので、ここで補間方法を設定します。

今回はSin波を選びます

(6)

このモーションにIDをつけます。

一つのみのモーションを使う場合はIDの必要はありませんが、今回は複数モーションを切り替えるために設定をします。

ちなみに複数のモーション切り替えは、ChangeMotionやLoadMotionを使用します。

(7)

二つ目のモーションを作る場合、BeginDynamicMotionではなく、NewDynamicMotionを実行します。

これで新しいモーションを作る準備が出来ます。

(8)

このモーションは決定時に使うので、ループさせずに一回再生しただけで終了するようにします。

なお、決定時のモーションにこのようなループしないモーションが設定されていると、モーション終了時に決定状態が自動解除されます。

(9)

これは通常時のモーションです。

通常時のモーションを用意しないと、選択時から元に戻ったときに設定した値が元に戻りません。

その為、0フレームのみに通常時の色を設定しておきます。

(10)

それぞれのステートに対し、使用するモーションIDをセットします。

第8章 終わりに

以上がボタンウィジェットとカーソルを使用したウィジェットの基本的なチュートリアルです。

今回のチュートリアルではほとんどプログラムコードで記述しましたが、 これらはほとんどデータで記述する事が可能です。

また、各種イベントをスクリプト記述する事も出来るため、 アプリケーション側では最終的な結果のみを受け取るようにする事が出来ます。

現状でもデータを作れば動作可能ですが、 それらを簡単につくるGUIツールを制作中です。

ウィジェットには今回紹介しなかったいろいろな機能がまだ存在し、 複雑なメニューを作る時に役に立ちます。 それらは、別のチュートリアルとして取り上げる予定です。