シーン:曲選択

ここでは曲選択画面の制御方法について説明しています。

曲選択画面には、曲選択処理とオプション変更、そして選択した曲のダウンロードの3つの処理があります。このうちダウンロードは通信プレイ時にのみ呼び出されますが、特にダウンロードゲージなどを出さないのであれば特に何も処理は必要ありません。また、通信エラーが発生した場合もシステム側でエラーを表示するので、特に何もする必要はありません。

この曲選択画面では、通常の曲選択の他にエキスパート用のコース選択にも使用されます。基本的に選択方法は共通ですが、選択した項目の曲名を表示するかコース名を表示するかはゲームモードに合わせて行います。なお、現在のゲームモードや選択した曲の情報などはhdx関数により取得できます。

曲選択画面に遷移直後はまだ選択が行えない状態となっています。これは直後のアニメーションが終わるまで選択をさせないような制御を行うことを想定しているためで、選択を許可するには任意の場所で選択を許可するhdx関数を呼び出します。これを呼び出さなければ曲の選択が一切行なえず、そこから先に遷移出来なくなるので、必ず選択許可を行うように指示しなければなりません。

曲選択画面ではゲームオプションが変更出来ます。ゲームオプションは通常STARTボタンを押すことで表示と非表示を制御しますが、これに対応するためオプションの開閉用にコールバックが用意されています。このコールバックを利用することで、必要な時に曲選択画面の上にオプション画面を表示することができます。なお、オプションの選択はシステム側で行うため、スクリプト側は現在設定されているオプションを表示するだけです。また、現在のオプションはhdx関数にて取得出来ます。

曲リスト選択処理はシステムにて管理されており、スクリプト側ではリストの表示のみを行います。リストは常に縦スクロールとなり、選択カーソルの位置は通常は真ん中をベースとします。また、選択中の曲は非選択中のリストより少し左側に表示されることを想定しています。

リストの選択処理を行うには、システムに曲リストの位置や1行のサイズなどを指示する必要があります。これは通常、曲選択開始時にhdx関数を使用して行います。システム側ではこれらの情報を使用して最終的なリストの位置やサイズを計算し、これを描画用のコールバックに渡すことで最終的にスクリプト側で描画を行います。なお、リストの表示は1個ずつ特定のコールバックが呼び出される仕様となっており、その際に左上の座標やクリアフラグなどがそのコールバックに渡されるので、これを参照してリストの色を変えたり、クリア状態を表示させるなどが出来ます。

曲選択開始時に呼び出されるhdx関数を曲選択やコース選択で別々に設定することが出来ます。これを利用して曲選択時は細めのリスト、コース選択時は太めのリストといった感じで分けることも出来ます。
外部スクリプトファイル
システムスキンのサンプルでは、曲選択画面の処理は_05_songselect.luaファイルにまとめられています。script.luaからこのファイルをinclude()関数を使って取り込むことで、最終的にscript.luaに定義されたものとして読み込まれます。
スクリプトによる制御方法
まずこの画面が開始される時にOnStartMusicSelect()が呼び出されるので、ここで曲選択で使用するリソースをロードしたり変数の初期化などを行います。また、システムに曲リストの位置やサイズを指示するためhdxSetListInfo()を呼び出します。

hdxSetListInfo()に渡す必要のあるパラメータは以下のとおりです。

※リストの左上を(0,0)とした差分となります

次に曲選択中は毎フレームOnRunMusicSelect()が呼ばれるので、ここで曲選択画面用の演算と画面描画などを行います。

曲選択処理を開始するには任意のタイミングでhdxEnableMusicSelect()を呼び出します。これを呼び出さないと選択処理が一切行われず、曲の決定も出来ないままとなります。なお、特に開始の演出などが無く画面に遷移した直後から選択可能とする場合は、OnStartMusicSelect()内で呼び出すことも出来ます。

次に背景など任意の画像を表示したあと、曲のリストを表示したいタイミングでhdxDrawMusicList()を呼び出します。この関数が呼び出されると、その中でリスト1つずつに対してOnDrawMusicList()が呼び出されます。このコールバックにはその曲が表示される左上座標やその曲のレベル、クリアフラグ、リストタイプなどが渡されるので、これを利用してリスト1つ分の描画を行います。なお、リストタイプは種類が決まっており、基本的にはリスト全体の色をこれで指定します。また、タイトル名はこのOnDrawMusicList()から返ったあとに描画されます。このため、OnDrawMusicList()は実際にはそのリストのフレームを描画するという意味になります。

リストタイプには以下のタイプがありますが、フレームの色はあくまでもデフォルトスキンのものであり、必ずこの色で描画する必要はありません。
FRAMETYPE_BACK 階層を1つ上がる際に使用されます。
FRAMETYPE_SUBDIR 階層の中に入る際に使用されます。
FRAMETYPE_MESSAGE 通信プレイ時に曲データが無かった場合などのメッセージに使用されます。
これは選択などは一切出来ず、タイトル名の部分がただのメッセージ表示となります。
FRAMETYPE_SONG 選択可能な曲に使用されます。
クリアランプなどはこのタイプでのみ対象となります。
FRAMETYPE_SONGERROR エラーのある曲で選択不可能な曲に使用されます。
※デフォルトスキンではさらにERRORと書かれています。
FRAMETYPE_COURSE 選択可能なコースに使用されます。

現在のステージ番号やプレイモード、ゲームモードなどの情報はhdxGetGameInfo()から取得出来ます。これを利用することで画面に現在の状態を表示させることが出来ます。また、モードによりデザインをまったく別のものに変更するといったことも可能です。


スクラッチを回すことで曲やコース名がスクロールしますが、この時次のリストがカーソル内に入るとOnChangeMusicList()が呼び出されます。ここで新たに選択された曲情報を画面に表示するため、タイトル名などの文字列テクスチャを再作成させることが出来ます。


エキスパート時などそのコースの中に複数の曲データが含まれる場合、これを画面に表示するためにはhdxGetCourseList()を呼び出してコース内の曲リストを取得します。なお曲名は文字列として取得されるので、通常はOnChangeMusicList()内で呼び出して先に曲名リストを作成しておき、OnRunMusicList()内でそれを表示するようにします。

曲を決定した際にダウンロードが必要な場合はOnStartDownload()が呼び出されます。通常はここでダウンロードゲージの表示を開始させます。次にダウンロード中に進行度が変化するごとにOnDownloadStatus()が呼び出されます。引数には現在の進行度が%値として入るので、これを利用してダウンロードゲージを進めたりなどで進行度を更新します。最後に曲のダウンロードが完了すると、OnEndDownload()が呼び出されます。引数にはエラーが発生していたかが入りますが、エラーの場合はシステムが現在のシーンの描画のあとにオーバーラップでエラー表示を行うため、スクリプト側でエラーを表示する必要はありません。また、OnEndDownload()が成功したとしてもまだ曲が開始されたわけではなく、あくまでもダウンロードの処理が終わったという告知のためで、ここで曲が開始されたとみなす必要はありません。

ダウンロードが正しく完了した場合や、ダウンロードが必要なくゲームが開始出来るようになるとOnEndMusicList()が呼び出されます。通常は曲選択によるゲーム開始アニメーション演出などはここで行い、アニメーションが終了したらhdxNextScene()を呼び出してシーンを終了します。


曲選択中にSTARTボタンを押すとオプション画面表示のためOnStartOption()が呼び出されます。ここでオプション画面を表示させるためのフラグなどを設定します。そしてSTARTボタンが離されるとOnEndOption()が呼び出されます。ここでオプション画面を消すためにフラグなどをクリアします。オプション画面は通常STARTを押している間だけ表示されますので、STARTボタンを連打するとOnStartOption()OnEndOption()が交互に連続で呼び出されます。このためオプション画面をスクロールなどで開閉するような演出を行っている場合は、移動している間に次の操作が行われても問題無いように制御する必要があります。

オプションが何か変更されるごとにOnChangeOption()が呼び出されます。これを利用して変更されたオプション部分に演出をかけることが出来ますが、変更されたあとのオプションの実際の値はhdxGetGameInfo()を参照する必要があります。

このオプション画面の他、ゲームプレイ時にスクロールスピードの変更を行う必要がある場合はOnChangeScrollSpeed()が呼び出されます。そしてこの関数の戻り値には、次に設定されるスクロールスピード値を計算して返す必要があります。これにより、常に一定量のスクロールスピードにしたり、フローティングスピードとして任意の値を指定することが出来るようになっています。

ゲーム画面と違い、この選曲画面内からスクロールスピードを変更する場合、OnChangeScrollSpeed()の引数には常にボタンによるUPのみが渡されます。つまり、この画面上では常に上昇しか出来ないため、ある一定以上のスクロール速度になった場合は最低速度にループして戻すような処理が必要となります。この場合、選曲画面なのかゲーム中なのかは引数に渡されるので、スクリプト上ではこれを判断して上限チェックをするようにしてください。


このコールバックから返したスクロールスピードがある範囲を超える場合は、システム側で自動的に補正されます。このため、実際のスクロールスピードは戻り値のものとは異なる可能性があるため、画面にスクロール速度を表示する必要がある場合は、必ずhdxGetGameInfo()から最終版を取得するようにしてください。
オプションの種類について
オプションはシステム側で管理しているため、スクリプト側で行うことは現在の状態を単に表示するだけです。
シャッフルモード(1P/2P別) ノーマル 譜面そのまま
RANDOM スクラッチ以外のレーンの順番をランダムに変更
RANDOM+ スクラッチを含めてレーンの順番をランダムに変更
S-RANDOM スクラッチ以外のレーン内のオブジェをランダムに配置
S-RANDOM+ スクラッチを含めてレーン内のオブジェをランダムに配置
H-RANDOM スクラッチ以外のレーン内のオブジェをなるべく前後に連続しないようにランダムに配置
H-RANDOM+ スクラッチを含めてレーン内のオブジェをなるべく前後に連続しないようにランダムに配置
MIRROR スクラッチ以外のレーンの順番を逆に変更
MIRROR+ スクラッチを含めてレーンの順番を逆に変更
R-RANDOM 通常レーンの順番を1レーンずつずらす
R-MIRROR スクラッチ以外のレーンの順番を逆に変更し、さらに1レーンずつずらす
難しさ ノーマル 通常のゲージ処理
EASY 通常のゲージより減りが少なく上昇が多い
HARD 赤いゲージで100%から始まり、0%になるとその場でゲームオーバー
レーンカバー 無し レーンカバーを使用しない
SUDDEN 上側カバー
HIDDEN 下側カバー
HIDSUD 中心クリップ
LIFT 上方オフセット
LIFTSUD 上方オフセット+SUD
アシスト NORMAL アシストなし
AUTO_SCRATCH オートスクラッチ(皿が緑色になる)
AUTO_PLAY オートプレイ(スコアは保存されない)
ダブル専用オプション NO 無し
FLIP 1Pと2Pを入れ替える
DOUBLEBATTLE シングル譜面を両方に適用
※現在のオプション値はhdxGetGameInfo()で取得出来ます
オプションの変更に使用されるボタンはシステム側で決められています。シャッフルオプションは左右別々、ダブル専用オプションは常に2P側のボタンに割り当てられていますが、それ以外は左右とも同じキーを使用します。

オプションのデザインを行う場合は以下の画像を参考にしてください。

※シングルプレイで1P側の場合は左側のみ、2P側の場合は右側のみを表示するといった制御はスクリプト側で行います
サンプルコード
このサンプルでは以下の処理を行っています。

曲選択の基本処理

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 曲選択
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
g_bMusicSelected            = FALSE;    // 曲が選択されたか
g_iMusicInfoDrawType        = 0;        // 曲情報の表示状態(0=非表示、1=曲、2=コース)
g_iMusicInfoLevel           = 0;        // 表示中の曲リストのレベル
g_iMusicInfoBpm             = 0;        // 表示中の曲リストの初期テンポ
g_iMusicInfoPoint           = -1;       // 表示中の曲が有料曲かの判定用のポイント値
g_iMusicInfoDrawCount       = 0;        // 曲名の表示カウンタ(15~0/60fps)
g_iMusicInfoCourseID        = -1;       // 表示中のリストがコースならそのリストID
g_fMusicInfoLampAlpha       = 0;        // クリアランプ点滅用アルファ値
g_iMusicInfoFullComboCount  = 0;        // フルコンボ時の点滅画像切り替えカウンタ
g_bMusicInfoOptionEnable    = FALSE;    // オプション画面表示中か
g_iMusicInfoOptionCount     = -1;       // オプション画面のオープンカウンタ(フレーム番号/-1=非表示)

///////////////////////////////////////////////////////////////////////
// 選曲画面が始まる際に呼び出される。
//   exp : エキスパートモードの場合はtrueが入る
///////////////////////////////////////////////////////////////////////
function OnStartMusicSelect( exp )
    
    // 初期化
    g_bMusicSelected            = FALSE;
    g_iMusicInfoDrawType        = 0;
    g_iMusicInfoLevel           = 0;
    g_iMusicInfoBpm             = 0;
    g_iMusicInfoPoint           = -1;
    g_iMusicInfoDrawCount       = 0;
    g_iMusicInfoCourseID        = -1;
    g_fMusicInfoLampAlpha       = 0;
    g_iMusicInfoFullComboCount  = 0;
    g_bMusicInfoOptionEnable    = FALSE;
    g_iMusicInfoOptionCount     = -1;

    // リストのデザインをセット
    hdxSetListInfo( 780,746,78,0,38,"メイリオ",36,0xFFFFFFFF,0xFFFFA5FF,2,0xFF000000 );
//    hdxSetListInfo( 780,746,78,0,38,"メイリオ",36,0x12345678,0x80123456,2,0xFFFFFFFF );

    // 3Dモデルロード
    hdxMatrixRotationX( 0,hdxToRadian(90) );            // 3dsMax座標補正
    hdxLoadModel( 0,"title.x",0 );

    // アニメーションロード
    hdxLoadAnime( 0,"frame.hda",0 );                    // 共通フレーム
    if( exp==TRUE ) then
        hdxCtrlAnime( 0,CTRLMODE_MANUAL,135,110,-1 );   // COURSE SELECT
    else
        hdxCtrlAnime( 0,CTRLMODE_MANUAL,130,110,-1 );   // MUSIC SELECT
    end

    hdxLoadAnime( 1,"lighting.hda",0 );                 // 背景ライティング
    hdxCtrlAnime( 1,CTRLMODE_AUTO_LOOP,0,0,-1 );        // 

    hdxLoadAnime( 2,"star.hda",0 );                     // 星アニメ
    hdxCtrlAnime( 2,CTRLMODE_AUTO_LOOP,0,0,-1 );        // 

    hdxLoadAnime( 3,"pl_light1.hda",0 );                // 1P側プレイヤーライト
    hdxCtrlAnime( 3,CTRLMODE_MANUAL,-1,0,-1 );          // 

    hdxLoadAnime( 4,"pl_light2.hda",0 );                // 2P側プレイヤーライト
    hdxCtrlAnime( 4,CTRLMODE_MANUAL,-1,0,-1 );          // 

    hdxLoadAnime( 5,"lst_difficulty.hda",0 );           // 曲情報用レベルゲージ
    hdxCtrlAnime( 5,CTRLMODE_MANUAL,0,0,-1 );           // デフォルトでは0フレーム目で停止

    hdxLoadAnime( 6,"lst_difficulty2.hda",0 );          // コース内の各楽曲用レベルゲージ
    hdxCtrlAnime( 6,CTRLMODE_MANUAL,-1,0,-1 );          // 

    hdxLoadAnime( 7,"lst_course.hda",0 );               // コース演出
    hdxCtrlAnime( 7,CTRLMODE_MANUAL,-1,0,-1 );          // 

    hdxLoadAnime( 8,"lst_start.hda",10 );               // ゲーム開始(SE込みのためかち合わないIDを指定)
    hdxCtrlAnime( 8,CTRLMODE_MANUAL,-1,0,-1 );          // 
    
    hdxLoadAnime( 9,"lst_download.hda",10 );            // DLゲージ
    hdxCtrlAnime( 9,CTRLMODE_MANUAL,-1,0,-1 );          // 

    hdxLoadAnime( 10,"lst_indicator.hda",0 );           // DL中インジケーター
    hdxCtrlAnime( 10,CTRLMODE_AUTO_LOOP,35,35,74 );     // 常にループ再生する(表示のON/OFFを制御するだけ)

    hdxLoadAnime( 11,"lst_option.hda",0 );              // オプションフレーム
    hdxCtrlAnime( 11,CTRLMODE_MANUAL,-1,0,-1 );         // 


    // 画像のロード
    hdxLoadImage( 0,"lst_image.tga" );
    hdxSetPutRange(  0,0,736,360,70,22,0,0      );                                              // BPM
    hdxSetPutRange(  1,0,808,344,78,50,0,0      );                                              // CLEAR_ASSIST
    hdxSetPutRange(  2,0,888,344,78,50,0,0      );                                              // CLEAR_EASY
    hdxSetPutRange(  3,0,648,304,78,50,0,0      );                                              // CLEAR_NORMAL
    hdxSetPutRange(  4,0,728,304,78,50,0,0      );                                              // CLEAR_HARD
    hdxSetPutRange(  5,0,0,216,565,72,0,0       );                                              // CURSOR
    hdxSetPutRange(  6,0,0,448,736,22,0,0       );                                              // DJ_POINT
    hdxSetPutRange(  7,0,632,144,130,100,0,0    );                                              // FULLCOMBO_LIGHT0
    hdxSetPutRange(  8,0,768,144,130,100,0,0    );                                              // FULLCOMBO_LIGHT1
    hdxSetPutRange(  9,0,632,40,130,100,0,0     );                                              // FULLCOMBO_LIGHT2
    hdxSetPutRange( 10,0,768,40,130,100,0,0     );                                              // FULLCOMBO_LIGHT3
    hdxSetPutRange( 11,0,904,160,78,50,0,0      );                                              // GAMEOVER_ASSIST
    hdxSetPutRange( 12,0,808,288,78,50,0,0      );                                              // GAMEOVER_EASY
    hdxSetPutRange( 13,0,888,288,78,50,0,0      );                                              // GAMEOVER_NORMAL
    hdxSetPutRange( 14,0,568,304,78,50,0,0      );                                              // GAMEOVER_HARD
    hdxSetPutRange( 15,0,736,424,34,26,0,0      );                                              // LSTLVERROR
    hdxSetPutRange( 16,0,776,424,34,26,0,0      );                                              // LSTLVNUM_1
    hdxSetPutRange( 17,0,984,160,34,26,0,0      );                                              // LSTLVNUM_2
    hdxSetPutRange( 18,0,568,216,34,26,0,0      );                                              // LSTLVNUM_3
    hdxSetPutRange( 19,0,960,240,34,26,0,0      );                                              // LSTLVNUM_4
    hdxSetPutRange( 20,0,968,272,34,26,0,0      );                                              // LSTLVNUM_5
    hdxSetPutRange( 21,0,968,304,34,26,0,0      );                                              // LSTLVNUM_6
    hdxSetPutRange( 22,0,968,336,34,26,0,0      );                                              // LSTLVNUM_7
    hdxSetPutRange( 23,0,968,368,34,26,0,0      );                                              // LSTLVNUM_8
    hdxSetPutRange( 24,0,960,400,34,26,0,0      );                                              // LSTLVNUM_9
    hdxSetPutRange( 25,0,568,408,34,26,0,0      );                                              // LSTLVNUM_10
    hdxSetPutRange( 26,0,608,408,34,26,0,0      );                                              // LSTLVNUM_11
    hdxSetPutRange( 27,0,648,408,34,26,0,0      );                                              // LSTLVNUM_12
    hdxSetPutRange( 28,0,688,408,34,26,0,0      );                                              // LSTLVPARENT
    hdxSetPutRange( 29,0,864,416,34,26,0,0      );                                              // LSTLVQUEST
    hdxSetPutRange( 30,0,904,416,34,26,0,0      );                                              // LSTLVSUBDIR
    hdxSetPutRange( 31,0,568,280,240,22,0,0     );                                              // NUM_BPM
    hdxSetPutRange( 32,0,568,360,168,22,0,0     );                                              // NUM_BUY
    hdxSetPutRange( 33,0,568,384,168,18,0,0     );                                              // NUM_POINT
    hdxSetPutRange( 34,0,864,400,96,12,0,0      );                                              // NUM_SONGINFO
    hdxSetPutRange( 35,0,632,0,336,40,0,0       );                                              // NUM_STAGE
    hdxSetPutRange( 36,0,568,248,240,26,0,0     );                                              // NUM_TOTALLEVEL
    hdxSetPutRange( 37,0,736,400,126,22,0,0     );                                              // POINT_END
    hdxSetPutRange( 38,0,1008,40,16,16,0,0      );                                              // POINT_ICON
    hdxSetPutRange( 39,0,904,216,92,22,0,0      );                                              // POINT_USE
    hdxSetPutRange( 40,0,0,408,564,38,0,0       );          hdxSetPutStatus( 40,0.5,1,1,0 );    // SONGFRAME_1
    hdxSetPutRange( 41,0,0,288,564,38,0,0       );          hdxSetPutStatus( 41,0.5,1,1,0 );    // SONGFRAME_2
    hdxSetPutRange( 42,0,0,328,564,38,0,0       );          hdxSetPutStatus( 42,0.5,1,1,0 );    // SONGFRAME_3
    hdxSetPutRange( 43,0,0,368,564,38,0,0       );          hdxSetPutStatus( 43,0.5,1,1,0 );    // SONGFRAME_4
    hdxSetPutRange( 44,0,0,0,632,209,0,0        );                                              // SONGINFO
    hdxSetPutRange( 45,0,608,216,20,12,0,0      );                                              // SONGINFO_NO
    hdxSetPutRange( 46,0,1008,56,8,12,0,0       );                                              // SONGINFO_QUEST
    hdxSetPutRange( 47,0,808,248,150,40,0,0     );                                              // STAGE
    hdxSetPutRange( 48,0,904,40,100,40,0,0      );                                              // STAGE_1ST
    hdxSetPutRange( 49,0,904,80,100,40,0,0      );                                              // STAGE_2ND
    hdxSetPutRange( 50,0,904,120,100,40,0,0     );                                              // STAGE_3RD
    hdxSetPutRange( 51,0,968,0,54,40,0,0        );                                              // STAGE_TH
    hdxSetPutRange( 52,0,1000,192,20,26,0,0     );                                              // 

    hdxLoadImage( 1,"lst_option.tga" );                                                         // オプション用画像
    hdxSetPutRange( 100,1,0,0,554,502,0,0       );                                              // frame_1p
    hdxSetPutRange( 101,1,0,504,554,502,0,0     );                                              // frame_2p
    hdxSetPutRange( 102,1,560,0,132,40,10,10    );                                              // select(オフセットをずらす)
    hdxSetPutRange( 103,1,696,0,110,18,0,0      );                                              // lock
    hdxSetPutRange( 104,1,808,0,96,16,0,0       );                                              // num
    hdxSetPutRange( 105,1,904,0,8,8,0,0         );                                              // diff_plus
    

    // サウンドロード
    hdxLoadSound( 0,"lst_bgm.wav" );
    hdxLoadSound( 1,"lst_cursor.wav" );
    hdxLoadSound( 2,"lst_option.wav" );
    hdxLoadSound( 3,"lst_select.wav" );

    // プレイヤーライトアニメのセット
    local game_info = hdxGetGameInfo();
    if( game_info["pmode"]==0 ) then
        // シングル左側なら
        hdxCtrlAnime( 3,CTRLMODE_AUTO_LOOP,0,0,-1 );
    elseif( game_info["pmode"]==1 ) then
        // シングル右側なら
        hdxCtrlAnime( 4,CTRLMODE_AUTO_LOOP,0,0,-1 );
    else
        // ダブルなら
        hdxCtrlAnime( 3,CTRLMODE_AUTO_LOOP,0,0,-1 );
        hdxCtrlAnime( 4,CTRLMODE_AUTO_LOOP,0,0,-1 );
    end

    // ログイン中ならニックネームフォントを生成
    if( game_info["login"]==TRUE ) then
        user_info = hdxGetUserInfo();
        trace( "ログイン中 : "..user_info["nickname"] );
        hdxCreateFont( 5,"メイリオ",32,user_info["nickname"],0xFFFFFF,0xFFFFFF,1,0x5027A3 );
    end

    // BGM再生
    hdxPlaySound( 0,TRUE );

    // 曲選択を許可
    hdxEnableMusicSelect();
end

///////////////////////////////////////////////////////////////////////
// 曲選択画面で毎フレーム呼び出されるので、ここで演算と描画処理を行う。
///////////////////////////////////////////////////////////////////////
function OnRunMusicSelect( upd_frm )

    // 情報取得
    local game_info = hdxGetGameInfo();
    local user_info = hdxGetUserInfo();

    ///////////////////////////////////
    // 処理
    ///////////////////////////////////
    if( upd_frm==TRUE ) then
        // 60fpsでカウンタを加算
        if( g_iMusicInfoDrawType==1 || g_iMusicInfoDrawType==2 ) then
            // 曲かコースの場合のみ
            g_iMusicInfoDrawCount = g_iMusicInfoDrawCount - 1;
            if( g_iMusicInfoDrawCount<0 ) then
                g_iMusicInfoDrawCount = 0;
            end
        end

        // オプションフレームの制御
        if( g_bMusicInfoOptionEnable==TRUE ) then
            // 開いている状態ならフレームを進める
            g_iMusicInfoOptionCount = g_iMusicInfoOptionCount + 1;
            if( g_iMusicInfoOptionCount>20 ) then
                g_iMusicInfoOptionCount = 20;
            end
        else
            // 閉じている状態ならフレームを戻す
            g_iMusicInfoOptionCount = g_iMusicInfoOptionCount - 1;
            if( g_iMusicInfoOptionCount<-1 ) then
                g_iMusicInfoOptionCount = -1;
            end
        end
        // フレームをセット
        hdxCtrlAnime( 11, CTRLMODE_MANUAL, g_iMusicInfoOptionCount, 0, -1 );
    end

    // 曲選択後のアニメーションが終了しているか
    if( g_bMusicSelected==TRUE ) then
        if( hdxIsEndAnime(8)==TRUE ) then
            // 曲選択終了
            hdxNextScene();
            return;
        end
    end

    // クリアランプ点滅用アルファ値の計算
    g_fMusicInfoLampAlpha       = math.sin( hdxGetTime() % 90 * 4 * 3.14159/180 ) / 3 + 0.5;

    // フルコンボ点滅用カウンタ処理(0~3のループ)
    g_iMusicInfoFullComboCount = (g_iMusicInfoFullComboCount + 1) % 4;


    ///////////////////////////////////
    // 描画
    ///////////////////////////////////

    // 3Dモデル
    hdxSetZBuffer( TRUE );
    hdxMatrixRotationY( 0, hdxToRadian( ((hdxGetTime()%(360*60))/60) ) );
    hdxSetLookAt( 0,0,40, 0,0,0, 0,1,0 );
    hdxSetViewport( 0,0,RENDER_WIDTH,RENDER_HEIGHT );
    hdxDrawModel( 0,0 );                                    // 背景オブジェクト
    hdxSetZBuffer( FALSE );

    hdxDrawAnime( 1, 0, 0, 1.0, 1.0 );                      // ライティング

    hdxDrawMusicList();                                     // 曲リスト描画

    // カーソル
    hdxSetBlendMode( BLEND_ADD );
    hdxSetPutStatus( 5, math.sin((hdxGetTime()%360)/2*3.145926/180)/8+0.25, 1, 1, 0 );
    hdxPut( 5, 726, 323 );
    hdxSetBlendMode( BLEND_NORMAL );

    hdxDrawAnime( 0, 0, 0, 1.0, 1.0 );                      // フレーム
    hdxDrawAnime( 2, 1150, 67, 1.0, 1.0 );                  // 星

    // プレイヤーライト(選択画面以降でアニメーション表示)
    hdxDrawAnime( 3, 224, 651, 1.0, 1.0 );                  // 1P側
    hdxDrawAnime( 4, 1056, 651, 1.0, 1.0 );                 // 2P側

    // ゲームモード別に常に表示するもの
    if( game_info["gmode"]==GAMEMODE_BEGINNER || game_info["gmode"]==GAMEMODE_STANDARD || game_info["gmode"]==GAMEMODE_FREE ) then
        // レベルゲージ
        hdxDrawAnime( 5, 96, 440, 1.0, 1.0 );
    elseif( game_info["gmode"]==GAMEMODE_EXPERT ) then
        // レベルゲージ
        hdxDrawAnime( 5, 50, 150, 1.0, 1.0 );
    end

    // ログイン中ならニックネームとポイントを表示
    if( game_info["login"]==TRUE ) then
        hdxPut( 6,17,534 );                                                             // フレーム
        hdxDrawFont( 5,50,524,1.0,0 );                                                  // ニックネーム
        hdxPutInt( 33, 752, 552, user_info["point"], 1.0, ALIGNTYPE_RIGHTBOTTOM );      // ポイント
    end

    // DLゲージ
    if( game_info["gmode"]==GAMEMODE_BEGINNER || game_info["gmode"]==GAMEMODE_STANDARD ) then
        // BEGINNERかSTANDARDモードの場合
        hdxDrawAnime( 9, 410, 473, 1.0, 1.0 );
    elseif( game_info["gmode"]==GAMEMODE_EXPERT ) then
        // EXPERTモードの場合
        hdxDrawAnime( 9, 364, 186, 1.0, 1.0 );
    end

    // 対象別表示
    if( g_iMusicInfoDrawType==1 ) then
        // 曲なら

        // タイトル名
        size    = hdxGetFontSize( 0 );                                                      // タイトル名の文字列テクスチャのサイズを取得
        move_x  = -(1 - math.cos( g_iMusicInfoDrawCount*(90/15)*3.1415926/180 )) * 200;     // 登場時の移動量(-200→0)
        alpha   = (15 - g_iMusicInfoDrawCount) / 15;                                        // アルファ値(0.0→1.0)
        if( size["width"]<548 ) then
            // 幅が規定値より小さい場合は中心に等倍で表示
            local x = 420 - size["width"] / 2;                                              // 原点を中心とした左上座標を算出
            local y = 294 - size["height"] / 2;
            hdxDrawFont( 0,x+move_x,y,alpha,ALIGNTYPE_LEFTTOP );
        else
            // 文字サイズが大きい場合
            local x = 420 - 548 / 2;                                                        // 原点を中心とした左上座標を算出(最大幅に収める)
            local y = 294 - size["height"] / 2;
            hdxSetFilterMode( FILTER_LINEAR );                                              // バイリニア
            hdxDrawFontEx( 0,x+move_x,y,548,size["height"],alpha );
            hdxSetFilterMode( FILTER_POINT );
        end

        // ジャンル
        hdxDrawFont( 1,420,250,alpha,ALIGNTYPE_CENTER );

        // アーティスト
        hdxDrawFont( 2,420,340,alpha,ALIGNTYPE_CENTER );

        // BPM
        hdxPut( 0, 514, 366 );
        hdxPutInt( 31, 654, 388, g_iMusicInfoBpm, 1.0, ALIGNTYPE_RIGHTBOTTOM );

        // ポイント使用曲か
        if( g_iMusicInfoPoint>=0 ) then
            // 0以上ならポイント曲
            if( g_iMusicInfoPoint==0 ) then
                // 0なら購入済み
                hdxPut( 37, 552, 415 );
            else
                // それ以上ならその曲のポイント数を表示
                hdxPut( 39, 552, 415 );
                hdxPutInt( 32, 653, 414, g_iMusicInfoPoint, 1.0, ALIGNTYPE_LEFTTOP );
            end
        end

        // 曲情報
        hdxDrawFont( 3, 100, 412, alpha, ALIGNTYPE_LEFTTOP );

    elseif( g_iMusicInfoDrawType==2 ) then
        // コースなら
        local course    = hdxGetCourseList( g_iMusicInfoCourseID );                                 // コース情報の取得

        local size  = hdxGetFontSize( 0 );                                                      // タイトル名の文字列テクスチャのサイズを取得
        local move_x    = -(1 - math.cos( g_iMusicInfoDrawCount*(90/15)*3.1415926/180 )) * 160;     // 登場時の移動量(-160→0)
        local alpha = (15 - g_iMusicInfoDrawCount) / 15;                                        // アルファ値(0.0→1.0)
        if( size["width"]<630 ) then
            // 幅が規定値より小さい場合は中心に等倍で表示
            local x = 50;                                           // 左ぞろえなので座標は固定値
            local y = 250 - size["height"] / 2;
            hdxDrawFont( 0,x+move_x,y,alpha,ALIGNTYPE_LEFTTOP );
        else
            // 文字サイズが大きい場合
            local x = 50;                                           // 左ぞろえなので座標は固定値
            local y = 250 - size["height"] / 2;
            hdxSetFilterMode( FILTER_LINEAR );              // バイリニア
            hdxDrawFontEx( 0,x+move_x,y,630,size["height"],alpha );
            hdxSetFilterMode( FILTER_POINT );/**/
        end

        // 選択時のアニメ演出
        hdxSetViewport( 48,198,630,80 );
        hdxDrawAnime( 7, 48, 204, 1.0, 1.0 );
        hdxSetViewport( 0,0,0,0 );

        // コース内曲リスト(フォントIDは10~)
        hdxPut( 44,48,314 );                                // フレーム

        // トータルレベル(1曲でも範囲外のレベルがあれば-1)
        local total_level = 0;

        // 描画はコースリスト内のみに制限(曲数が11以上存在した場合の対処用)
        hdxSetViewport( 56,358,615,161 );
        for i=0,course["count"]-1 do
            // 番号
            hdxPut( 45, 69, 366+i*15 );                             // No.
            hdxPutInt( 34, 90, 366+i*15, i+1, 1.0, ALIGNTYPE_LEFTTOP );     // 数値(1~)

            // タイトル名
            hdxDrawFont( i+10, 120, 362+i*15, 1, ALIGNTYPE_LEFTTOP );

            // レベル
            local lv = course["level"..i];
            if( lv>=1 && lv<=12 ) then
                // 範囲内なら
                hdxCtrlAnime( 6, CTRLMODE_MANUAL, lv, lv, lv );         // レベルに合ったランプのフレームへ
                hdxDrawAnime( 6, 566, 367+i*15, 1.0, 1.0 ); 
                // レベル値
                hdxPutInt( 34, 650, 377+i*15, lv, 1.0, ALIGNTYPE_RIGHTBOTTOM );
                // トータルレベルを算出(まだレベルが範囲外ではない場合のみ)
                if( total_level>=0 ) then
                    total_level = total_level + lv;
                end
            else
                // 範囲外なら
                hdxCtrlAnime( 6, CTRLMODE_MANUAL, lv, lv, lv );         // レベルに合ったランプのフレームへ
                hdxDrawAnime( 6, 566, 367+i*15, 1.0, 1.0 ); 
                // 「?」
                hdxPut( 46,642,365+i*15 );
                // 1つでも範囲外なら
                total_level = -1;
            end
        end
        // ビューポートを全体に戻す
        hdxSetViewport( 0,0,0,0 );

        // トータルレベルの表示
        if( total_level>=0 ) then
            // -1でなければトータル値の表示が可能
            if( total_level>999 ) then
                total_level = 999;                                  // 念のため最大値補正
            end
            hdxPutInt( 36, 630, 332, total_level, 1.0, ALIGNTYPE_CENTER );
        else
            // -1なら「?」を表示
            hdxPut( 52,630-10,332-13 );
        end

        // コース内曲数
        hdxPutInt( 34, 542, 337, course["count"], 1.0, ALIGNTYPE_RIGHTBOTTOM );
    end

    // ステージ番号
    if( game_info["gmode"]==GAMEMODE_BEGINNER ||
        game_info["gmode"]==GAMEMODE_STANDARD ||
        game_info["gmode"]==GAMEMODE_FREE ) then

        // STAGE
        hdxPut( 47,146,162 );

        // 数値
        if( game_info["stage"]==1 ) then
            // 1st
            hdxPut( 48,32,162 );
        elseif( game_info["stage"]==2 ) then
            // 2nd
            hdxPut( 49,32,162 );
        elseif( game_info["stage"]==3 ) then
            // 3rd
            hdxPut( 50,32,162 );
        else
            // 数+th
            hdxPutInt( 35, 76, 202, game_info["stage"], 1.0, ALIGNTYPE_RIGHTBOTTOM );
            hdxPut( 51, 76, 162 );
        end
    end

    // オプションフレーム(コールバック指定あり)
    if( g_iMusicInfoOptionCount>=0 ) then
        hdxDrawAnime2( 11, 0, 0, 1.0, 1.0, "_lstDrawOption" );
    end

    // 曲決定後のアニメ表示
    hdxDrawAnime( 8, 0, 0, 1.0, 1.0 );

    // DLインジケーター
    if( game_info["download"]==TRUE )then
        hdxDrawAnime( 10, 640, 360 ,1.0, 1.0 );
    end
end

オプションアニメーションのコールバック

オプションの登場と退場はアニメーションファイルにて作成されており、フレーム番号を指定することで移動するようになっています。この位置をコールバックで受け取り、そのコールバック内で実際のオプション画面を構成しています。

アニメーションファイルは位置の情報だけが必要なので、中の画像はダミーが入っています。
左上位置合わせ用のロケーター
// オプションフレーム表示コールバック
function _lstDrawOption( layer,x,y,frm_x,frm_y,frm_sx,frm_sy,frm_alpha,frm_rot )

    local info = hdxGetGameInfo();

    // フレームの左上ベース座標
    local _x = x + frm_x;
    local _y = y + frm_y;

    if( layer==0 ) then
        // 1P側
        if( info["pmode"]==PLAYMODE_SINGLE_1P || info["pmode"]==PLAYMODE_DOUBLE ) then
            // シングル1P側かダブルなら表示する
            hdxPut( 100, _x, _y );                      // 1P側フレーム
            _lstDrawOptionSub( _x, _y, info["opt_shuffle_1P"], info["opt_difficult"], info["opt_cover"], info["opt_assist"], OPTDOUBLE_NO, info["opt_speed"] ); // 1P側にダブル専用オプションの表示エリアは無い
        end
        return TRUE;                                    // 自前表示完了
    elseif( layer==1 ) then
        // 2P側
        if( info["pmode"]==PLAYMODE_SINGLE_2P || info["pmode"]==PLAYMODE_DOUBLE ) then
            // シングル2P側かダブルなら表示する
            hdxPut( 101, x+frm_x, y+frm_y );            // 2P側フレーム

            if( info["pmode"]!=PLAYMODE_DOUBLE ) then
                // シングルでは使用出来ないオプションをマスク
                hdxPut( 103, _x+105, _y+87 );           // FLIP
                hdxPut( 103, _x+105, _y+111 );          // DOUBLE BATTLE
            end

            _lstDrawOptionSub( _x, _y, info["opt_shuffle_2P"], info["opt_difficult"], info["opt_cover"], info["opt_assist"], info["opt_double"], info["opt_speed"] );
        end
        return TRUE;                                    // 自前表示完了
    end

    return FALSE;
end

////////////////////////////////////////////
// オプション用定数(Luaは配列は1からのため気をつけること)
g_mOptShuffle = {
    // ■シャッフル表示オフセット
    // X  Y  plus? plusX plusY
    { 48,324,FALSE,  0,    0 },             // OPTSHUFFLE_RANDOM
    { 48,324,TRUE, 129,  329 },             // OPTSHUFFLE_RANDOM_PLUS
    { 48,348,FALSE,  0,    0 },             // OPTSHUFFLE_S_RANDOM
    { 48,348,TRUE, 137,  353 },             // OPTSHUFFLE_S_RANDOM_PLUS
    { 48,372,FALSE,  0,    0 },             // OPTSHUFFLE_H_RANDOM
    { 48,372,TRUE, 137,  377 },             // OPTSHUFFLE_H_RANDOM_PLUS
    { 48,396,FALSE,  0,    0 },             // OPTSHUFFLE_MIRROR
    { 48,396,TRUE, 128,  401 },             // OPTSHUFFLE_MIRROR_PLUS
    { 48,420,FALSE,  0,    0 },             // OPTSHUFFLE_R_RANDOM
    { 48,444,FALSE,  0,    0 },             // OPTSHUFFLE_R_MIRROR
};
g_mOptDifficult = {
    // ■難しさ表示位置オフセット
    { 164,324 },                            // OPTDIFFICULT_EASY
    { 164,348 },                            // OPTDIFFICULT_HARD
};
g_mOptCover = {
    // ■レーンカバー表示位置オフセット
    { 280,324 },                            // OPTCOVER_SUDDEN
    { 280,348 },                            // OPTCOVER_HIDDEN
    { 280,372 },                            // OPTCOVER_HIDSUD
    { 280,396 },                            // OPTCOVER_LIFT
    { 280,420 },                            // OPTCOVER_LIFTSUD
};
g_mOptAssist = {
    // ■アシスト表示位置オフセット
    { 337,87 },                             // OPTASSIST_AUTO_SCRATCH
    { 337,111 },                            // OPTASSIST_AUTO_PLAY
};
g_mOptDouble = {
    // ■ダブル専用オプション表示オフセット
    { 105,87 },                             // OPTDOUBLE_FLIP
    { 105,111 },                            // OPTDOUBLE_DOUBLEBATTLE
};

// フレームの左上を原点として、現在のオプション状態を表示する
function _lstDrawOptionSub( x,y,shuffle,diff,cover,assist,dbl,spd )

    // カーソル点滅
    local alpha = math.sin( (hdxGetTime()%180) * 2 * 3.14159 / 180 ) / 16 + 0.25;
    hdxSetPutStatus( 102,alpha,1,1,0 );

    // シャッフルモード
    if( shuffle!=OPTSHUFFLE_OFF ) then
        // 先に+を表示
        if( g_mOptShuffle[shuffle][3]==TRUE ) then
            // +対応のオプションの場合
            hdxPut( 105, g_mOptShuffle[shuffle][4]+x, g_mOptShuffle[shuffle][5]+y );
        end
        // 選択カーソル
        hdxSetBlendMode( BLEND_ADD );
        hdxPut( 102, g_mOptShuffle[shuffle][1]+x, g_mOptShuffle[shuffle][2]+y );
        hdxSetBlendMode( BLEND_NORMAL );
    end

    // 難しさ
    if( diff!=OPTDIFFICULT_NORMAL ) then
        // 選択カーソル
        hdxSetBlendMode( BLEND_ADD );
        hdxPut( 102, g_mOptDifficult[diff][1]+x, g_mOptDifficult[diff][2]+y );
        hdxSetBlendMode( BLEND_NORMAL );
    end

    // レーンカバー
    if( cover!=OPTCOVER_NO ) then
        // 選択カーソル
        hdxSetBlendMode( BLEND_ADD );
        hdxPut( 102, g_mOptCover[cover][1]+x, g_mOptCover[cover][2]+y );
        hdxSetBlendMode( BLEND_NORMAL );
    end

    // アシスト
    if( assist!=OPTASSIST_NORMAL ) then
        // 選択カーソル
        hdxSetBlendMode( BLEND_ADD );
        hdxPut( 102, g_mOptAssist[assist][1]+x, g_mOptAssist[assist][2]+y );
        hdxSetBlendMode( BLEND_NORMAL );
    end

    // ダブル専用オプション
    if( dbl!=OPTDOUBLE_NO ) then
        // 選択カーソル
        hdxSetBlendMode( BLEND_ADD );
        hdxPut( 102, g_mOptDouble[dbl][1]+x, g_mOptDouble[dbl][2]+y );
        hdxSetBlendMode( BLEND_NORMAL );
    end

    // スクロールスピード
    hdxPutFloat( 104, x+500, y+341, spd, 1.0, ALIGNTYPE_RIGHTBOTTOM, "%.2f" );

end

スクロールによるリスト更新処理

///////////////////////////////////////////////////////////////////////
// リストが選択されるごとに呼び出される。
// ここで選択された楽曲の情報の表示開始などを行う。
//  frm_type     : フレームタイプ
//                    FRAMETYPE_BACK=戻る
//                    FRAMETYPE_SUBDIR=階層に入る
//                    FRAMETYPE_MESSAGE=メッセージのみ
//                    FRAMETYPE_SONG=曲
//                    FRAMETYPE_SONGERROR=エラー曲
//                    FRAMETYPE_COURSE=コース
//  title        : タイトル名(全タイプで何かしらの文字が入る)
//  genre        : ジャンル名(楽曲の場合のみ存在)
//  artist       : アーティスト名(楽曲の場合のみ存在)
//  level        : レベル値(楽曲の場合のみ存在)
//  bpm          : テンポ値(楽曲の場合のみ存在)
//  info         : その他情報(特に無ければ空文字)
//  dl_point     : DLに必要なポイント数(-1=フリー曲、0=購入済の曲、1以上=未購入曲)
//  dl_count     : DLカウント数
//  course_id    : コースID(コース以外は-1)
//   first        : リスト構築直後の選択なら1、通常の選択時は0
///////////////////////////////////////////////////////////////////////
function OnChangeMusicList( frm_type,title,genre,artist,level,bpm,info,dl_point,dl_count,course_id,first )

    // SE再生
    if( first==0 ) then
        // 通常選択時のみ
        hdxPlaySound( 1,FALSE );
    end
    
    // 選択状態を記録
    g_iMusicInfoLevel       = level;
    g_iMusicInfoBpm         = bpm;
    g_iMusicInfoPoint       = dl_point;
    g_iMusicInfoCourseID    = course_id;

    // ゲージフレームのみで初期化
    hdxCtrlAnime( 5,CTRLMODE_MANUAL,0,0,0 );

    if( frm_type==FRAMETYPE_SONG /*|| frm_type==FRAMETYPE_SONGERROR*/ ) then
        // 曲なら
        hdxCreateFont( 0,"メイリオ",48,title,0xFFFEFDFF,0xFFCE95FF,2,0xFF000000 );
        hdxCreateFont( 1,"メイリオ",22,genre,0xFFFFFFFF,0xFFFFFFFF,1,0xFF000000 );
        hdxCreateFont( 2,"メイリオ",22,artist,0xFFFFFFFF,0xFFFFFFFF,1,0xFF000000 );
        hdxCreateFont( 3,"メイリオ",24,info,0xFFFFFFFF,0xFFFFFFFF,2,0xFF1ED5FF );

        g_iMusicInfoDrawType    = 1;
        g_iMusicInfoDrawCount   = 15;           // 登場アニメーションリセット
        if( level>=1 && level<=12 ) then
            // レベルが範囲内ならゲージアップアニメ開始(レベルがそのままフレーム番号)
            hdxCtrlAnime( 5,CTRLMODE_AUTO_END,0,0,level );
        else
            // 範囲外なら「?」表示
            hdxCtrlAnime( 5,CTRLMODE_MANUAL,13,0,-1 );
        end
    elseif( frm_type==FRAMETYPE_COURSE ) then
        // コースなら
        hdxCreateFont( 0,"メイリオ",80,title,0xFFFFFFFF,0xFF51FFE7,2,0xFF000000 );
        hdxCreateFont( 1,"メイリオ",22,genre,0xFFFFFFFF,0xFFFFFFFF,1,0xFF000000 );
        hdxCreateFont( 2,"メイリオ",22,artist,0xFFFFFFFF,0xFFFFFFFF,1,0xFF000000 );
        hdxCreateFont( 3,"メイリオ",24,info,0xFFFFFFFF,0xFFFFFFFF,2,0xFF1ED5FF );
        // コース内の曲リスト(フォントIDは10~)
        local course    = hdxGetCourseList( g_iMusicInfoCourseID );                     // コース情報の取得
        for i=0,course["count"]-1 do
            hdxCreateFont( i+10,"メイリオ",16,course["title"..i],0xFF000000,0xFF000000,1,0xFFD7A6DB );
        end

        g_iMusicInfoDrawType    = 2;
        g_iMusicInfoDrawCount   = 15;                       // 登場アニメーションリセット
        if( level>=1 && level<=12 ) then
            // レベルが範囲内ならゲージアップアニメ開始(レベルがそのままフレーム番号)
            hdxCtrlAnime( 5,CTRLMODE_AUTO_END,0,0,level );
        else
            // 範囲外なら「?」表示
            hdxCtrlAnime( 5,CTRLMODE_MANUAL,13,0,-1 );
        end

        // 選択時の演出開始
        hdxCtrlAnime( 7,CTRLMODE_AUTO_END,0,0,-1 );
    else
        // 非表示
        g_iMusicInfoDrawType    = 0;
    end
end

曲リスト1曲分を表示するコールバック処理

ここには今回表示するリストの左上の座標が渡されるので、これを基点としてフレーム画像などを表示します。
///////////////////////////////////////////////////////////////////////
// hdxDrawMusicList()を呼び出した際に、曲リストの表示が必要になると呼び出される。
///////////////////////////////////////////////////////////////////////
function OnDrawMusicList( x,y,frm_type,level,bpm,flag,lamp_type,clear,fullcombo,best_jadge,best_exscore,dl_point,dl_count,course_cnt )

    if( frm_type==FRAMETYPE_SONG || frm_type==FRAMETYPE_COURSE ) then
        // 曲・コース
        hdxPut( 40,x,y );
        if( level>=1 && level<=12 ) then
            // レベル値
            hdxPut( 16+(level-1), x+16, y+6 );
        else
            // 「?」
            hdxPut( 29, x+16, y+6 );
        end

        // ポイント曲アイコン
        if( dl_point>=0 ) then
            hdxPut( 38, x+66, y+11 );
        end

        // クリアランプ
        if( lamp_type==CLEARLAMP_NO ) then
            return;         // 無しなら何もしない
        end

        // 加算
        hdxSetBlendMode( BLEND_ADD );

        if( clear==TRUE ) then
            // クリアしていたなら点灯
            hdxSetPutStatus( (lamp_type-1)+1, 1, 1, 1, 0 );
            hdxPut( (lamp_type-1)+1, x-6, y-6 );
        else
            // クリアしていなかった場合は点滅
            hdxSetPutStatus( (lamp_type-1)+11, g_fMusicInfoLampAlpha, 1, 1, 0 );
            hdxPut( (lamp_type-1)+11, x-6, y-6 );
        end

        if( fullcombo==TRUE ) then
            // フルコンボなら超点滅
            hdxSetPutStatus( 7+g_iMusicInfoFullComboCount, 1, 1, 1, 0 );
            hdxPut( 7+g_iMusicInfoFullComboCount,x-35,y-35 );
        end

        hdxSetBlendMode( BLEND_NORMAL );

    elseif( frm_type==FRAMETYPE_MESSAGE ) then
        hdxPut( 40,x,y );
    elseif( frm_type==FRAMETYPE_SONGERROR ) then
        hdxPut( 41,x,y );
        hdxPut( 15, x+16, y+6 );
    elseif( frm_type==FRAMETYPE_SUBDIR ) then
        hdxPut( 42,x,y );
        hdxPut( 30, x+16, y+6 );
    elseif( frm_type==FRAMETYPE_BACK ) then
        hdxPut( 43,x,y );
        hdxPut( 28, x+16, y+6 );
    end

end

リスト決定時の処理

曲が選択されると曲データのチェックが行われるため、実際にゲームが開始されるまで若干のラグがあります。このコールバックは曲データのチェック前に呼び出されるので、選択された瞬間の効果音などを鳴らす時などに使用できます。なお、階層などの曲以外が選択された時も呼び出されるので、これで演出を変えたりすることも出来ます。
///////////////////////////////////////////////////////////////////////
// リストが決定されると   呼び出される。
// ここで選択された楽曲の情報の表示開始などを行う。
//  frm_type     : フレームタイプ
//                    FRAMETYPE_BACK=戻る
//                    FRAMETYPE_SUBDIR=階層に入る
//                    FRAMETYPE_MESSAGE=メッセージのみ
//                    FRAMETYPE_SONG=曲
//                    FRAMETYPE_SONGERROR=エラー曲
//                    FRAMETYPE_COURSE=コース
///////////////////////////////////////////////////////////////////////
function OnSelectMusicList( frm_type )
    // 決定音を鳴らす
//    trace( "OnSelectMusicList : frm_type="..frm_type );
    hdxPlaySound( 3,FALSE );
end

曲ダウンロード処理

///////////////////////////////////////////////////////////////////////
// 楽曲のDLが必要な場合に最初に呼び出される。
///////////////////////////////////////////////////////////////////////
function OnStartDownload()
    trace( "ダウンロード開始" );
    // ゲージを0%で表示
    hdxCtrlAnime( 9,CTRLMODE_MANUAL,0,0,-1 );
end

///////////////////////////////////////////////////////////////////////
// 楽曲DL中に進行度が変化した場合に呼び出される。
//  per : DLパーセント値(0~100)
///////////////////////////////////////////////////////////////////////
function OnDownloadStatus( per )
    trace( "ダウンロード中 : "..per.."%" );

    // ゲージの更新(0~100をそのままフレーム番号にセット)
    hdxCtrlAnime( 9,CTRLMODE_MANUAL,per,0,-1 );
end

///////////////////////////////////////////////////////////////////////
// 楽曲のDLが完了したり失敗した場合に呼び出される。
// ※エラー表示はシステムが行うため独自でエラー表示を出す必要はないが、
//  エラー演出だけを行うといったことは可能
//  err : エラー時はtrueが入る
///////////////////////////////////////////////////////////////////////
function OnEndDownload( err )
    trace( "ダウンロード終了 : "..err );
    // ゲージを非表示
    hdxCtrlAnime( 9,CTRLMODE_MANUAL,-1,0,-1 );
end

ゲーム開始処理

曲を決定した際にダウンロード完了のあとや、ダウンロードが必要無い場合でゲームが開始出来る状態になると呼び出されます。これ以降にシーンを終了させて実際にゲームを開始させることが出来ます。
///////////////////////////////////////////////////////////////////////
// 楽曲が選択されてプレイ可能な状態になると呼び出される。
///////////////////////////////////////////////////////////////////////
function OnEndMusicList()
    trace( "ゲームプレイ開始" );
    hdxStopSound( 0 );
    hdxCtrlAnime( 8,CTRLMODE_AUTO_END,0,0,-1 );
    g_bMusicSelected    = TRUE;
end

オプション処理

///////////////////////////////////////////////////////////////////////
// Startボタンが押されてオプション変更が開始されると呼び出される。
///////////////////////////////////////////////////////////////////////
function OnStartOption()
    g_bMusicInfoOptionEnable    = TRUE;
end

///////////////////////////////////////////////////////////////////////
// スクロールスピード以外のオプションが変更されるごとに呼び出される。
///////////////////////////////////////////////////////////////////////
function OnChangeOption( opt_id )
    trace( "オプション変更 : "..opt_id );
    hdxPlaySound( 2,FALSE );
end

///////////////////////////////////////////////////////////////////////
// Startボタンが離されてオプション変更が終了されると呼び出される。
///////////////////////////////////////////////////////////////////////
function OnEndOption()
    g_bMusicInfoOptionEnable    = FALSE;
end

スクロールスピード処理(メインゲームと共通)

///////////////////////////////////////////////////////////////////////
// スクロールスピードの変更指示があると呼び出される。
// ※この関数のみゲーム中でも呼び出される
///////////////////////////////////////////////////////////////////////

// スクロールスピードの固定倍率
FIXED_SCROLLSPEED = { 1.00, 1.50, 2.00, 2.25, 2.50, 2.75, 3.00, 3.25, 3.50, 3.75, 4.00 };

function OnChangeScrollSpeed( scene,cur_spd,digital,analog )
//    trace( "digital="..digital );

    local ret;
    if( digital==0 ) then
        // アナログ値ならそのまま加算
        ret = cur_spd + analog;
        if( ret<0.5 ) then 
            ret = 0.5;
        end
        if( ret>10 ) then
            ret = 10;
        end
    elseif( digital>0 ) then
        // デジタルの上昇なら
        for i=1,11 do
            if( FIXED_SCROLLSPEED[i]>cur_spd ) then
                // 現在の値より大きい倍率なら確定
                return FIXED_SCROLLSPEED[i];
            end
        end
        // すでに最大値なら
        if( scene==0 ) then
            // 曲選択中の場合は回り込んで再び1倍から
            return FIXED_SCROLLSPEED[1];
        end
        ret = cur_spd;
    else
        // デジタルの下降なら
        for i=11,1,-1 do
            if( FIXED_SCROLLSPEED[i]<cur_spd ) then
                // 現在の値より小さい倍率なら確定
                return FIXED_SCROLLSPEED[i];
            end
        end
        // すでに最小値なら何もしない
        ret = cur_spd;
    end

    return ret;
end