この記事では、タイルグラフィック、割り込み、タッチスクリーン、キーボードの操作を検討します。 これに基づいて、私たちは子供の頃から「タグ」という有名なゲームをすべての人に書きます。
まず、DSビデオコントローラーの動作を詳細に調べます。
ビデオコントローラーの初期化
ほとんどすべてのビデオモードは、画面への出力を整理するために「マルチレイヤー」構造を使用します。つまり、同時に最大4つのプラン(背景)を表示できます。
どの用語を使用するのが良いかよくわかりません。「計画」-「背景」があるとします。
合計で6種類の背景があります。
- フレームバッファ-最も単純なタイプの背景。 ビデオメモリ内の各ワード(16ビット)は、画面上のピクセルとして表示されます。 (前の例で使用);
- 3D-画面上の画像はOpenGLのようなコマンドによって形成されます。
- テキスト-テキストの背景(別名タイル)は8x8ピクセルのブロックに分割され、各ブロックにはタイルの1つが表示されます。
- 回転-回転とスケーリングの可能性があるタイル張りの計画。
- 拡張回転-フレームバッファと同じですが、ピクセルあたり8ビットの色深度を表示することもでき、スクロール、スケーリング、回転もサポートしています。さらに、アルファビットを使用できます。
- 大きなビットマップ-ピクセルあたり8ビットの大きな512x1024または1024x512画像。
既に述べたように、両方のニンテンドーDSグラフィックコアは共有ビデオメモリ(656KB)を使用します。 さまざまなサイズと目的の9つの銀行に分かれており、AからIまでのラテン文字で名前が付けられています。完全なリストを
次に示します。 ビデオコントローラがこれらのバンクを使用できるようにするには、それらを0x06000000で始まるアドレス空間の特別な領域にマップ(「マップ」)する必要があります。
ビデオメモリの構成とさまざまな銀行の目的の詳細については、
こちらをご覧ください 。
ゲームでは、ビデオコントローラーのゼログラフィックモード(MODE_0_2D)を使用します。このモードには4つのタイルプランがあります。 下の画面(デフォルトでは追加のコア)では、ゲームの1つが実際に発生し(チップを移動)、もう1つはスプラッシュ画面の表示に適用されます。 上部画面(メインコア)は、テキスト情報を表示するためにのみ使用されます。
videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE); //
videoSetModeSub(MODE_0_2D | DISPLAY_BG1_ACTIVE | DISPLAY_BG0_ACTIVE); //
* This source code was highlighted with Source Code Highlighter .
タイルプランモードでのビデオメモリの構成についてもう少し詳しく説明します。 このモードでの画面上の画像は、いわゆるタイルカードに基づいて形成されます。このタイルカードには、画像内の8x8ピクセルの正方形に表示されるタイルの数が書き込まれます。 タイル自体は別のメモリ領域に保存されます。 ビデオコントローラーはどのアドレスからマップを表示し、タイルはコントロールレジスタ(CR)によって決定されます。 8つのプラン(メインコアに4つ、セカンダリに4つ)のそれぞれにレジスタがあります。 この例では、SUB_BG0_CR、SUB_BG1_CRおよびBG0_CRの3つを初期化する必要があります-使用される各プランに1つです。
ちょっとしたトリックがあります。 実際には、制御レジスタは16ビットであり、その中にカードアドレスとタイルのアドレスおよびその他のパラメーターの両方を格納する必要があります。 これに関連して、5ビットがアドレスに割り当てられます。 したがって、タイルは16Kのオフセットで32個のベースアドレスに保存でき、カードは2Kの位置で32個のアドレスに保存できます。
1つのメモリバンクに格納されているという事実にもかかわらず、次の図があります。
下の画面には2枚のタイルカードが必要です。 それらはベースアドレス0と1に配置されます。また、タイル自体の2つのセットが必要です。 タイルのゼロベースアドレスは、カードの使用済みメモリと交差するため、使用しません。 ベースアドレス1から、タイルタイルを配置します。 これらは36 KBを占有するため、ベースアドレス2、3、および4も使用しません。 次に、アドレス5から、スタートアップスクリーンセーバー用のタイルのセットを配置します。
トップ画面では、ベースアドレスが0のタイルカードを使用し、ロシア語フォントが配置されるタイルは、アドレス1から配置します。テキストの制御レジスタは、コンソールの初期化中にlibNDSを設定します。
次に、16個のカラータイル(BG_COLOR16)を使用するように制御レジスタを初期化します。
int tile_base = 1;
int map_base = 0;
int tile_base_s = 5;
int map_base_s = 1;
int char_base = 1;
int scr_base = 0;
REG_BG0CNT_SUB = BG_COLOR_16 | BG_TILE_BASE(tile_base_s) | BG_MAP_BASE(map_base_s); //
REG_BG1CNT_SUB = BG_COLOR_16 | BG_TILE_BASE(tile_base) | BG_MAP_BASE(map_base); //
* This source code was highlighted with Source Code Highlighter .
実際、他のベースアドレスにタイルとカードを配置すると、かなりの量のビデオメモリ(32KB)を節約できます。 ただし、この場合、このような最適化は必要ありません。 空きメモリが十分すぎる。
次に、ベースアドレス番号を絶対的なビデオメモリアドレスに変換して、直接操作できるようにします。
u16* sub_tile = (u16*)BG_TILE_RAM_SUB(tile_base);
u16* sub_map = (u16*)BG_MAP_RAM_SUB(map_base);
u16* sub_tile0 = (u16*)BG_TILE_RAM_SUB(tile_base_s);
u16* sub_map0 = (u16*)BG_MAP_RAM_SUB(map_base_s);
u16* tile_char = (u16*)BG_TILE_RAM(char_base);
u16* map_char = (u16*)BG_MAP_RAM(scr_base);
* This source code was highlighted with Source Code Highlighter .
次に、タイルのデータをビデオメモリにコピーします。
memcpy(( void *)sub_tile, (u8*)tilesTiles, 192*192/2); //
for (i=0; i < 16; ++i)
BG_PALETTE_SUB[i] = tilesPal[i]; //
memcpy(( void *)sub_tile0, (u8*)startTiles, 256*192/2); //
for (i=0; i < 16; ++i)
BG_PALETTE_SUB[i+16] = startPal[i]; //
* This source code was highlighted with Source Code Highlighter .
そして、すぐに下の画面のゼロプランにスプラッシュスクリーンタイルを入力します。
for (i=0; i< 24*32; i++) //
sub_map0[i] = (u16)(i)|0x1000;
* This source code was highlighted with Source Code Highlighter .
タイル番号以外のタイルマップの各単語には、パレットと軸に沿った反射に関する情報が含まれています。 最初のパレットを使用するために、マップの各要素に対して12ビットを1に設定します。
タイルマップのビットマップ要素をペイントすると、次のように表示されます。
ビット | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|
予定 | パレット | Vetrik.otr。 | ホライズ。 | タイル番号 |
---|
libNDSコンソールを初期化します。
PrintConsole *console = consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, scr_base, char_base, true , true );
ConsoleFont font;
font.gfx = (u16*)pa_text2Tiles; //
font.pal = (u16*)pa_text2Pal; //
font.numChars = 256; //
font.numColors = pa_text2PalLen/2;
font.bpp = 4;
font.asciiOffset = 0;
font.convertSingleColor = false ;
consoleSetFont(console, &font);
* This source code was highlighted with Source Code Highlighter .
プログラムでは、PAlibエンコードCP1251用に
ClusterMによって作成されたフォントを使用します。 残念ながら、現在のバージョンのライブラリでは、Unicodeに切り替えようとしたときに、ASCIIの上半分の文字を出力するサポートが壊れていたため、ロシア語のテキストなしで行う必要があります。 もちろん、文字コードをタイルマップに書き込むことで直接表示できます。
すべてのタイルは、
gritプログラムを使用してBMPから変換することにより作成されます。
キーボードとタッチスクリーン
libNDSを使用せずにキーの状態を読み取るには、ARM9プロセッサレジスタだけでなく、ARM7も使用する必要があります。 幸いなことに、ライブラリの作成者はこの事実を無視するように誘われます。 scanKeys()関数を使用して、クリック情報の状態を更新するだけです。 また、押されたキーを決定するため、またはタッチスクリーンを押すためのkeysHeld()。 正確に押されるものは、関数によって返される値のビットに従って決定されます。
キー定義 | 仮面
ビット | 関連する入力 |
---|
KEY_A | 1 << 0 | ボタンA |
KEY_B | 1 << 1 | ボタンB |
KEY_SELECT | 1 << 2 | 選択ボタン |
KEY_START | 1 << 3 | スタートボタン |
KEY_RIGHT | 1 << 4 | 右ボタン |
KEY_LEFT | 1 << 5 | 左ボタン |
KEY_UP | 1 << 6 | 上ボタン |
KEY_DOWN | 1 << 7 | ボタンダウン |
KEY_R | 1 << 8 | Rボタン |
KEY_L | 1 << 9 | Lボタン |
KEY_X | 1 << 10 | Xボタン |
KEY_Y | 1 << 11 | Yボタン |
KEY_TOUCH | 1 << 12 | タッチスクリーン |
KEY_LID | 1 << 13 | カバーを閉じました |
したがって、ループでそれを行うだけです。
scanKeys();
held = keysHeld();
* This source code was highlighted with Source Code Highlighter .
そして、保持された変数で必要なアクションを実行するという事実に応じて。
KEY_TOUCHビットが設定されている場合、タッチスクリーンが検出され、touchRead関数を使用してスタイラスの座標を読み取ることができます。 touchPosition構造体を返します。この構造体では、スタイラスが指すピクセルの座標を含むpxおよびpyフィールドに関心があります。
if (held&KEY_TOUCH){ //
touchRead(&touchXY);
...
}
* This source code was highlighted with Source Code Highlighter .
中断
ユーザーと対話するほとんどのプログラムの通常の動作(プログラムも例外ではありません)には、時間間隔の制御が必要です。これは通常、タイマーからの割り込みによって提供されます。 割り込みを処理するための3つのレジスタがあります。
名 | 住所 | 大きさ | 説明 |
---|
REG_IME | 0x04000208 | 16ビット | メイン割り込み許可レジスタ |
REG_IE | 0x04000210 | 32ビット | 割り込み許可レジスタ |
REG_IF | 0x04000214 | 32ビット | 割り込みフラグレジスタ |
割り込みマスタイネーブルレジスタは、すべての割り込みハンドラをオンおよびオフにする機能を提供します。
割り込み有効化レジスタを使用すると、個々の割り込みを有効または無効にすることができます。 レジスタの各ビットは、特定の割り込みを担当します。
ビット | libndsの名前 | 説明 |
---|
0 | IRQ_VBLANK | 垂直逆ビーム |
1 | IRQ_HBLANK | 水平戻りビーム |
2 | IRQ_YTRIGGER | REG_VCOUNT行のスキャン |
3 | IRQ_TIMER0 | タイマーが作動しました0 |
4 | IRQ_TIMER1 | タイマー1が機能しました |
5 | IRQ_TIMER2 | タイマー2が作動した |
6 | IRQ_TIMER3 | タイマー3トリガー |
7 | IRQ_NETWORK | ? |
8 | IRQ_DMA0 | DMA 0 |
9 | IRQ_DMA1 | DMA 1 |
10 | IRQ_DMA2 | DMA 2 |
11 | IRQ_DMA3 | DMA 3 |
12 | IRQ_KEYS | キーを押した |
13 | IRQ_CART | GBAカートリッジを取り外しました |
16 | | ARM7 IPC割り込みがトリガーされました |
17 | | 入力FIFOは空ではありません |
18 | | 出力FIFOは空ではありません |
19 | IRQ_CARD | DSカードのデータが完了しました |
20 | IRQ_CARD_LINE | DSカード割り込み3 |
21 | | GFX FIFO割り込み |
割り込みフラグレジスタは、割り込みが発生するとハードウェアによって設定されます。 割り込みビットマスクが含まれています。
割り込みを直接処理することはありませんが、通常どおり、libndsのサービスを使用します。
まず、「垂直リバースビーム」に沿って割り込みハンドラーを設定します。 この割り込みは、画面のレンダリングが完了すると呼び出されます。 画像のちらつきや裂けを防ぐために、この割り込みのハンドラーに画像を出力します。
void IRQ_vblank( void ){ //
... ...
}
...
irqSet(IRQ_VBLANK, IRQ_vblank); // .
* This source code was highlighted with Source Code Highlighter .
次に、タイマーの1つを目的の周波数に設定し、割り込みハンドラーを設定します。 libNDSライブラリは、これらの目的に非常に便利なtimerStart関数を提供します。 タイマーと割り込みを完全に初期化するには、必要な分周器、周波数、割り込みハンドラーへのポインターを使用してこの関数を呼び出すだけで十分です。
void timer0_function( void ){
... ...
}
...
timerStart(0, ClockDivider_256, TIMER_FREQ_256(1000), timer0_function); // 256 1000
* This source code was highlighted with Source Code Highlighter .
最後に、libndsライブラリによって提供される別の関数、swiWaitForVBlankを検討します。 垂直後方割り込みが発生するまでARM9プロセッサを停止します。
上記のすべてを使用して、簡単なゲームをすでに作成できます。
ここでは、ゲームの「タグ」のソースコードを取得できます。
ここに実行可能ファイルがあります。
スクリーンショット: