DMAの応用例

ウィンドウとDMA HBLANK機能を使ったサンプルコードです。元ネタのToncチュートリアルからdevkitPro用に変換させて頂きました。メトロイド序盤で表示されるライトをほぼ再現しています。

1.png

mode0を使用して16色。bg0:ASCII文字(256x256)、bg1:マスク(256x256)、bg2:ステージ(512x256)となっておりステージのみ512x256です。まずステージ表示には始点座標を変えて、さらにウィンドウの内部と外部にどれを表示させるかの設定を加えています。

	REG_BG2HOFS = 120;
	REG_BG2VOFS = 64;

	REG_WIN0H  = 0;
	REG_WIN0V  = SCREEN_HEIGHT;
	REG_WININ  = WIN_0_BG0 | WIN_0_BG2;
	REG_WINOUT = WIN_0_BG0 | WIN_0_BG1;

円を表示させる方法ですが、ウィンドウ機能とHBLANKを組み合わせています。表示させる円の大きさを計算し、その円の横ラインごとの「非表示、表示」座標を計算、HBLANK毎にウィンドウレジスタ(REG_WIN0H)に放り込みます。

/* Create an array of horizontal offsets for a circular window.
*	The offsets are to be copied to REG_WINxH each HBlank, either 
*	by HDMA or HBlank isr. Offsets provided by modified 
*	Bresenham's circle routine (of course); the clipping code is not
*	optional.
*
*	\param x0	X-coord of circle center.
*	\param y0	Y-coord of circle center.
*	\param rr	Circle radius.
*/

u16 BgWinh[SCREEN_HEIGHT+1] ALIGN(4);

IWRAM_CODE void BgCreateWindowCircleDma(s32 x0, s32 y0, s32 rr)
{
	// Zero clear
	for(vs32 i=0; i<SCREEN_HEIGHT+1; i++)
	{
		BgWinh[i] = 0;
	}


	s32 x=0, y=rr, d=1-rr;
	u32 tmp;

	while(y >= x)
	{
		// Side octs
		tmp  = BgClamp(x0 + y, 0, SCREEN_WIDTH+1);
		tmp += BgClamp(x0 - y, 0, SCREEN_WIDTH+1) << 8;

		// o4, o7
		if(BgInRange(y0-x, 0, SCREEN_HEIGHT))
		{
			BgWinh[y0 - x]= tmp;
		}

		// o0, o3
		if(BgInRange(y0+x, 0, SCREEN_HEIGHT))
		{
			BgWinh[y0 + x]= tmp;
		}

		// Change in y: top/bottom octs
		if(d >= 0)
		{
			tmp  = BgClamp(x0 + x, 0, SCREEN_WIDTH+1);
			tmp += BgClamp(x0 - x, 0, SCREEN_WIDTH+1) << 8;

			// o5, o6
			if(BgInRange(y0-y, 0, SCREEN_HEIGHT))
			{
				BgWinh[y0 - y]= tmp;
			}

			// o1, o2
			if(BgInRange(y0+y, 0, SCREEN_HEIGHT))
			{
				BgWinh[y0 + y]= tmp;
			}

			d -= 2 * (--y);
		}

		d += 2 * (x++) + 3;
	}

/*
	for(vs32 i=0; i<SCREEN_HEIGHT+1; i++)
	{
		TRACE("%d: %4x\n", i, BgWinh[i]);
	}
	for(;;){}
*/

	REG_DMA3CNT = 0;
	REG_DMA3SAD = (u32)&BgWinh[1];
	REG_DMA3DAD = (u32)&REG_WIN0H;
	REG_DMA3CNT = 1 | (DMA_DST_RELOAD | DMA_REPEAT | DMA_HBLANK | DMA_ENABLE);
}
//---------------------------------------------------------------------------
IWRAM_CODE s32 BgClamp(s32 val, s32 min, s32 max)
{
	if(val < min)
	{
		return min;
    }

	if(val > max)
	{
		return max;
	}

	return val;
}
//---------------------------------------------------------------------------
IWRAM_CODE bool BgInRange(s32 x, s32 min, s32 max)
{
	return ((x)>=(min)) && ((x)<(max)) ? TRUE : FALSE;
}

GBAの画面の高さは160ですので160回HBLANKを発生させます。1ライン分のウィンドウサイズを2バイト、REG_WIN0Hに格納しています。HBLANK毎にウィンドウ値(円の大きさ)を決定させているわけです。チラツキもなく実現しており、非常に芸術点が高い作品となっています。ぜひ動作確認をしてみてください。

出典元

履歴


添付ファイル: file1.png 28件 [詳細]

トップ   差分 履歴 リロード   一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2023-06-27 (火) 11:52:08