Mercurial > hg > Papers > 2011 > koba-master
view paper/dandy.tex @ 14:19be75493fbb
fix.
author | koba <koba@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 15 Feb 2011 18:04:22 +0900 |
parents | d711f469cdb7 |
children |
line wrap: on
line source
\chapter{テストに用いるゲームプログラム Super Dandy} \section{テストプログラムに最適なシューティングゲーム}\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 のコード やデータ構造を流用し、比較、テストが容易に行えるように設計した。 その為、Super Dandy で Move や Collision の処理を行う state\_update() や collision\_detect() において Move Task や Collision Task を生成している。 また、obj\_draw() はオブジェクトの描画を行う関数であったが、Task Dandy では SceneGraph の tree を生成している。そしてゲームの処理を抜け、Cerium の処理に 入ると、さきほど生成した SceneGraph の tree から描画処理を行う 3 つの Task を生成する(\ref{sec:rendering})。この一連の処理を繰り返すことによって シューティングゲームである Task Dandy が形成される。(図\ref{fig:taskdandy}) \begin{figure}[h] \begin{center} \includegraphics[scale=0.5]{images/taskdandy.pdf} \end{center} \caption{Super Dandy と Task Dandy の処理} \label{fig:taskdandy} \end{figure} \newpage \subsection{Super Dandy のデータ構造} データ構造は Super Dandy のものを流用している。Super Dandy では主に以下の ようなデータが存在する。 \begin{itemize} \item player:\\ プレイヤーの操作する機体。xy 座標の他、残機数、無敵時間、 コンテニュー回数などが存在する。 \item CHARACTER:\\ 敵キャラクターや敵の弾。xy 座標とその方向の速さ、体力、倒したときのスコア、 オブジェクトの種類を表すキャラナンバー、死亡フラグなどがある。 また、Move と Collision を関数ポインタで持ち、ステートパターンで 切り替えて状態遷移する。 \item tama\_lv1〜laser\_lv3:\\ プレイヤーが撃った弾。xy 座標と存在の有無を表すフラグを持っている。 プレイヤーが射撃ボタンを押すと対応した弾の配列に状態が格納され、 敵に当たるか、画面外に消えるまで存在フラグが立つ。 \end{itemize} これらのデータは オブジェクトの情報として管理されるだけでなく、 その他のオブジェクトの移動や衝突判定時にも使用される。 \subsection{データ転送に用いる Property}\label{sec:property} Task Dandy の Task は処理のために、複数のデータを set\_inData する 必要がある。特に Collision Task に使用するデータはオブジェクト自身の情報の 他にプレイヤーの機体、プレイヤーの出した弾など、種類が多く、全てを set\_in Data するとコードが無駄に長く、煩雑になってしまう。そこで必要なパラメータを Property という構造体にコピーする。こうすることで多くのパラメータを 1回の set\_inData で送ることが出来る。(図\ref{fig:property}) \begin{figure}[h] \begin{center} \includegraphics[scale=0.7]{images/property.pdf} \end{center} \caption{Property のデータ構造} \label{fig:property} \end{figure} \subsection{SPE における状態遷移} SPE では各々に固有の LS を持つ\ref{sec:spe}為、Super Dandy で使用していた ステートパターンによる状態遷移は使用できない。これは関数ポインタに格納されて いるアドレスが PPE 上のものであり、SPE では意味を為さないからである。 そこで SPE 上では Task の ID を変更することによりオブジェクトの状態遷移を 実現するようにした。CHARACTER 構造体に Task ID を格納する新たなパラメータを 追加した。以下のようなコードで状態が遷移する条件に入ると Task ID が 書き換えられる。 \newpage \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} \newpage 書き換えられた 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 されるが、 Task 内で set\_outputSize をすることで set\_outData で設定されたサイズを書き 換える。そして事前に allocate された write buffer を free し、新たに設定 されたサイズで write buffer を allocate することで可変長な output Data を 定義している。(図\ref{fig:set_w2}) \if0 \begin{figure}[h] \begin{center} \includegraphics[scale=0.7]{images/set_wbuf1.pdf} \end{center} \caption{Task 実行前の allocate} \label{fig:set_w1} \end{figure} \fi \begin{figure}[h] \begin{center} \includegraphics[scale=0.8]{images/set_wbuf2.pdf} \end{center} \caption{output Data の再定義} \label{fig:set_w2} \end{figure}