9
|
1 \chapter{テストに用いるゲームプログラム Super Dandy}
|
6
|
2
|
9
|
3 \section{テストプログラムに最適なシューティングゲーム}\label{sec:dandy}
|
6
|
4 Super Dandy は我々が PlayStation でのゲーム開発を行っていた 1998 年に
|
|
5 開発されたシューティングゲームである。PlayStation アーキテクチャの
|
|
6 スプライト描画機能を用いて宇宙空間を表現しており、タイトルからゲーム本編中の
|
|
7 敵機の登場、攻略後のスコア一覧、エンディングなどのシーン切り替え、4 種類の
|
|
8 射撃と 2 種類の特殊射撃を駆使し、ステージをクリアしていくなど、ゲーム性も
|
|
9 高い。SuperDandy は開発する環境が変わる度に移植されており、過去には
|
|
10 PlayStation2 Linux、OpenGL バージョンも作られた。(図\ref{fig:dandy})
|
|
11
|
|
12 \begin{figure}[hb]
|
|
13 \begin{center}
|
|
14 \includegraphics[scale=0.4]{images/dandy.pdf}
|
|
15 \end{center}
|
|
16 \caption{Super Dandy}
|
|
17 \label{fig:dandy}
|
|
18 \end{figure}
|
|
19
|
|
20 \newpage
|
|
21
|
|
22 Super Dandy が伝統的に移植されてきた背景には、ある程度のボリュームのある
|
|
23 ゲームであること、衝突判定やオブジェクト管理、ステージクリアによる
|
|
24 シーン切り替えなど、基本的なゲームとしての要素が入っていること、
|
|
25 そして動作結果を過去の環境と比較することで新たな環境のチューニングが行える
|
|
26 ことが挙げられる。
|
|
27
|
|
28 \section{Task Dandy(Super Dandy Task version)}\label{sec:taskdandy}
|
|
29 本研究を進めるにあたり、Super Dandy を Cerium の Task で書き換えた
|
|
30 Task Dandy を作成した。Task Dandy はできるだけ元の Super Dandy のコード
|
|
31 やデータ構造を流用し、比較、テストが容易に行えるように設計した。
|
9
|
32 その為、Super Dandy で Move や Collision の処理を行う state\_update() や
|
|
33 collision\_detect() において Move Task や Collision Task を生成している。
|
|
34 また、obj\_draw() はオブジェクトの描画を行う関数であったが、Task Dandy では
|
|
35 SceneGraph の tree を生成している。そしてゲームの処理を抜け、Cerium の処理に
|
|
36 入ると、さきほど生成した SceneGraph の tree から描画処理を行う 3 つの Task
|
|
37 を生成する(\ref{sec:rendering})。この一連の処理を繰り返すことによって
|
|
38 シューティングゲームである Task Dandy が形成される。(図\ref{fig:taskdandy})
|
6
|
39
|
9
|
40 \begin{figure}[h]
|
|
41 \begin{center}
|
|
42 \includegraphics[scale=0.5]{images/taskdandy.pdf}
|
|
43 \end{center}
|
|
44 \caption{Super Dandy と Task Dandy の処理}
|
|
45 \label{fig:taskdandy}
|
|
46 \end{figure}
|
|
47
|
|
48 \newpage
|
|
49
|
|
50 \subsection{Super Dandy のデータ構造}
|
6
|
51 データ構造は Super Dandy のものを流用している。Super Dandy では主に以下の
|
|
52 ようなデータが存在する。
|
|
53
|
|
54 \begin{itemize}
|
|
55 \item player:\\
|
|
56 プレイヤーの操作する機体。xy 座標の他、残機数、無敵時間、
|
|
57 コンテニュー回数などが存在する。
|
|
58 \item CHARACTER:\\
|
|
59 敵キャラクターや敵の弾。xy 座標とその方向の速さ、体力、倒したときのスコア、
|
|
60 オブジェクトの種類を表すキャラナンバー、死亡フラグなどがある。
|
|
61 また、Move と Collision を関数ポインタで持ち、ステートパターンで
|
|
62 切り替えて状態遷移する。
|
|
63 \item tama\_lv1〜laser\_lv3:\\
|
|
64 プレイヤーが撃った弾。xy 座標と存在の有無を表すフラグを持っている。
|
|
65 プレイヤーが射撃ボタンを押すと対応した弾の配列に状態が格納され、
|
|
66 敵に当たるか、画面外に消えるまで存在フラグが立つ。
|
|
67 \end{itemize}
|
|
68
|
7
|
69 これらのデータは オブジェクトの情報として管理されるだけでなく、
|
|
70 その他のオブジェクトの移動や衝突判定時にも使用される。
|
|
71
|
9
|
72 \subsection{データ転送に用いる Property}\label{sec:property}
|
7
|
73 Task Dandy の Task は処理のために、複数のデータを set\_inData する
|
|
74 必要がある。特に Collision Task に使用するデータはオブジェクト自身の情報の
|
|
75 他にプレイヤーの機体、プレイヤーの出した弾など、種類が多く、全てを set\_in
|
|
76 Data するとコードが無駄に長く、煩雑になってしまう。そこで必要なパラメータを
|
|
77 Property という構造体にコピーする。こうすることで多くのパラメータを 1回の
|
|
78 set\_inData で送ることが出来る。(図\ref{fig:property})
|
|
79
|
|
80 \begin{figure}[h]
|
|
81 \begin{center}
|
|
82 \includegraphics[scale=0.7]{images/property.pdf}
|
|
83 \end{center}
|
|
84 \caption{Property のデータ構造}
|
|
85 \label{fig:property}
|
|
86 \end{figure}
|
6
|
87
|
|
88 \subsection{SPE における状態遷移}
|
|
89 SPE では各々に固有の LS を持つ\ref{sec:spe}為、Super Dandy で使用していた
|
|
90 ステートパターンによる状態遷移は使用できない。これは関数ポインタに格納されて
|
|
91 いるアドレスが PPE 上のものであり、SPE では意味を為さないからである。
|
|
92
|
|
93 そこで SPE 上では Task の ID を変更することによりオブジェクトの状態遷移を
|
|
94 実現するようにした。CHARACTER 構造体に Task ID を格納する新たなパラメータを
|
|
95 追加した。以下のようなコードで状態が遷移する条件に入ると Task ID が
|
|
96 書き換えられる。
|
|
97
|
9
|
98 \newpage
|
|
99
|
6
|
100 \begin{verbatim}
|
|
101 static int
|
|
102 state6(SchedTask *smanager, void *rbuf, void *wbuf)
|
|
103 {
|
|
104 CHARACTER *p = (CHARACTER*)smanager->get_input(rbuf, 0);
|
|
105 CHARACTER *q = (CHARACTER*)smanager->get_output(wbuf, 0);
|
|
106 player *jiki = (player*)smanager->get_input(rbuf, 1);
|
|
107
|
|
108 p->y += p->vy;
|
|
109 p->x += p->vx;
|
|
110 if(p->y + 96 < jiki->y
|
|
111 && p->y + 128 > jiki->y)
|
|
112 {
|
|
113 p->vy = 2;
|
|
114 p->vx = ((jiki->x > p->x) ? 4 : -4);
|
|
115 p->state_task = STATE0;
|
|
116 }
|
|
117 else p->state_task = STATE6;
|
|
118
|
|
119 *q = *p;
|
|
120 return 0;
|
|
121 }
|
|
122 \end{verbatim}
|
|
123
|
9
|
124 \newpage
|
|
125
|
6
|
126 書き換えられた ID は次に Task を生成する際に使用され、別の種類の Task を
|
|
127 生成するようになる。
|
|
128
|
|
129 \begin{verbatim}
|
|
130 int task_num = p->state_task;
|
|
131 HTaskPtr state_task = tmanager->create_task(task_num);
|
|
132 \end{verbatim}
|
|
133
|
|
134 \subsection{SPE におけるオブジェクトの生成}
|
|
135 Super Dandy では敵オブジェクトが弾丸を作り出し、プレイヤーを攻撃する、
|
|
136 といったイベントが存在する。SPE に送られた Task 内でこのイベントが発生した時
|
|
137 、SPE の LS 内で弾丸オブジェクトの生成が行われる。しかし、Cerium の Rendering
|
|
138 Engine はPPE 上の SceneGraph tree に登録されているオブジェクトを見て描画用
|
|
139 Task を生成するので(\ref{sec:rendering} 節)、このままでは弾丸オブジェクトは
|
|
140 描画されない。また、複数の SPE 上の Task からこのオブジェクトのデータを参照
|
|
141 したい時、データを同期するためにも 1 箇所のメモリでオブジェクトを管理する方が
|
|
142 良い。よって SPE 内で生成されたオブジェクトデータを DMA 転送により
|
|
143 メインメモリへオブジェクトデータを送る必要がある。
|
|
144
|
|
145 Cerium は set\_outData と get\_output により、Task からデータを書き出すこと
|
|
146 ができるが、書き出すサイズと数が決め打ちである。例えば以下のコードでは
|
|
147 Puttama で弾丸オブジェクトを生成しているが、条件によって 0〜3 個の
|
|
148 弾丸オブジェクトが生成される為、オブジェクトの最大数分だけサイズをセット
|
|
149 しなければならない。これによって、余計な DMA 転送が発生する。
|
|
150
|
|
151 \begin{verbatim}
|
|
152 if((p->dt1 > 60) && (p->dt1 <= 70))
|
|
153 {
|
|
154 if(p->dt1 % 2 == 1)
|
|
155 {
|
|
156 // Puttama は弾丸オブジェクトを生成する
|
|
157 Puttama(0, rinkx - 16, rinky);
|
|
158 Puttama(0, rinkx, rinky);
|
|
159 Puttama(0, rinkx + 16, rinky);
|
|
160 }
|
|
161 }
|
|
162 if((p->dt1 > 180) && (p->dt1 <= 240))
|
|
163 {
|
|
164 if(p->dt1 % 2 == 1)
|
|
165 {
|
|
166 rinkf2 = 1;
|
|
167 Puttama(2, rinkx - 16, p->y - 32);
|
|
168 Puttama(3, rinkx + 32 - 16, p->y - 32);
|
|
169 }
|
|
170 else
|
|
171 {
|
|
172 rinkf2 = 2;
|
|
173 }
|
|
174 }
|
|
175 \end{verbatim}
|
|
176
|
|
177 その為、Task Dandy の Task 内では set\_outputSize (\ref{sec:refine_wbuf})に
|
|
178 よって write buffer の大きさを再定義している。これにより無駄な DMA 転送は
|
|
179 抑えることができるが、メインメモリ上には予めオブジェクトの最大数分のメモリを
|
|
180 確保しておく必要がある。
|
|
181
|
|
182 \begin{verbatim}
|
|
183 int obj_size = sizeof(ObjContainer)*DATA_LENGTH;
|
|
184 HTaskPtr state_task = manager->create_task(task_id);
|
|
185 ObjContainerPtr obj = (ObjContainerPtr)tmanager->allocate(obj_size);
|
|
186
|
|
187 state_task->set_outData(0, obj, 0);
|
|
188 \end{verbatim}
|
|
189
|
|
190 \subsection{可変長な Output Data の定義}\label{sec:refine_wbuf}
|
|
191 図 \ref{fig:wbuf} にあるように write buffer の allocate は Task の実行前に
|
|
192 行われており、また DMA 転送により書き出されるサイズは事前に set\_outData で
|
|
193 指定したサイズとなるため、Task 内で書き出すデータサイズを変更することは
|
|
194 出来なかった。
|
|
195
|
|
196 そこで新たに{\bf set\_outputSize(int index, int size) } という API を
|
|
197 実装した。index にはサイズを変更したいバッファの番号を入れ、size には新たに
|
|
198 設定するバッファサイズを入れる。
|
|
199
|
7
|
200 write buffer は Task 実行前に allocate されるが、
|
6
|
201 Task 内で set\_outputSize をすることで set\_outData で設定されたサイズを書き
|
|
202 換える。そして事前に allocate された write buffer を free し、新たに設定
|
|
203 されたサイズで write buffer を allocate することで可変長な output Data を
|
7
|
204 定義している。(図\ref{fig:set_w2})
|
|
205 \if0
|
6
|
206 \begin{figure}[h]
|
|
207 \begin{center}
|
|
208 \includegraphics[scale=0.7]{images/set_wbuf1.pdf}
|
|
209 \end{center}
|
|
210 \caption{Task 実行前の allocate}
|
|
211 \label{fig:set_w1}
|
|
212 \end{figure}
|
7
|
213 \fi
|
6
|
214
|
|
215 \begin{figure}[h]
|
|
216 \begin{center}
|
|
217 \includegraphics[scale=0.8]{images/set_wbuf2.pdf}
|
|
218 \end{center}
|
|
219 \caption{output Data の再定義}
|
|
220 \label{fig:set_w2}
|
|
221 \end{figure}
|