Mercurial > hg > Papers > 2011 > koba-master
changeset 6:f59edc38c858
add graphic files.
author | koba <koba@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Sun, 06 Feb 2011 01:58:33 +0900 |
parents | f515d7e7e4df |
children | 2dcc784d62e0 |
files | paper/dandy.tex paper/images/pad_buff.bb paper/images/pad_buff.pdf paper/images/ppe_random.bb paper/images/ppe_random.pdf paper/images/set_wbuf1.bb paper/images/set_wbuf1.pdf paper/images/set_wbuf2.bb paper/images/set_wbuf2.pdf paper/images/spe_random.bb paper/images/spe_random.pdf paper/images/test_log.bb paper/images/test_log.pdf paper/images/video.bb paper/images/video.pdf paper/images/video_none.bb paper/images/video_none.pdf paper/unittest.tex |
diffstat | 18 files changed, 314 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/dandy.tex Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,184 @@ +\chapter{Super Dandy} + +\section{Super Dandy}\label{sec:dandy} +Super Dandy は我々が PlayStation でのゲーム開発を行っていた 1998 年に +開発されたシューティングゲームである。PlayStation アーキテクチャの +スプライト描画機能を用いて宇宙空間を表現しており、タイトルからゲーム本編中の +敵機の登場、攻略後のスコア一覧、エンディングなどのシーン切り替え、4 種類の +射撃と 2 種類の特殊射撃を駆使し、ステージをクリアしていくなど、ゲーム性も +高い。SuperDandy は開発する環境が変わる度に移植されており、過去には +PlayStation2 Linux、OpenGL バージョンも作られた。(図\ref{fig:dandy}) + +\begin{figure}[hb] +\begin{center} +\includegraphics[scale=0.4]{images/dandy.pdf} +\end{center} +\caption{Super Dandy} +\label{fig:dandy} +\end{figure} + +\newpage + +Super Dandy が伝統的に移植されてきた背景には、ある程度のボリュームのある +ゲームであること、衝突判定やオブジェクト管理、ステージクリアによる +シーン切り替えなど、基本的なゲームとしての要素が入っていること、 +そして動作結果を過去の環境と比較することで新たな環境のチューニングが行える +ことが挙げられる。 + +\section{Task Dandy(Super Dandy Task version)}\label{sec:taskdandy} +本研究を進めるにあたり、Super Dandy を Cerium の Task で書き換えた +Task Dandy を作成した。Task Dandy はできるだけ元の Super Dandy のコード +やデータ構造を流用し、比較、テストが容易に行えるように設計した。 + +\subsection{データ構造} +データ構造は Super Dandy のものを流用している。Super Dandy では主に以下の +ようなデータが存在する。 + +\begin{itemize} +\item player:\\ + プレイヤーの操作する機体。xy 座標の他、残機数、無敵時間、 + コンテニュー回数などが存在する。 +\item CHARACTER:\\ + 敵キャラクターや敵の弾。xy 座標とその方向の速さ、体力、倒したときのスコア、 + オブジェクトの種類を表すキャラナンバー、死亡フラグなどがある。 + また、Move と Collision を関数ポインタで持ち、ステートパターンで + 切り替えて状態遷移する。 +\item tama\_lv1〜laser\_lv3:\\ + プレイヤーが撃った弾。xy 座標と存在の有無を表すフラグを持っている。 + プレイヤーが射撃ボタンを押すと対応した弾の配列に状態が格納され、 + 敵に当たるか、画面外に消えるまで存在フラグが立つ。 +\end{itemize} + +\subsection{Task Dandy の Task} +Task Dandy では + +\subsection{SPE における状態遷移} +SPE では各々に固有の LS を持つ\ref{sec:spe}為、Super Dandy で使用していた +ステートパターンによる状態遷移は使用できない。これは関数ポインタに格納されて +いるアドレスが PPE 上のものであり、SPE では意味を為さないからである。 + +そこで SPE 上では Task の ID を変更することによりオブジェクトの状態遷移を +実現するようにした。CHARACTER 構造体に Task ID を格納する新たなパラメータを +追加した。以下のようなコードで状態が遷移する条件に入ると Task ID が +書き換えられる。 + +\begin{verbatim} +static int +state6(SchedTask *smanager, void *rbuf, void *wbuf) +{ + CHARACTER *p = (CHARACTER*)smanager->get_input(rbuf, 0); + CHARACTER *q = (CHARACTER*)smanager->get_output(wbuf, 0); + player *jiki = (player*)smanager->get_input(rbuf, 1); + + p->y += p->vy; + p->x += p->vx; + if(p->y + 96 < jiki->y + && p->y + 128 > jiki->y) + { + p->vy = 2; + p->vx = ((jiki->x > p->x) ? 4 : -4); + p->state_task = STATE0; + } + else p->state_task = STATE6; + + *q = *p; + return 0; +} +\end{verbatim} + +書き換えられた ID は次に Task を生成する際に使用され、別の種類の Task を +生成するようになる。 + +\begin{verbatim} + int task_num = p->state_task; + HTaskPtr state_task = tmanager->create_task(task_num); +\end{verbatim} + +\subsection{SPE におけるオブジェクトの生成} +Super Dandy では敵オブジェクトが弾丸を作り出し、プレイヤーを攻撃する、 +といったイベントが存在する。SPE に送られた Task 内でこのイベントが発生した時 +、SPE の LS 内で弾丸オブジェクトの生成が行われる。しかし、Cerium の Rendering + Engine はPPE 上の SceneGraph tree に登録されているオブジェクトを見て描画用 +Task を生成するので(\ref{sec:rendering} 節)、このままでは弾丸オブジェクトは +描画されない。また、複数の SPE 上の Task からこのオブジェクトのデータを参照 +したい時、データを同期するためにも 1 箇所のメモリでオブジェクトを管理する方が +良い。よって SPE 内で生成されたオブジェクトデータを DMA 転送により +メインメモリへオブジェクトデータを送る必要がある。 + +Cerium は set\_outData と get\_output により、Task からデータを書き出すこと +ができるが、書き出すサイズと数が決め打ちである。例えば以下のコードでは +Puttama で弾丸オブジェクトを生成しているが、条件によって 0〜3 個の +弾丸オブジェクトが生成される為、オブジェクトの最大数分だけサイズをセット +しなければならない。これによって、余計な DMA 転送が発生する。 + +\begin{verbatim} + if((p->dt1 > 60) && (p->dt1 <= 70)) + { + if(p->dt1 % 2 == 1) + { + // Puttama は弾丸オブジェクトを生成する + Puttama(0, rinkx - 16, rinky); + Puttama(0, rinkx, rinky); + Puttama(0, rinkx + 16, rinky); + } + } + if((p->dt1 > 180) && (p->dt1 <= 240)) + { + if(p->dt1 % 2 == 1) + { + rinkf2 = 1; + Puttama(2, rinkx - 16, p->y - 32); + Puttama(3, rinkx + 32 - 16, p->y - 32); + } + else + { + rinkf2 = 2; + } + } +\end{verbatim} + +その為、Task Dandy の Task 内では set\_outputSize (\ref{sec:refine_wbuf})に +よって write buffer の大きさを再定義している。これにより無駄な DMA 転送は +抑えることができるが、メインメモリ上には予めオブジェクトの最大数分のメモリを +確保しておく必要がある。 + +\begin{verbatim} + int obj_size = sizeof(ObjContainer)*DATA_LENGTH; + HTaskPtr state_task = manager->create_task(task_id); + ObjContainerPtr obj = (ObjContainerPtr)tmanager->allocate(obj_size); + + state_task->set_outData(0, obj, 0); +\end{verbatim} + +\subsection{可変長な Output Data の定義}\label{sec:refine_wbuf} +図 \ref{fig:wbuf} にあるように write buffer の allocate は Task の実行前に +行われており、また DMA 転送により書き出されるサイズは事前に set\_outData で +指定したサイズとなるため、Task 内で書き出すデータサイズを変更することは +出来なかった。 + +そこで新たに{\bf set\_outputSize(int index, int size) } という API を +実装した。index にはサイズを変更したいバッファの番号を入れ、size には新たに +設定するバッファサイズを入れる。 + +write buffer は Task 実行前に allocate されるが(図\ref{fig:set_w1})、 +Task 内で set\_outputSize をすることで set\_outData で設定されたサイズを書き +換える。そして事前に allocate された write buffer を free し、新たに設定 +されたサイズで write buffer を allocate することで可変長な output Data を +定義している。 +(図\ref{fig:set_w2}) + +\begin{figure}[h] +\begin{center} +\includegraphics[scale=0.7]{images/set_wbuf1.pdf} +\end{center} +\caption{Task 実行前の allocate} +\label{fig:set_w1} +\end{figure} + +\begin{figure}[h] +\begin{center} +\includegraphics[scale=0.8]{images/set_wbuf2.pdf} +\end{center} +\caption{output Data の再定義} +\label{fig:set_w2} +\end{figure}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/pad_buff.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/pad_buff.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 551 177 +%%CreationDate: Sat Feb 5 03:50:56 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/ppe_random.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/ppe_random.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 588 485 +%%CreationDate: Sun Feb 6 00:35:06 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/set_wbuf1.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/set_wbuf1.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 539 571 +%%CreationDate: Sat Feb 5 15:13:35 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/set_wbuf2.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/set_wbuf2.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 555 677 +%%CreationDate: Sat Feb 5 15:13:35 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/spe_random.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/spe_random.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 585 298 +%%CreationDate: Sun Feb 6 00:20:06 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/test_log.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/test_log.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 552 474 +%%CreationDate: Sat Feb 5 17:38:27 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/video.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/video.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 589 594 +%%CreationDate: Sun Feb 6 01:53:12 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/images/video_none.bb Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,5 @@ +%%Title: ./images/video_none.pdf +%%Creator: extractbb 20100328 +%%BoundingBox: 0 0 444 221 +%%CreationDate: Sun Feb 6 01:53:19 2011 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/unittest.tex Sun Feb 06 01:58:33 2011 +0900 @@ -0,0 +1,90 @@ +\chapter{CppUnit による単体テスト} \label{chapter:unittest} +ここでは一般的なテスト駆動開発のフレームワークの例として CppUnit の紹介と、 +過去に CppUnit を用いて行われた Cerium の単体テストについて述べる。 + +\section{CppUnit} +CppUnit は、xUnit と呼ばれる、単体テストを自動化するテスティング +フレームワークの内の一つで C++ 用に開発されている。 +CppUnit の特徴はテストケースを増やすことが容易であり、1 つの事象に対して +様々なテストケースを同時にテストする事ができる。また、羅列したテストケースは +一括で実行、結果の表示ができる。(図\ref{fig:cpptest}) + +\begin{figure}[h] +\includegraphics[scale=0.6]{images/test_example.pdf} +\caption{CppUnitTest} +\label{fig:cpptest} +\end{figure} + +\newpage + +\section{CppUnit によるゲームプログラムの単体テスト} +我々は過去に CppUnit を用いてゲームプログラムの単体テストを行なっている。 +このテストでは Cerium のオブジェクト管理システムである SceneGraph +(\ref{sec:scenegraph}節) を用い、3 つの SceneGraph ノードを持つオブジェクトの +テストを行った。このオブジェクトは本体の他に左右にパーツを 1 つずつ持つ。 +本体を tree の root として左右のパーツがその子供になっている。 +(図\ref{fig:boss1}) + +\begin{figure}[htbp] +\includegraphics[scale=0.8]{images/boss1_SG.pdf} +\caption{boss1} +\label{fig:boss1} +\end{figure} + +この boss1 は右に一定速度で移動し、画面上の適当な位置に来ると State パターン +により左方向への移動に状態が遷移する、簡単なゲームの例題となっている。 + +\begin{verbatim} +static void +boss1_move_right(SceneGraphPtr node, int screen_w, int screen_h) { + node->xyz[0] += node->stack_xyz[0]; + if(node->xyz[0] > screen_w-280) { + node->set_move_collision(boss1_move_left, boss1_collision); + } +} + +static void +boss1_move_left(SceneGraphPtr node, int screen_w, int screen_h) { + node->xyz[0] -= node->stack_xyz[0]; + if(node->xyz[0] < 280) { + node->set_move_collision(boss1_move_right, boss1_collision); + } +} +\end{verbatim} + +このテストでは root のアドレスを取得し、そこから tree を辿って +各オブジェクトの座標を取得し、その初期化が正しいか、状態遷移において正しい +値を保持しているか調べた。 + +\begin{verbatim} +void +sgTest::rootTest() { + test_init(); + + sg_root->print_member(); + CPPUNIT_ASSERT_EQUAL((float)width/2, sg_root->xyz[0]); + CPPUNIT_ASSERT_EQUAL(0.0f, sg_root->xyz[1]); + CPPUNIT_ASSERT_EQUAL(-100.0f, sg_root->xyz[2]); +} + +void +sgTest::childTest() { + while (sg_root) { + if(sg_root->children != NULL) { + sg_root->children->print_member(); +... + sg_root = sg_root->children; + } else if(sg_root->brother != NULL) { + sg_root->brother->print_member(); + CPPUNIT_ASSERT_EQUAL(0.0f, sg_root->brother->xyz[0]); +... + sg_root = sg_root->brother; + } else { + ... +\end{verbatim} + +このテストの結果、全てのオブジェクトの初期位置と状態遷移した値が正しいことが +分かった。しかし、ここで行った単体テストはゲームにおける、ある一瞬の値の正誤 +しか調べることができない。ゲームプログラムは時間の経過と共にオブジェクトの +パラメータが常に変化するため、こうした一般的な単体テストではゲームのバグを +発見するのは難しい。