...

りんご採りゲーム - 数理情報学科

by user

on
Category: Documents
2

views

Report

Comments

Transcript

りんご採りゲーム - 数理情報学科
りんご採りゲーム
∼ニンテンドーDSソフトウェア∼
理工学部数理情報学科
T050092 南 香澄
指導教員 池田 勉
概要
2004 年 12 月に任天堂が発売したニンテンドーDSで子ども・大人関係なく遊んでいる
ことを電車などでよく見かける.発売 4 年目の 2008 年年間売り上げ台数は前年比 4 割減
だが,ハードの売り上げ台数ランキングではいまだ 1 位である.ニンテンドーDSにはダ
ブルスクリーンやタッチパネルを採用するなど今までの携帯型ゲーム機器にない新しい機
能を搭載している。私はこのニンテンドーDSに興味を持ち,今までパソコンでしか動か
したことのない自分のプログラムをニンテンドーDSというハードの上で動かすことを目
的にこの研究を始めた.
ニンテンドーDSで自作のプログラムを動かすためには実行ファイルを作成するための
コンパイラはもちろんプログラムを保存するメディアとそのメディアを読み込むマジック
コンピュータ,いわゆるマジコンなどの開発環境が必要である.プログラムはパソコン上
で C++ 言語で作成した.これによって得られる cpp ファイルは,フリーソフトである
devkitPro(コンパイラ)msys(パケージ) と を利用して,nds というニンテンドーDSでも
動作する形式に変換した.これをフリーのエミュレータ NO$GBA を用いてパソコン上で
テストを行った後,microSD カードに保存すれば,ニテンドーDSのプログラムは完成す
る.
作成したプログラムは「りんご採りゲーム」だ.りんご採りゲームとはニンテンドーD
Sの下画面がメイン画面で,画面左に木があり,そこになったりんごをタッチペンでスラ
イド (ドラッグ&ドロップ) して右下にあるかごに入れるというオリジナルのアクション
ゲームである.ゲームを起動するとランダムに赤いりんごと黄色いりんごが合わせて 10
個表示され,そのりんごを次々とできるだけ早くかごにいれていく.赤いリンゴをかごに
入れるとりんご 1 つにつき 100 点,黄色いりんごをかごに入れると -100 点を加点しスコ
アを計算する.制限時間は 15 秒で,その間りんごは 10 個を超えないだけランダムに出現
し,3 秒ほどしたら消えるように設定している.そして,スコアを競うというものだ.こ
のプログラムを組むにあたって,プログラムの中身はもちろん,画像の作成にも力を入れ
た.おかげで見た目がとてもきれいに仕上がっている.ただし,動きはもう少し完成度の
高いものを作成したかった.特に時間の設定に手間取って時間が足りず,まだ納得いくも
のができあがっていない.仕方なく本研究ではここまでで完成として一旦終了としている
がいつか納得のいくものを作り上げたいと考えている.
2008 年度 卒業論文
りんご採りゲーム
∼ニンテンドーDSソフトウェア∼
龍谷大学理工学部数理情報学科
T050092 南 香澄
指導教員 池田 勉
目次
1
はじめに
1
2
開発環境
2.1 ARM アーキテクチャについて . . .
2.2 msys と devkitPro のセットアップ
2.3 NO$GBA と M3REAL の入手 . .
2.4 動作テスト . . . . . . . . . . . . .
.
.
.
.
1
2
2
2
4
.
.
.
.
.
.
.
.
.
.
4
4
5
5
5
6
6
10
10
10
16
3
ソフトウェア開発
3.1 サンプルプログラムの編集 . . .
3.1.1 ヘッダファイル . . . . .
3.1.2 VIDEO の設定 . . . . .
3.1.3 VRAM の設定 . . . . .
3.1.4 その他の設定 . . . . . .
3.2 画像を表示させる . . . . . . . .
3.3 スプライトを使ってみる . . . .
3.3.1 スプライトとは . . . . .
3.3.2 とりあえず動かしてみる
3.4 ゲーム完成 . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4
終わりに
23
4.1 まとめ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
5
参考文献
24
6
資料
6.1 表 . . . . . . . . . .
6.1.1 videoSetMode
6.1.2 VRAM Bank
6.2 プログラム . . . . .
6.2.1 demo.cpp . .
6.2.2 sky.cpp . . .
6.2.3 apple.cpp . .
6.2.4 game.cpp . .
i
i
i
ii
vi
vi
vi
vii
ix
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
i
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
はじめに
私は幼少の頃からゲームが大好きで物心つく頃には当時発売されていたファミリーコン
ピュータで遊んでいた.母の話では 3 歳の頃からファミリーコンピュータのコントローラ
を持って遊んでいたそうだ.そんな私は一度任天堂やソニーが発売しているような多くの
人が人が使うハードウェアで遊べるゲームを自分で作ってみたいと考えていた.
任天堂が発売しているニンテンドーDSは携帯型ゲーム機の中では日本のシェアが 1 位
である.海外にも積極的に輸出している.そんなニンテンドーDSには他のゲーム機にな
い機能がたくさん備わっている.画面が 2 つある「ダブルウィンドウ 1」やタッチパネル,
マイク入力などである.このニンテンドーDSの機能と,今までパソコンでしか動かした
ことのないプログラムを他のハード上で動かすことに関心をひかれ本研究をはじめた.ニ
ンテンドーDSの機能の中でも特にタッチペンでのアクションに興味があったので,タッ
チペンでりんごを採るゲームを考案し作成した.
図 1: ニンテンドーDS
2
開発環境
ニンテンドーDSのソフトウェアを開発するためにはまず必要な開発環境を整えなけれ
ばならない.ニンテンドーDSのコンピュータにはパソコンとは違った ARM という CPU
が採用されているため専用のコンパイラが必要である.専用の環境を利用してプログラ
ムファイルを nds ファイルに変換する.本研究に使用するパソコンの環境 WindowsXP
1
Professional Version 2002 Service Pack 2 で動作するコンパイラを入手しなければならな
い.ニンテンドーDSというパソコンとは違ったハードウェアで実行するため,プログラ
ムを保存するメディアも必須である.
2.1
ARM アーキテクチャについて
開発環境を整えるにはまず,ニンテンドーDSの構造を知る必要がある.インターネッ
トを用いて調べてみたところニンテンドーDSの CPU は ARM(アーム) アーキテクチャ
であった.そこで ARM について調べてみた.
ARM とは Acorn RISC Machine の略でイギリスの ARM ホールディングスによって
開発されている.消費電力を抑える特徴を持ち,携帯電話・携帯型ゲーム機や電卓など低
消費電力を目標に設計されるモバイル向けに広く用いられる 32bit CPU アーキテクチャ
のことである.任天堂ではこの ARM アーキテクチャをゲームボーイアドバンスから採用
している.
ARM には 10 余りのいろいろな種類があり,ニンテンドーDSでは ARM7 や ARM9
が使用されている.これらは他に一般的な携帯電話や,アップルが開発している iPod な
どに用いられている.
このようにニンテンドーDSではアーキテクチャがパソコンとは違うため,ARM アー
キテクチャ専用の開発環境が必要である.
2.2
msys と devkitPro のセットアップ
まず,プログラミングに欠かせないのはソースを実行ファイルに変えるためのコンパイ
ラである.ニンテンドーDSの CPU は先ほども述べたように ARM である.そのため,
ARM 専用のコンパイラが必要である.ニンテンドーDSのソフトを開発するにはフリー
ソフトの devkitPro[5] を使用するのが一般的で,devkitPro のインストーラでは開発に
必要な基本的な機能を全て用意してくれる.インストーラに従ってインストールすればこ
の devkitPro を Windows 上で動作するためのシェルやアーカイブツールなどを統合した
パッケージである MSYS(Minimal SYStem)[4] も一緒にインストールしてくれる.私は
Cygwin などの環境もなかったので devkitPro のインストーラに従い一緒に MSYS もイ
ンストールした.これらのソフトを使って C++ 言語で書かれたプログラムの cpp ファ
イルから nds ファイルを作成する.
devkitPro をインストールする際に devkitPPC や devkitPSP というパッケージのイ
ンストールもできる.これらもあわせて振るインストールをするとニンテンドーDSだけ
でなく,同じく任天堂のゲームキューブやソニーのプレイステーションポータブルのソフ
ト開発もすることができる.
2.3
NO$GBA と M3REAL の入手
nds ファイルを実際にニンテンドーDS上で動かすためにはマジックコンピュータ (マジ
コン) と呼ばれるプログラムを読み込む媒介が必要である.主に R4 Revolution for DS とい
2
うマジコンが流通しているようだ.私は M3REAL[6](図 2)を購入した.これは microSD
カードに保存された nds ファイルをニンテンドーDSで動かすための媒介となるものであ
る.microSD カードのデータが読み込めるため,microSD カードに保存した画像や音楽
データもニンテンドーDS上で実行できるようになる.このハードを使う前には初期設定
をする必要がある.microSD カードにシステムファイルを作成しなければならない.シス
テムファイルは M3REAL の Web ページから簡単にダウンロードすることができる.シ
ステムファイルの中身は本研究の内容から外れてしまうため省略する.ダウンロードした
ファイルをフォーマットした microSD カードに保存すれば初期設定完了である.microSD
カードは 2GB を使用した.
図 2: M3REAL と microSD カード
M3REAL や R4 Revolution for DS などのマジコンは市販されているソフトウェア
のデータをコピーすれば市販されているソフトウェアを購入しなくても遊ぶことができて
しまう.そのため,任天堂をはじめ ニンテンドーDSのソフトウェアを開発する各社が告
訴した.その後の動きは見られないが現在は入手が困難になっている.なお,昨年新しく
発売されたニンテンドーDSiではマジコンは動作しないよう設定されている.
しかし,作成したプログラムの動作確認のためいちいち microSD カードに保存して,ニ
ンテンドDS上で実行するのは大変な手間である.パソコン上で nds ファイルを実行でき
るようエミュレータであるフリーソフト,NO$GBA[7] (ノーキャッシュジービーエー) を
ダウンロードした.フリーでお金がいらないからノーキャッシュだそうだ.名前にもある通
りこのソフトは元々ゲームボーイアドバンス上で動く gba ファイルを動かすためのもので
ある.よって gba ファイルも実行することができる.ダウンロードしたら no$gba-w とい
うフォルダがデスクトップにできるのでその中にある NO$GBA.EXE のショートカット
をデスクトップに作っておくと便利である.このショートカットに nds ファイルをドラッ
3
グ&ドロップして実行する.ただし,このようなエミュレータは必ずしもニンタンドーD
Sと全く同じように表示されるわけではないことに注意しなければならない.実際の動作
を確認したいときには必ずニンテンドーDS上で確認を行わなければ正しい結果は得るこ
とができない.
2.4
動作テスト
開発環境が整ったらサンプルプログラムを用い
て開発環境の動作テストをする.サンプルプログ
ラムは devkitPro のフォルダにたくさん入ってい
る.
それぞれのプログラムは最低でも次のようなも
のが用意されている
・sample
・Makefile
・source
・filename.c または filename.cpp
それぞれの Makefile がある階層で make のコ
マンドを使うと同じ階層に nds ファイルが生成
される.このとき nds ファイルのファイル名は
Makefile の入っているフォルダ名と同じになる.こ
れを NO$GBA のショートカットにドラッグ&ド
ロップすれば,その動作確認ができる.パソコン
上で動作が確認できたら microSD カードに nds
ファイルをコピーし,M3REAL に挿して実行す
る.
私は devkitPro/examples/Graphics/2D/
hello_word をコンパイルし,実行した.このプ
ログラムは下の画面に「hello」と表示され,画面
をタッチすると座標を読み込み表示する(図 3).
図 3: hello word.nds
ソフトウェア開発
3
環境が整ったらいよいよソフトウェアを開発する.
3.1
サンプルプログラムの編集
私は C++ 言語も nds ファイルも触ったことがなかったので,サンプルプログラムを理
解することからはじめた.サンプルプログラムを手本に好きな文字を好きな場所に書き,
タッチした場所の座標を読み込むプログラムを作成した.たったそれだけのプログラムだ
4
が,nds ファイルを作成するときはいろいろな設定をしなければコンパイルがうまくいか
ない.画面の設定がニンテンドーDSのソフトウェア開発で一番の課題であると強く感じ
た.
作成したプログラムは資料の 6.2.1 demo.cpp である.
3.1.1
ヘッダファイル
プログラムの最上部にヘッダファイル ( .h ファイル) が記述されている.
²
¯
[ 1] #include <nds.h>
[ 2] #include <stdio.h>
±
°
2 行目の stdio.h は C 言語などのプログラムでもよく使われる printf を使えるようにす
るためのヘッダファイルである.問題なのは 1 行目の nds.h である.このヘッダファイル
は nds ファイルを作成するときに必ず必要になるヘッダファイルで,devkitPro にある.
nds ファイルを作るとき,実際にはたくさんのヘッダファイルが必要となる.これらのた
くさんのヘッダファイルは標準のフォルダにインストールした場合 C:/devkitPro/nds に
入っており, nds.h をインクルードすればそれらの必要なヘッダファイルが自動的に全て
インクルードされる仕組みになっている.ニンテンドーDSのソフトウェアを作成する際
にはこれらのヘッダファイルの中身を理解することが重要となってくる.
3.1.2
VIDEO の設定
nds ファイルを作成する際には videoSetMode を設定する必要がある.videoSetMode
にはいろいろな種類があり,資料の表 1 を参考にモードを設定する.
²
[ 6]
[ 7]
±
¯
videoSetMode(0);
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
°
6 行目の videoSetMode はメイン画面の設定で通常は上画面である.このプログラムでは
上画面を使わないため設定はなし (0) になっている.一方 7 行目にある videoSetModeSub
は,サブ画面の設定で,サブ画面とは通常下画面のことである.このプログラムでは文
字を表示したいので資料の表 1 で text とある設定を選び設定する.ここでは Mode 0 の
BG0 をアクティブにしている.
3.1.3
VRAM の設定
次に VRAM の設定である.VRAM とは Video Random Access Memory の略で,ディ
スプレイに対するビデオ表示部分のメモリとして使われる RAM(Random Access Memory)
のことである [9].画像や文字を表示するなど,場合によっては番地を指定する.
5
¯
²
[ 8]
[ 9]
±
vramSetBankC(VRAM_C_SUB_BG);
SUB_BG0_CR = BG_MAP_BASE(31);
°
ここでは VRAM Bank C に資料の表 4 を参考にして文字が表示できるようサンプルプロ
グラムと同じ設定にした.
その他の設定
3.1.4
¨
§
[11]
¥
BG_PALETTE_SUB[255] = RGB15(31,31,31);
¦
これは色の設定である.ニンテンドーDSには色を格納しておくパレットがたくさんあ
り,サブ画面のパレットは BG PALETTE SUB という 1 次元配列になっている.文字は
標準で 255 番目のパレットで描画されるため,このパレットに使いたい色を設定すればそ
の色で文字が書ける.RGB15 は 15bit の RGB カラーであることを指し,各色 5bit で指
定することを意味している.ここでは白に設定したいので RGB15(31, 31, 31) としてい
る.
²
¯
[13]
±
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31),
(u16*)CHAR_BASE_BLOCK_SUB(0), 16);
文字を書くためのコンソールを初期化している.
Â
[17]
[18]
[19]
[20]
Á
¿
touchPosition touchXY = touchReadXY();
printf(" x1b[10;0H");
printf("Touch x = %d
\n", touchXY.px);
printf("Touch y = %d
\n", touchXY.py);
À
17 行目の touchPosition でタッチした場所の座標を読み込んで TouchXY に格納して
いる.touchXY.px はタッチした地点の x 座標,touchXY.py には y 座標を格納している.
文字の出力は以上の画面設定さえしておけば printf をそのまま使うことができる.
3.2
画像を表示させる
文字を表示することができるようになったので今
度は画像を表示させてみることにした.
ニンテンドーDSに画像を表示するには,png ファ
イルから .o ファイルと .h ファイルに変換させる必
要がある.これは devkitPro にある,grit という
ツールを使用する.
grit とは GBA Raster Image Transmogrifier の
6
図 5: skyImg.png
°
図 4: demo.nds
略で, Raster Image とは,ドットの集まりによる
無圧縮画像のことである.
表示させる画像を Windows に付属しているペイ
ントと,Adobe の Photoshop を使用し,作成した
(図 5).サイズはニンテンドーDSの画面いっぱいの 256px × 192px である.この画像
を上画面に表示する.
プログラムの構造は以下の通りにする.
sky
・Makefile
・source
・sky.cpp
・data
・skyImg.png
・skyImg.grit
skyImg.png のファイル名は最初 sky.png にしていたが,コンパイルがうまくいかなかっ
ため,ファイル名の途中に大文字を入れた.原因は謎だが大文字を入れないといけないよ
うなので以後そうする.
grit ファイルは自分で作成する.中身は以下の通りである.
7
$
'
/*
[ 1]
[ 2]
[ 3]
[ 4]
[ 5]
[ 6]
[ 7]
[ 8]
skyImg.grit
*/
# disable alpha and set opaque bit for all pixels
-gT!
# 16 bit bitmap
-gB16
# bitmap format
-gb
&
%
これで skyImg.png を 16 色のビットマップイメージに変換する設定をしている.このファ
イルも Makefile 同様使いまわせるが grit ファイルは画像を使う分だけ必要で,ファイル
名は png ファイルと同じにしなければならない.
作成したプログラムは 6.2.2 sky.cpp である.
画像を表示するにあたり,ヘッダファイルに以下のファイルを追加した.先ほど述べた
ように grit というツールによって生成されたファイルである.
¨
§
¥
[ 2] #include "skyImg.h"
¦
skyImg.h の中身を見てみると
²
¯
#define skyImgBitmapLen 98304
extern const unsigned int skyImgBitmap[24576];
±
°
とあり, int サイズの 24576 個のビットマップ配列になっていることがわかる.配列の
名前は skyImgBitmap である.このように変換した画像は次のように画面に表示する.
'
[
[
[
[
[
$
4] void skyImg(){
5] for(int i=0;i<256*192;i++){
6]
((u16*)BG_BMP_RAM(0))[i] = ((u16*)skyImgBitmap)[i] | BIT(15);
7]
}
8] }
&
ニンテンドーDSの画面の大きさは 256 × 192 で,画面いっぱいに画像を表示するの
で for 文を使って配列の中身を順番に表示させる.
次にニンテンドーDSのソフトウェア開発で一番大変な画面の設定である.
º
[11]
[12]
[13]
¹
·
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE);
videoSetModeSub(0);
vramSetMainBankA(VRAM_A_MAIN_BG_0x06000000);
8
¸
%
videoSetMode ではメイン画面(上画面)を 資料の表 1 を参考に MODE5 の BG3 を
ACTIVE にして Extended モードに設定している.このプログラムでは画像を一枚表示す
るだけなので, Extended モードを一枚使用するだけである.よって vramSetMainBank
の設定は資料の表 2 を参考にして番地 0x0600000000 を設定する.下画面は使用しないの
でサブ画面の設定はなしにしてある.
次に指定した VRAM にどのようなことを書きこむかの指示を以下のように書く.
'
[15]
[16]
[17]
[18]
[19]
[20]
[21]
&
$
BG3_CR=BG_BMP16_256x256 | BG_BMP_BASE(0);
BG3_YDX = 0;
BG3_XDX = 1 << 8;
BG3_XDY = 0;
BG3_YDY = 1 << 8;
BG3_CX = 0;
BG3_CY = 0;
%
grit の設定で画像を 16px のビットマップにしているため,ここでも同じように設定す
る.15 行目の BG BMP BASE(0) は 32 ある BASE の中から一番最初の BASE を使う
ことを指示している.16∼21 行目では,画像の回転・拡大・縮小・スクロールなどの設定
ができる.ここでは等倍の画像をそのまま表示するよう設定している.
これらの設定が終わったら 4 行目に書いた関数を実行すれば上画面に画像が表示される
(図 6).
図 6: sky.nds
9
3.3
3.3.1
スプライトを使ってみる
スプライトとは
スプライトとは画像処理ソフトウェアなどによくあ
る「レイヤー」のような表現ができるもので,画像を
他の画像の上に重ねて表示させたり,別々に動作させ
たりすることができる.例えば図 7 のように猫の画像
と車の画像を用意し,重ねて表示することで「車に乗っ
ている猫」を表現することができる.もちろんそれぞ
れの画像を単体で猫や車としても利用できる.このよ
うにいくつかの画像を組み合わせて新しい画像を作る
図 7: スプライト
ことができるので,より少ない枚数の画像でプログラ
ムを組むことができる.こうしてファイルサイズをよ
り小さくできることがスプライトの利点でる.キャラクターが別々の動きをしたり,たく
さんの画像を使ってファイルサイズが大きくなりがちなゲームにとってスプライトはなく
てはならない存在である.
図 9: appleImg.png
図 8: treeImg.png
3.3.2
とりあえず動かしてみる
下画面に木を表示させ,その上に重ねてりんごをスプライトで表示させる.木 [図 8] や
りんご [図 9] は例によってペイントや Photoshop で作成した.木は sky.png と同じように
256px × 192px で,りんごは赤いりんごと黄色いりんごそれぞれ 32px × 32px で 32px
× 64px に設定してある.木の背景の前に赤いりんごと黄色いりんごをスプライトを使っ
て表示させる.
プログラムの構造は次のようにする.
10
apple
・Makefile
・source
・apple.cpp
・data
・appleImg.png
・appleImg.grit
・treeImg.png
・treeImg.grit
画像を表示させるプログラム sky では grit ファイルでビットマップイメージを作成し
たが,ここではスプライト用のファイル変換しなければならない.よって appleImg.png
に対応する grit ファイルを次のように書きかえる.
'
$
/* appleImg.grit */
[ 1] # disable alpha and set opaque bit for all pixels
[ 2] #-gT!
[ 3]
[ 4] # 16 bit bitmap
[ 5] -gB8
[ 6]
[ 7] # bitmap format
[ 8] -gt
[ 9]
[10] # transparent white
[11] -gTFF00FF
&
%
スプライトでは画像を重ねて表示させるので透明色を設定しなければならない.ニンテ
ンドーDSに表示される画像はビットマップイメージで,透明色がないからである.透明
色にする色を画像に使われていない色に設定し,その色で画像の透過したい部分を塗りつ
ぶしておく.そしてその色を grit ファイルで透明に設定する.このファイルでは 10・11
行目の transparent white でで RGB カラーの FF00FF(16 進数) が透明色になって表示
されるよう設定しており,りんご [図??] のまわりの部分が透過され見えないようになる.
この grit ファイルで作成されたヘッダファイルには次のように書かれている.
$
'
#define appleImgTilesLen 2048
extern const unsigned int appleImgTiles[512];
#define appleImgPalLen 512
extern const unsigned short appleImgPal[256];
&
%
今回は grit ファイルを画像をスプライトとして使用できる設定にしているので appleImgTiles と appleImgPal という配列が生成された.このスプライトを表示するにはこ
11
れらの配列を表示させれば良い.
作成したプログラムは資料の 6.2.3 apple.cpp である.
'
[
[
[
[
[
[
11]
12]
13]
14]
15]
16]
$
SpriteEntry sprites[128];
pSpriteRotation spriteRotations=(pSpriteRotation)sprites;
void copySprites(void){
swiCopy(appleImgPal, SPRITE_PALETTE, 256);
swiCopy(appleImgTiles, SPRITE_GFX, 32*32*12/2);
}
&
%
スプライトを使うには OAM(Object Attribute Memory) というメモリにスプライトに
ついての指定をする.スプライトの数は全部で 128 個あり,1 次元の構造体の配列になっ
ていて,128 個全てに表示するものや動きを別々に指定することができる.
$
'
[
[
[
[
[
[
[
[
17] void initSprites(void){
18]
for(int i=0; i<128; i++){
19]
sprites[i].attribute[0] = ATTR0_DISABLED;
10]
sprites[i].attribute[1] = 0;
21]
sprites[i].attribute[2] = 0;
22]
sprites[i].attribute[3] = 0;
23]
}
24] }
&
%
これがスプライトの構造体の中身である.これは OAM のの構造と一致させている.こ
のまま OAM にコピーすればスプライトとして表示させることができる.当たり前のこと
だがスプライトの配列はメモリに何かが格納されていれば,それを表示してしまうので,
initSprites というこの関数でそれを防ぐため初期化をしておく.
Â
[
[
[
[
¿
25] void updateOAM(void){
26]
DC_FlushRange(sprites, 128*sizeof(SpriteEntry));
27]
dmaCopy(sprites, OAM, 128*sizeof(SpriteEntry));
28] }
Á
À
updateOAM は OAM にスプライトのデータをコピーする関数である.27 行目の OAM
はメイン画面のスプライトを管理するメモリの番地で 0X07000000 である.サブ画面のス
プライトを管理するときには OAM SUB とする.
12
$
'
[
[
[
[
[
[
[
30]
31]
32]
33]
34]
35]
36]
enum apple{RED=0, YELLOW=32};
typedef struct tAppleColor{
int x, y, px, py, tileNumber;
bool visible;
}AppleColor;
AppleColor appleColor[20];
AppleColor* appleField[10][10];
&
%
AppleColor はスプライトとして表示するりんごの構造体である.スプライトを格納す
る OAM と同じ構造にしてあるので AppleColor の必要な項目だけをそのまま OAM に
反映すればスプライトとして表示されるようになっている.構造体 AppleColor の内容は,
りんごを表示する場所の配列番号 (x,y),りんごを表示する座標 (px,py),りんごの色
(tileNumber),りんごがあるかないか (visible) である.このプログラムではりんごを 20
個置きたいので,appleColor(OAM に反映する配列を利用しやすくするために作成した
sprites のミラー配列) を 20 までにしている.りんごを置く場所は appleField という 2 次
元配列で表現していて,大きさは 10 × 10 に設定している.ニンテンドーDSの実際の画
面は横長なため,この配列のうち一番上と一番下の行を除いた 8 × 10 の部分を使用する.
13
$
'
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
37] void initializeAppleField(){
38]
int i, j;
39]
for(i=0;i<10;i++){
40]
for(j=0;j<10;j++){
41]
appleField[i][j]=NULL;
42]
}
43]
}
44]
appleColor[0].visible=false;
45]
for(i=0;i<10;i++){
46]
AppleColor apple;
47]
apple.visible=true;
48]
apple.x=i+1;
49]
apple.px=i*24;
50]
51]
apple.tileNumber=RED;
52]
apple.y=1;
53]
apple.py=0;
54]
appleColor[i+1]=apple;
55]
appleField[apple.x][apple.y]=&appleColor[i+1];
56]
57]
apple.tileNumber=YELLOW;
58]
apple.y=2;
59]
apple.py=1*24;
60]
appleColor[i+10+1]=apple;
61]
appleField[apple.x][apple.y]=&appleColor[i+10+1];
62]
}
63] }
&
%
initializeAppleField はりんごを配置する関数である.この関数でりんごを並べる配列
appleField の具体的な内容を指定する.赤いりんごは画面の一番上の行(y=1),黄色い
りんごは赤いりんごのすぐ下(y=2)に横一列に並ぶよう設定している.りんごを置く場
所は 2 次元配列だが,スプライトの配列は 1 次元なので,それを考慮して,1 行目の赤い
りんご(計 10 個)のすぐ後に 2 行目の黄色いりんごを 36 行目の appleColor(スプライ
トにそのままコピーする 1 次元配列)に格納していく.
14
$
'
[
[
[
[
[
[
[
[
[
[
[
[
65] void updateAppleColorOAM(){
66]
for(int i=0;i<33;i++){
67]
AppleColor apple=appleColor[i];
68]
if(apple.visible){
69]
sprites[i].attribute[0]=ATTR0_COLOR_256|OBJ_Y(apple.py);
70]
sprites[i].attribute[1]=ATTR1_SIZE_32|OBJ_X(apple.px);
71]
sprites[i].attribute[2]=apple.tileNumber;
72]
}
73]
else sprites[i].attribute[0]=ATTR0_DISABLED;
74]
}
75]
updateOAM();
76] }
&
%
initializeAppleField() の内容を OAM にコピーする.りんごがある場所には関数の内
容をコピーし,それ以外には何も表示させないよう初期化する.
これでスプライトに関する準備は終わりである.
'
[ 80]
[ 81]
[ 82]
$
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE |
DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D_LAYOUT);
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
vramSetMainBanks(VRAM_A_MAIN_BG_0x06000000,
VRAM_B_MAIN_SPRITE_0x06400000,
VRAM_C_LCD, VRAM_D_LCD);
&
%
画面の設定には sky.cpp の画像を表示する設定にスプライトを表示するための設定を追加
しなければならない.videosSetMode には Extended モードを設定し,スプライトの表示を
ACTIVE にした.VRAM の設定は VRAM を複数使用するので vramSetMainBanks で設
定する.これをつかえば A∼D までの VRAM の設定が一度にできる.今回は VRAM A
をバックグラウンド (木の画像) 用に,VRAM B をスプライト用に設定する.VRAM C
と VRAM D は使用しない.
あとは作った関数を順番に実行する.
'
[
[
[
[
[
92]
93]
94]
95]
96]
&
$
treeImg();
initSprites();
copySprites();
initializeAppleField();
updateAppleColorOAM();
%
15
最初に背景の画像を表示させる.次にスプライトを全て非表示にしておく.スプライト
のデータをコピーして,画面にりんごを並べればプログラムは完成である [図??].
図 10: apple.nds
3.4
ゲーム完成
これまでに紹介した技術を使って木になったりんごをかごにドラッグ&ドロップして入
れるゲームを作成する.プログラムを実行すれば木とかごが表示され,木に重なってりん
ごが 10 個 ランダムに表示される.りんごは赤色と黄色があり,赤いりんごをかごの上に
ドラッグ&ドロップすると 100 点,黄色いりんごをかごの上にドラッグ&ドロップすると
-100 点が加算される.りんごは表示されてからしばらく経ったら消え,また新たなりんご
が表示される.制限時間は 15 秒とする.
プログラムの構造は次のようにする.
game
・Makefile
・source
・game.cpp
・data
・appleImg.png
・appleImg.grit
・treeImg.png
・treeImg.grit
16
画像は apple.cpp とほぼ同じである.
作成したプログラムは資料 6.2.4 game.cpp である.
²
[
[
±
¯
3] #include <stdlib.h>
4] #include <time.h>
°
乱数や時間の設定を使用するのでヘッダファイル stdlib.h や time.h で,rand や time
を使えるようにしておく.これは C++ 言語と同じである.
²
[
[
±
¯
8] int hours, seconds, minutes, count=0, yellow=0, point=0;
9] time t unixTime = time(NULL);
°
このプログラムでは制限時間やりんごが表示されている時間など時間を扱う部分がたく
さんある.全体を通して時間を管理する必要がある.これらをグローバル変数を用いて管
理する.hours は時間 seconds は分 minutes は秒をそれぞれ格納するために使用する.9
行目ので格納するための時間を読み込んでいる.他のグローバル変数は,count は表示さ
れているりんごの総数 yellow は表示されている黄色いりんごの個数,point はポイントを
それぞれ格納する.
$
'
[
[
[
[
[
[
[
[
11] int timeCount(){
12]
unixTime = time(NULL);
13]
struct tm* timeStruct=gmtime((const time_t *)&unixTime);
14]
hours=timeStruct->tm hour;
15]
minutes=timeStruct->tm min;
16]
seconds=timeStruct->tm sec;
17]
return hours*3600+minutes*60+seconds;
18] }
&
%
この関数では読み込んだ時間を時間・分・秒のそれぞれのグローバル変数に格納し,秒
単位に直す関数である.時間には構造体が用いられており,それぞれ別々に格納されてい
るのでそのままコピーすれば良い.
Â
[
[
[
[
¿
40] typedef struct tAppleColor{
41]
int x, y, px, py, tileNumber, timestart, timeend, time;
42]
bool visible,red;
43] }AppleColor;
Á
À
AppleColor には新たに時間を格納する変数を追加する.timestart は初めて表示され
た時間,timeend は経過時間を計算するために使用,time は経過時間を格納する.
17
$
'
[ 53]
・
・
・
[ 97]
[ 98]
[ 99]
・
・
・
[114]
[115]
&
void initializeAppleField(){
}
void initializeAppleFieldStart(){
initializeAppleField();
}
%
initializeAppleField は apple.cpp とほとんど同じである.追加した機能としては乱数
で表示されるところをランダムにしている.また,何度もりんごを表示させなければなら
ないので初期設定がふくまれていて起動したときのみ実行する initializeAppleFieldStart
とただりんごを並べるだけの initializeAppleField とに分けている.りんごは木の上にだ
け表示されて欲しいので 46 行目の fieldcheck で木の上であるかをチェックしている.こ
こでの表現は C++ と同様なので詳しい説明は省略する.
'
$
[132] bool kagoCheck(int x, int y){
[133]
if(x>=8&&x<=10&&y>=4&&y<=10){
[134]
return true;
[135]
}
[136]
else{
[137]
return false;
[138]
}
[139] }
&
%
かごの上であるかをチェックする関数が上である.かごの上にあれば true を返し,そ
うでなければ false を返す.そして,以下の appleMove でかごの上にドラッグ&ドロップ
されたりんごを消す.
18
'
$
&
%
[141] void appleMove(AppleColor* apple, int x, int y) {
[142]
if(apple->red){
[143]
point=point+100;
[144]
}
[145]
else{
[146]
point=point-100;
[147]
yellow--;
[148]
}
[149]
appleField[apple->x][apple->y] = NULL;
[150]
apple->visible=false;
[151]
count--;
[152] }
もし赤いりんごがかごに入れられれば +100 点,黄色いりんごならば -100 点をポイン
ト用の変数 point に加算する.りんごを消したら同時にりんごの数も一つ減らしておくこ
とに注意する.
'
$
[154] void appleCheck(AppleColor* apple){
[155]
int time=0, start=0, end=0;
[156]
if(apple->visible){
[157]
end=timeCount();
[158]
start=apple->timestart;
[159]
time=end-start;
[160]
apple->timeend=end;
[161]
apple->time=time;
[162]
if(time>=3){
[163]
apple->visible=false;
[164]
apple->timestart=0;
[165]
apple->timeend=0;
[166]
apple->time=0;
[167]
count--;
[168]
if(!(apple->red)){
[169]
yellow--;
[170]
}
[171]
}
[172]
}
[173] }
&
%
これは 3 秒以上表示されていたりんごを消す関数である.私はこの関数に一番手間取っ
た.ここで timeCount を利用して再度時間を読み,timestart(そのりんごが初めて表示
された時間)との差をとる.その差が 3 秒以上ならばそのりんごを消す.このときりんご
19
の数である count や yellow を消すことも忘れてはならない.そして,りんごを消したら
構造体を初期化しておく.
これで新しく作成した関数の説明は終わりである.
今回は main 文もより複雑になる.
º
[182]
[183]
[184]
¹
·
powerON(POWER_ALL_2D);
irqInit();
irqSet(IRQ_VBLANK, 0);
¸
スプライトをいつでもドラッグ&ドロップできるよう割り込みを許可する.これはその
設定である.割り込みを許可しないとドラッグ&ドロップなどの動作がうまくいかない.
割り込みをさせることで実現する.
'
[186]
[187]
[188]
[189]
&
$
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE |
DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D_LAYOUT);
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
vramSetMainBanks(VRAM_A_MAIN_BG_0x06000000,
VRAM_B_MAIN_SPRITE_0x06400000,
VRAM_C_SUB_BG, VRAM_D_LCD);
lcdMainOnBottom();
%
画面やメモリには今まで紹介してきた全ての設定を適用する.メイン画面には背景画像
とスプライトの表示のため,サブ画面には文字を表示する設定をする.そして,タッチパ
ネルは下画面にしかないので,メイン画面を下にしておく.VRAM では,A に背景画
像,B にスプライト,C に文字の設定をし,D は使用しない.
画面の設定が済んだら,ドラッグ&ドロップのための構造体を作成する.
'
[210]
[211]
[212]
[213]
[214]
[215]
&
$
bool isDrag=false;
struct Position{
int x, y;
} dragPosition;
dragPosition.x=0;
dragPosition.y=0;
%
この構造体はドラッグ&ドロップしているりんごを一時的に表示させるために使用する
ので,AppleColor と同じ構造にしておかなければならない.最初はなにも表示しないの
で初期化しておく.
ここまでが準備である.
20
$
'
[217]
[218]
[219]
[220]
[221]
[222]
[223]
[224]
[225]
[226]
&
while(1){
te=timeCount();
totaltime=te-ts;
printf("\x1b[10;0H");
printf(" %i:%i:%i\n", hours, minutes, seconds);
printf(" time %5d second\n", totaltime);
printf(" point %5d\n", point);
if(totaltime>=15){
break;
}
%
上の画面に現在の時間と経過時間,得点の文字を表示させる.文字は中央に時間が変わ
るたびに上書きされるようになっている.時間はスプライトと同じように管理し,15 秒経
つと while 文が break され,繰り返しを終了する.
'
[228]
[229]
[230]
[231]
[232]
[233]
[234]
[235]
[236]
[237]
[238]
[239]
[240]
[241]
[242]
[243]
[244]
&
$
scanKeys();
if(!isDrag){
if(keysDown()&KEY_TOUCH){
for(i=0;i<100;i++){
AppleColor apple=appleColor[i];
if(apple.visible&&(apple.px<=t.px)&&(t.px<=apple.px+24)
&&(apple.py<=t.py)&&(t.py<=apple.py+24)){
isDrag=true;
appleColor[0]=apple;
target=i;
appleColor[i].visible=false;
dragPosition.x=t.px;
dragPosition.y=t.py;
break;
}
}
}
}
ドラッグ&ドロップの操作をしたときの動作を指定する.もし,ドラッグ&ドロップし
たところにりんごがあったら,211 行目で作った Position にコピーして,スプライトの 0
番として最前面に表示,タチペンを放すまでタッチしているところにりんごを表示する.
そして,元あったりんごは非表示にしておく.
21
%
'
else {
AppleColor* apple=&appleColor[target];
if(keysUp()&KEY_TOUCH){
isDrag=false;
appleColor[0].visible=false;
apple->visible=true;
x=(appleColor[0].px+12)/24+1;
y=(appleColor[0].py+12)/24+1;
if(kagoCheck(x, y)){
appleMove(apple, x, y);
}
}
[245]
[246]
[247]
[248]
[249]
[250]
[251]
[252]
[253]
[254]
[255]
[256]
&
$
%
ドラッグ&ドロップでタッチペンを画面から離したら,その座標はかごの上であるかそ
うでないかを KagoCheck を利用して判断する.もしかごの上であれば appleMove を実
行してりんごを消し,ポイントの計算をする.もしかごの上でなければもとの場所に戻す.
º
·
[266]
[267]
[268]
if(count<8){
initializeAppleField();
}
¹
¸
りんごをたくさん採るとりんごがなくなってしまうので,ある程度減ったら(ここでは
7 個以下)追加でりんごを表示させる.
º
[269]
[270]
[271]
¹
·
swiWaitForVBlank();
updateAppleColorOAM();
}
¸
そして,割り込み待機状態にして割り込みを待つ.また下画面にタッチをするとりんご
があるかないかのチェックからりんごの移動までを繰り返す.
これでプログラムの完成である(11). これらの設定が終わったら 4 行目に書いた関
数 updateAppleColorOAM を実行すれば上画面に画像が表示される(図 6).
$
'
[272]
[273]
[274]
[275]
&
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31),
(u16*)CHAR_BASE_BLOCK_SUB(0),16);
printf("\x1b[10;0H");
printf(" Total Point %5d\n", point);
printf("\n\n\n game over\n");
%
while 文が終了して,ゲームが終わったら,最後にトータルポイントとゲームオーバー
のメッセージを表示する.このとき,ゲーム中に表示していた文字の上に上書きするので,
22
再度コンソールを初期化してから文字を表示する.
これでゲームは完成である.
図 11: game.nds
4
4.1
終わりに
まとめ
ニンテンドーDSとパソコンとは CPU が違うため特別な開発環境が必要であった.こ
のようにパソコン以外のハード上で作成したプログラムを動作させるにはそれ専用の開発
環境が必要であることが本研究によってわかった.そして,ニンテンドーDSで採用され
ている ARM アーキテクチャの構造や nds.h によってインクルードされる様々な機能が
ほんの少し理解できた.これらの理解にはまだまだ時間がかかりそうである.
最後の game.cpp は未だ未完成である.予定では最後に時間の制限をつけて完成であっ
たが,time 関数がうまく動作せず機能を追加することができなかった.ニンテンドーD
Sには他の機能を使って時間を取得しなければならないのかも知れないがそれは現在調査
中である.
23
5
参考文献
参考文献
[1] 任天堂
http://www.nintendo.co.jp/
[2] ARM アーキテクチャ-Wikipedia
http://ja.wikipedia.org/wiki/ARM アーキテクチャ
[3] ニンテンドーDSでプログラミング
http://void-main.org/dsprogram/
[4] MinGW
http://www.mingw.org/
[5] devkitPro
http://www.devkitpro.org/
[6] M3REAL
http://www.m3flash.jp/M3realpd.htm
[7] NO$GBA
http://nocash.emubase.de/gba.htm
[8] NDS/Tutorias Day-Scence
http://www.dev-scene.com/NDS/Tutorials Day 3
[9] VRAM - Wikipedia
http://ja.wikipedia.org/wiki/VRAM
24
6
資料
6.1
6.1.1
表
videoSetMode
Mode
Mode 0
Mode 1
Mode 2
Mode 3
Mode 4
Mode 5
Mode 6
Frame Buffer
Main 2D Engine
BG0
BG1
BG2
BG3
Text/3D Text
Text
Text
Text/3D Text
Text
Rotation
Text/3D Text
Rotation
Rotation
Text/3D Text
Text
Extended
Text/3D Text
Rotation
Extended
Text/3D Text
Extended
Extended
3D
Large Bitmap
Direct VRAM display as a bitmap
Sub 2D Engine
Mode
Mode 0
Mode 1
Mode 2
Mode 3
Mode 4
Mode 5
BG0
Text
Text
Text
Text
Text
Text
BG1
Text
Text
Text
Text
Text
Text
BG2
Text
Text
Rotation
Text
Rotation
Extended
表 1: videoSetMode[8]
i
BG3
Text
Rotation
Rotation
Extended
Extended
Extended
6.1.2
VRAM Bank
Where they can be mapped
libnds Defines
VRAM Bank A (VRAM A) 128KB
0x06800000 (Unmapped)
0x06800000
0x06820000
0x06840000
0x06860000
(3D
(3D
(3D
(3D
texture
texture
texture
texture
0x06000000
0x06020000
0x06040000
0x06060000
(Main
(Main
(Main
(Main
slot
slot
slot
slot
VRAM A LCD (unmapped)
0)
1)
2)
3)
Background)
Background)
Background)
Background)
0x06400000 (Main Sprite Graphics)
0x06420000 (Main Sprite Graphics)
VRAM A TEXTURE (maps to slot 0)
VRAM A TEXTURE SLOT0
VRAM A TEXTURE SLOT1
VRAM A TEXTURE SLOT1
VRAM A TEXTURE SLOT3
VRAM A MAIN BG (maps to 0x6000000)
VRAM A MAIN BG 0x06000000
VRAM A MAIN BG 0x06020000
VRAM A MAIN BG 0x06040000
VRAM A MAIN BG 0x06060000
VRAM A MAIN SPRITE (maps to 0x6400000)
VRAM A MAIN SPRITE 0x06400000
VRAM A MAIN SPRITE 0x06420000
表 2: VRAM Bank A[8]
ii
Where they can be mapped
libnds Defines
VRAM Bank B (VRAM B) 128KB
0x06820000 (Unmapped)
0x06800000
0x06820000
0x06840000
0x06860000
(3D
(3D
(3D
(3D
texture
texture
texture
texture
0x06000000
0x06020000
0x06040000
0x06060000
(Main
(Main
(Main
(Main
VRAM B LCD (unmapped)
slot
slot
slot
slot
0)
1)
2)
3)
Background)
Background)
Background)
Background)
0x06400000 (Main Sprite Graphics)
0x06420000 (Main Sprite Graphics)
VRAM B TEXTURE (maps to slot 0)
VRAM B TEXTURE SLOT0
VRAM B TEXTURE SLOT1
VRAM B TEXTURE SLOT1
VRAM B TEXTURE SLOT3
VRAM B MAIN BG (maps to 0x6020000)
VRAM B MAIN BG 0x06000000
VRAM B MAIN BG 0x06020000
VRAM B MAIN BG 0x06040000
VRAM B MAIN BG 0x06060000
VRAM B MAIN SPRITE (maps to 0x6420000)
VRAM B MAIN SPRITE 0x06400000
VRAM B MAIN SPRITE 0x06420000
表 3: VRAM Bank B[8]
iii
Where they can be mapped
libnds Defines
VRAM Bank C (VRAM C) 128KB
0x06840000 (Unmapped)
0x06800000
0x06820000
0x06840000
0x06860000
(3D
(3D
(3D
(3D
texture
texture
texture
texture
0x06000000
0x06020000
0x06040000
0x06060000
(Main
(Main
(Main
(Main
slot
slot
slot
slot
VRAM C LCD (unmapped)
0)
1)
2)
3)
Background)
Background)
Background)
Background)
VRAM C TEXTURE (maps to slot 2)
VRAM C TEXTURE SLOT0
VRAM C TEXTURE SLOT1
VRAM C TEXTURE SLOT1
VRAM C TEXTURE SLOT3
0x06200000 (Sub Background)
VRAM C MAIN BG (maps to 0x6040000)
VRAM C MAIN BG 0x06000000
VRAM C MAIN BG 0x06020000
VRAM C MAIN BG 0x06040000
VRAM C MAIN BG 0x06060000
0x06000000 (ARM7 work memory)
0x06020000 (ARM7 work memory)
VRAM C SUB BG (maps to 0x6200000)
VRAM C SUB BG 0x06200000
VRAM C ARM7
VRAM C ARM7 0x06000000
VRAM C ARM7 0x06020000
表 4: VRAM Bank C[8]
iv
Where they can be mapped
libnds Defines
VRAM Bank D (VRAM D) 128KB
0x06820000 (Unmapped)
0x06800000
0x06820000
0x06840000
0x06860000
(3D
(3D
(3D
(3D
texture
texture
texture
texture
0x06000000
0x06020000
0x06040000
0x06060000
(Main
(Main
(Main
(Main
slot
slot
slot
slot
VRAM D LCD (unmapped)
0)
1)
2)
3)
Background)
Background)
Background)
Background)
0x06600000 (Sub Sprite Graphics)
VRAM D TEXTURE (maps to slot 0)
VRAM D TEXTURE SLOT0
VRAM D TEXTURE SLOT1
VRAM D TEXTURE SLOT1
VRAM D TEXTURE SLOT3
VRAM D MAIN BG (maps to 0x6060000)
VRAM D MAIN BG 0x06000000
VRAM D MAIN BG 0x06020000
VRAM D MAIN BG 0x06040000
VRAM D MAIN BG 0x06060000
0x06000000 (ARM7 work memory)
0x06020000 (ARM7 work memory)
VRAM D SUB SPRITE
VRAM D ARM7 (maps to 0x6060000)
VRAM D ARM7 0x06000000
VRAM D ARM7 0x06020000
表 5: VRAM Bank D[8]
v
6.2
プログラム
6.2.1
/*
[ 1]
[ 2]
[ 3]
[ 4]
[ 5]
[ 6]
[ 7]
[ 8]
[ 9]
[10]
[11]
[12]
[13]
demo.cpp
demo.cpp
*/
#include <nds.h>
#include <stdio.h>
int main(void)
{
videoSetMode(0);
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
vramSetBankC(VRAM_C_SUB_BG);
SUB_BG0_CR = BG_MAP_BASE(31);
BG_PALETTE_SUB[255] = RGB15(31,31,31);
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31),
(u16*)CHAR_BASE_BLOCK_SUB(0), 16);
[14]
[15]
[16]
[17]
[18]
[19]
[20]
[21]
[22]
[23] }
printf("\n\n\tHello World!\n");
while(1){
touchPosition touchXY = touchReadXY();
printf("\ x1b[10;0H");
printf("Touch x = %d \n", touchXY.px);
printf("Touch y = %d \n", touchXY.py);
}
return 0;
6.2.2
sky.cpp
/*
[ 1]
[ 2]
[ 3]
[ 4]
[ 5]
[ 6]
[ 7]
[ 8]
[ 9]
sky.cpp
*/
#include <nds.h>
#include "skyImg.h"
void skyImg(){
for(int i=0;i<256*192;i++){
((u16*)BG_BMP_RAM(0))[i] = ((u16*)skyImgBitmap)[i] | BIT(15);
}
}
vi
[10] int main(void){
[11]
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE);
[12]
videoSetModeSub(0);
[13]
vramSetMainBankA(VRAM_A_MAIN_BG_0x06000000);
[14]
[15]
BG3_CR=BG_BMP16_256x256 | BG_BMP_BASE(0);
[16]
BG3_YDX = 0;
[17]
BG3_XDX = 1 << 8;
[18]
BG3_XDY = 0;
[19]
BG3_YDY = 1 << 8;
[20]
BG3_CX = 0;
[21]
BG3_CY = 0;
[22]
[23]
skyImg();
[24]
return 0;
[25] }
6.2.3
apple.cpp
/* apple.cpp */
[ 1] #include <nds.h>
[ 2] #include "appleImg.h"
[ 3] #include "treeImg.h"
[ 4]
[ 5] void treeImg(){
[ 6] for(int i=0;i<256*192;i++){
[ 7]
((u16*)BG_BMP_RAM(0))[i] = ((u16*)treeImgBitmap)[i] | BIT(15);
[ 8]
}
[ 9] }
[ 10]
[ 11] SpriteEntry sprites[128];
[ 12] pSpriteRotation spriteRotations=(pSpriteRotation)sprites;
[ 13] void copySprites(void){
[ 14]
swiCopy(appleImgPal, SPRITE_PALETTE, 256);
[ 15]
swiCopy(appleImgTiles, SPRITE_GFX, 32*32*12/2);
[ 16] }
[ 17] void initSprites(void){
[ 18]
for(int i=0; i<128; i++){
[ 19]
sprites[i].attribute[0] = ATTR0_DISABLED;
[ 10]
sprites[i].attribute[1] = 0;
[ 21]
sprites[i].attribute[2] = 0;
[ 22]
sprites[i].attribute[3] = 0;
vii
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
23]
24]
25]
26]
27]
28]
29]
30]
31]
32]
33]
34]
35]
36]
37]
38]
39]
40]
41]
42]
43]
44]
45]
46]
47]
48]
49]
50]
51]
52]
53]
54]
55]
56]
57]
58]
59]
60]
61]
62]
63]
}
}
void updateOAM(void){
DC_FlushRange(sprites, 128*sizeof(SpriteEntry));
dmaCopy(sprites, OAM, 128*sizeof(SpriteEntry));
}
enum apple{RED=0, YELLOW=32};
typedef struct tAppleColor{
int x, y, px, py, tileNumber;
bool visible;
}AppleColor;
AppleColor appleColor[20];
AppleColor* appleField[10][10];
void initializeAppleField(){
int i, j;
for(i=0;i<10;i++){
for(j=0;j<10;j++){
appleField[i][j]=NULL;
}
}
appleColor[0].visible=false;
for(i=0;i<10;i++){
AppleColor apple;
apple.visible=true;
apple.x=i+1;
apple.px=i*24;
apple.tileNumber=RED;
apple.y=1;
apple.py=0;
appleColor[i+1]=apple;
appleField[apple.x][apple.y]=&appleColor[i+1];
apple.tileNumber=YELLOW;
apple.y=2;
apple.py=1*24;
appleColor[i+10+1]=apple;
appleField[apple.x][apple.y]=&appleColor[i+10+1];
}
}
viii
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
64]
65] void updateAppleColorOAM(){
66]
for(int i=0;i<33;i++){
67]
AppleColor apple=appleColor[i];
68]
if(apple.visible){
69]
sprites[i].attribute[0]=ATTR0_COLOR_256|OBJ_Y(apple.py);
70]
sprites[i].attribute[1]=ATTR1_SIZE_32|OBJ_X(apple.px);
71]
sprites[i].attribute[2]=apple.tileNumber;
72]
}
73]
else sprites[i].attribute[0]=ATTR0_DISABLED;
74]
}
75]
updateOAM();
76] }
77]
78]
79] int main(void){
80]
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE |
DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D_LAYOUT);
81]
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
82]
vramSetMainBanks(VRAM_A_MAIN_BG_0x06000000,
VRAM_B_MAIN_SPRITE_0x06400000,
VRAM_C_LCD, VRAM_D_LCD);
83]
lcdMainOnBottom();
84]
BG3_CR=BG_BMP16_256x256;
85]
BG3_YDX = 0;
86]
BG3_XDX = 1 << 8;
87]
BG3_XDY = 0;
88]
BG3_YDY = 1 << 8;
89]
BG3_CX = 0;
90]
BG3_CY = 0;
91]
92]
treeImg();
93]
initSprites();
94]
copySprites();
95]
initializeAppleField();
96]
updateAppleColorOAM();
97]
return 0;
98] }
6.2.4
game.cpp
/* game.cpp */
ix
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
1] #include <stdio.h>
2] #include <nds.h>
3] #include <stdlib.h>
4] #include <time.h>
5] #include "treeImg.h"
6] #include "appleImg.h"
7]
8] int hours, seconds, minutes, count=0, yellow=0, point=0;
9] time_t unixTime = time(NULL);
10]
11] int timeCount(){
12]
unixTime = time(NULL);
13]
struct tm* timeStruct=gmtime((const time_t *)&unixTime);
14]
hours=timeStruct->tm_hour;
15]
minutes=timeStruct->tm_min;
16]
seconds=timeStruct->tm_sec;
17]
return hours*3600+minutes*60+seconds;
18] }
19]
20] SpriteEntry sprites[128];
21] pSpriteRotation spriteRotations=(pSpriteRotation)sprites;
22] void copySprites(void){
23]
swiCopy(appleImgPal, SPRITE_PALETTE, 256);
24]
swiCopy(appleImgTiles, SPRITE_GFX, 32*32*12/2);
25] }
26] void initSprites(void) {
27]
for(int i=0;i<128;i++) {
28]
sprites[i].attribute[0]=ATTR0_DISABLED;
29]
sprites[i].attribute[1]=0;
30]
sprites[i].attribute[2]=0;
31]
sprites[i].attribute[3]=0;
32]
}
33] }
34] void updateOAM(void) {
35]
DC_FlushRange(sprites, 128*sizeof(SpriteEntry));
36]
dmaCopy(sprites, OAM, 128*sizeof(SpriteEntry));
37]}
38]
39] enum apple{RED=0, YELLOW=32};
40] typedef struct tAppleColor{
41]
int x, y, px, py, tileNumber, timestart, timeend, time;
x
[
[
[
[
[
[
42]
43]
44]
45]
46]
47]
bool visible,red;
}AppleColor;
AppleColor appleColor[100];
AppleColor* appleField[10][10];
bool fieldcheck(int x,int y){
if((y==0&&x==3)||(y==1&&x>=1&&x<=4)||(y==2&&x>=1&&x<=5)||
(y>=3&&y<=4&&x<=5)||(y>=5&&y<=6&&x>=1&&x<=4)){
return true;
}
else
return false;
}
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
[
48]
49]
50]
51]
52]
53] void initializeAppleField(){
54]
int i, j, n, m, color;
55]
bool check;
56]
srand((unsigned) time(NULL));
57]
while(1){
58]
n=rand()%10;
59]
m=rand()%10;
60]
if(n1==n&&n2==n){
61]
srand((unsigned) time(NULL));
62]
}
63]
check=fieldcheck(n,m);
64]
if(appleField[n*24][m*24]!=NULL){
65]
check=false;
66]
}
67]
AppleColor apple;
68]
69]
if(check){
70]
apple.visible=true;
71]
count++;
72]
apple.timestart=timeCount();
73]
apple.x=n+1;
74]
apple.y=m+1;
75]
apple.px=n*24;
76]
apple.py=m*24;
77]
color=rand()%5;
78]
if(color==0&&yellow<3){
79]
apple.red=false;
80]
apple.tileNumber=YELLOW;
81]
yellow++;
xi
[ 82]
[ 83]
[ 84]
[ 85]
[ 86]
[ 87]
[ 88]
[ 89]
[ 90]
[ 91]
[ 92]
[ 93]
[ 94]
[ 95]
[ 96]
[ 97]
[ 98]
[ 99]
[100]
[101]
[102]
[103]
[104]
[105]
[106]
[107]
[108]
[109]
[110]
[111]
[112]
[113]
[114]
[115]
[116]
[117]
[118]
[119]
[120]
[121]
[122]
}
else{
apple.red=true;
apple.tileNumber=RED;
}
for(i=1;i<=10;i++){
if(!(appleColor[i].visible)){
break;
}
}
appleColor[i]=apple;
appleField[n*24][m*24]=&appleColor[i];
}
if(count>=10)
break;
}
}
void initializeAppleFieldStart(){
int i, j, n, m, color;
bool check;
srand((unsigned) time(NULL));
for(i=0;i<10;i++){
for(j=0;j<10;j++){
appleField[i][j]=NULL;
}
}
for(i=0;i<100;i++){
appleColor[i].timestart=0;
appleColor[i].timeend=0;
appleColor[i].time=0;
appleColor[i].visible=false;
}
initializeAppleField();
}
void updateAppleColorOAM(){
for(int i=0;i<100;i++){
AppleColor apple=appleColor[i];
if(apple.visible) {
sprites[i].attribute[0]=ATTR0_COLOR_256|OBJ_Y(apple.py);
sprites[i].attribute[1]=ATTR1_SIZE_32|OBJ_X(apple.px);
xii
[123]
[124]
[125]
[126]
[127]
[128]
[129]
[130]
[131]
[132]
[133]
[134]
[135]
[136]
[137]
[138]
[139]
[140]
[141]
[142]
[143]
[144]
[145]
[146]
[147]
[148]
[149]
[150]
[151]
[152]
[153]
[154]
[155]
[156]
[157]
[158]
[159]
[160]
[161]
[162]
[163]
sprites[i].attribute[2]=apple.tileNumber;
}
else{
sprites[i].attribute[0]=ATTR0_DISABLED;
}
}
updateOAM();
}
bool kagoCheck(int x, int y){
if(x>=8&&x<=10&&y>=4&&y<=10){
return true;
}
else{
return false;
}
}
void appleMove(AppleColor* apple, int x, int y) {
if(apple->red){
point=point+100;
}
else{
point=point-100;
yellow--;
}
appleField[apple->x][apple->y] = NULL;
apple->visible=false;
count--;
}
void appleCheck(AppleColor* apple){
int time=0, start=0, end=0;
if(apple->visible){
end=timeCount();
start=apple->timestart;
time=end-start;
apple->timeend=end;
apple->time=time;
if(time>=3){
apple->visible=false;
xiii
[164]
[165]
[166]
[167]
[168]
[169]
[170]
[171]
[172]
[173]
[174]
[175]
[176]
[177]
[178]
[179]
[180]
[181]
[182]
[183]
[184]
[185]
[186]
[187]
[188]
[189]
[190]
[191]
[192]
[193]
[194]
[195]
[196]
[197]
[198]
[199]
[200]
apple->timestart=0;
apple->timeend=0;
apple->time=0;
count--;
if(!(apple->red)){
yellow--;
}
}
}
}
void treeImg(){
for(int i=0;i<256*192;i++){
((u16*)BG_BMP_RAM(0))[i] = ((u16*)treeImgBitmap)[i] | BIT(15);
}
}
int main(void){
powerON(POWER_ALL_2D);
irqInit();
irqSet(IRQ_VBLANK, 0);
videoSetMode(MODE_5_2D | DISPLAY_BG3_ACTIVE |
DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D_LAYOUT);
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE);
vramSetMainBanks(VRAM_A_MAIN_BG_0x06000000,
VRAM_B_MAIN_SPRITE_0x06400000,
VRAM_C_SUB_BG, VRAM_D_LCD);
lcdMainOnBottom();
SUB_BG0_CR = BG_MAP_BASE(31);
BG_PALETTE_SUB[255]=RGB15(31, 31, 31);
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31),
(u16*)CHAR_BASE_BLOCK_SUB(0),16);
BG3_CR=BG_BMP16_256x256;
BG3_YDX=0;
BG3_XDX=1<<8;
BG3_XDY=0;
BG3_YDY=1<<8;
BG3_CX=0;
BG3_CY=0;
xiv
[201]
[202]
[203]
[204]
[205]
[206]
[207]
[208]
[209]
[210]
[211]
[212]
[213]
[214]
[215]
[216]
[217]
[218]
[219]
[220]
[221]
[222]
[223]
[224]
[225]
[226]
[227]
[228]
[229]
[230]
[231]
[232]
[233]
[234]
[235]
[236]
[237]
[238]
[239]
[240]
treeImg();
initSprites();
copySprites();
initializeAppleFieldStart();
updateAppleColorOAM();
int x, y, i, j, target=0, totaltime=0, ts=0, te=0;
ts=timeCount();
bool isDrag=false;
struct Position{
int x, y;
} dragPosition;
dragPosition.x=0;
dragPosition.y=0;
while(1){
te=timeCount();
totaltime=te-ts;
printf("\x1b[10;0H");
printf(" %i:%i:%i\n", hours, minutes, seconds);
printf(" time %5d second\n", totaltime);
printf(" point %5d\n", point);
if(totaltime>=15){
break;
}
touchPosition t=touchReadXY();
scanKeys();
if(!isDrag){
if(keysDown()&KEY_TOUCH){
for(i=0;i<100;i++){
AppleColor apple=appleColor[i];
if(apple.visible&&(apple.px<=t.px)&&(t.px<=apple.px+24)
&&(apple.py<=t.py)&&(t.py<=apple.py+24)){
isDrag=true;
appleColor[0]=apple;
target=i;
appleColor[i].visible=false;
dragPosition.x=t.px;
dragPosition.y=t.py;
break;
xv
[241]
[242]
[243]
[244]
[245]
[246]
[247]
[248]
[249]
[250]
[251]
[252]
[253]
[254]
[255]
[256]
[257]
[258]
[259]
[260]
[261]
[262]
[263]
[264]
[265]
[266]
[267]
[268]
[269]
[270]
[271]
[272]
[273]
[274]
[275]
[276]
[277] }
}
}
}
}
else {
AppleColor* apple=&appleColor[target];
if(keysUp()&KEY_TOUCH){
isDrag=false;
appleColor[0].visible=false;
apple->visible=true;
x=(appleColor[0].px+12)/24+1;
y=(appleColor[0].py+12)/24+1;
if(kagoCheck(x, y)){
appleMove(apple, x, y);
}
}
else{
appleColor[0].px=apple->px+t.px-dragPosition.x;
appleColor[0].py=apple->py+t.py-dragPosition.y;
}
}
for(i=1;i<100;i++){
AppleColor* apple=&appleColor[i];
appleCheck(apple);
}
if(count<8){
initializeAppleField();
}
swiWaitForVBlank();
updateAppleColorOAM();
}
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31),
(u16*)CHAR_BASE_BLOCK_SUB(0),16);
printf("\x1b[10;0H");
printf(" Total Point %5d\n", point);
printf("\n\n\n game over\n");
return 0;
xvi
Fly UP