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
+
Binary file paper/images/pad_buff.pdf has changed
--- /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
+
Binary file paper/images/ppe_random.pdf has changed
--- /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
+
Binary file paper/images/set_wbuf1.pdf has changed
--- /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
+
Binary file paper/images/set_wbuf2.pdf has changed
--- /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
+
Binary file paper/images/spe_random.pdf has changed
--- /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
+
Binary file paper/images/test_log.pdf has changed
--- /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
+
Binary file paper/images/video.pdf has changed
--- /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
+
Binary file paper/images/video_none.pdf has changed
--- /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}
+
+このテストの結果、全てのオブジェクトの初期位置と状態遷移した値が正しいことが
+分かった。しかし、ここで行った単体テストはゲームにおける、ある一瞬の値の正誤
+しか調べることができない。ゲームプログラムは時間の経過と共にオブジェクトの
+パラメータが常に変化するため、こうした一般的な単体テストではゲームのバグを
+発見するのは難しい。