# HG changeset patch # User koba # Date 1297761662 -32400 # Node ID d39c452010eab780fa03856b2307a78f3ad84b45 # Parent 4524ba5be532641e8ff7f1208873954bcda9640f add poster base. diff -r 4524ba5be532 -r d39c452010ea poster/master-lt.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/master-lt.html Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,873 @@ + + + + + +master_presentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ + +
+ +
+

Game Framework Cerium を用いた
+ ゲームプログラミングにおける
+ テスト手法の提案

+

発表者:小林 佑亮

+

所属:琉球大学 理工学研究科 情報工学専攻 並列信頼研究室

+

指導教員:河野 真治

+
+ +
+

発表内容

+
    +
  1. 序論
  2. +
  3. CppUnit を用いたゲームプログラムテスト
  4. +
  5. ゲームプログラミングにおけるテスト
  6. +
  7. Cell BE と Cerium
  8. +
  9. テスト対象のシューティングゲーム Super Dandy
  10. +
  11. 構築したテスト環境
  12. +
  13. テスト環境によるデバッグと検証
  14. +
  15. まとめ
  16. +
+
+ +
+

研究背景

+
    +
  • 我々は PlayStation3(以下 PS3) 上においてゲーム開発が行えるフレームワーク + Cerium を開発した。
  • +
  • Cerium ではプログラムを Task という単位に分けて管理し、これを PS3 の +アーキテクチャである Cell B.E に渡して並列処理を行う。
  • +
  • シーケンシャルなプログラムを Task に分割して並列実行させても、 +逐次実行させた時と同じ動作をするとは限らない。
  • +
  • オブジェクト同士のデータの同期や、処理の実行順序など、シーケンシャルな +プログラムに比べて、バグを発生させる要因は多い。
  • +
  • また、ゲームプログラムの特徴はプレイヤーの入力やプログラム内にある乱数 +などの非決定的な要素が多いことが挙げられる。
  • +
  • これによってバグの再現性が低下するため、ゲームプログラムのテストは + 一般的なソフトウェアのテストに比べて難しい
  • +
+
+ +
+

研究目的

+
    +
  • 本研究では Task に分割されたゲームプログラムがシーケンシャルなバージョン +と同じ動作である事を確認できるテスト環境の構築を目的とする。
  • +
  • プレイヤーの入力や乱数などの非決定的な要素を固定化し、バグの再現性を +低下させずにテストを行えるようにする。
  • +
  • 動作の同一性を確かめるために必要なパラメータの書き出しを行う
  • +
  • 高速なテストを行う為、テストに影響しない範囲で実行時間が大きい処理を +排除する。
  • +
+
+ +
+

CppUnit

+
    +
  • xUnit と呼ばれる単体テストのためのフレームワークの内の 1 つ
  • +
  • 単体テストとは関数やメソッドなどの比較的小さな単位で行うテストで、 + モジュールへの入力と出力を調べることでそのモジュールが要求された仕様を + 満たしているかをテストする手法
  • +
  • CppUnit は 1 つの事象に対して様々なテストケースを同時にテストできる
  • +
  • 羅列したテストケースは一括で実行と結果表示が出来る
  • +
  • しかしこうした単体テストではゲームプログラムのバグを見つけるのは難しい
  • +
+
+ +
+

ゲームオブジェクトに対するテスト

+ + + + + +
    +
  • 3 つの SceneGraph ノードを持つ
  • +
  • 本体を tree の root として左右のパーツがその子供になっている。
  • +
  • オブジェクトの動き(Move)として左右の平行移動をする
  • +
  • 簡単なゲームの例題
  • +
+
+ +
+

ゲームオブジェクトに対するテスト

+

テスト方法

+
    +
  • SceneGraph tree の root のアドレスを取得
  • +
  • そこから tree を辿って各オブジェクトの座標を取得
  • +
  • 初期値を入力してオブジェクトの初期化が正しいか調べる
  • +
+

テスト結果

+
    +
  • 全てのオブジェクトの初期値が正しい事がわかった
  • +
  • 初期化の段階でバグが発生していないことが保証された
  • +
+
+ +
+

ゲームに対する単体テストの欠点

+
    +
  • 単体テストは瞬間的な値の正しさは調べられる
  • +
  • 常にオブジェクトのパラメータが変化するゲームには有効的ではない
  • +
  • ゲームのバグは他のオブジェクトのパラメータとの関係により発生するものが +多い
  • +
  • 一般的な単体テストではゲームのバグの発見は難しい
  • +
+
+ +
+

ゲームプログラムの特徴

+ + + + + +
    +
  • 多数のオブジェクトが存在する
  • +
  • オブジェクト同士が相互に干渉する
  • +
  • プレイヤーの入力やゲームの進行によって新たなオブジェクトが生成される
  • +
  • オブジェクトは他のオブジェクトのパラメータを見て、衝突判定や挙動の変化をする
  • +
+ +
+
+ +
+

ゲームプログラムの特徴

+
    +
  • プレイヤーの入力がゲームに影響する
  • +
  • 遷移する状態が膨大
  • +
  • 遷移する状態が仕様の範囲内に収まるかをチェックするテストは向かない
  • +
  • 実際にプレイヤーがゲームをプレイするのが重要なテスト
  • +
+
+ +
+

プレイヤーの入力の不定性

+
    +
  • プレイヤーの入力は常に非決定的(毎回結果が異なる)
  • +
  • 同じ人間が同じゲームの同じ場面をプレイしても全く同じ入力をする可能性 + は極めて低い
  • +
  • プレイヤーは制御不能なランダム要素
  • +
  • テストにおけるバグの再現性を低下させている
  • +
+
+ +
+

ゲームにおける乱数の役割

+
    +
  • オブジェクトの振る舞いに多様性を持たせる
  • +
  • ランダムな位置配置に使われる
  • +
  • 乱数のランダム性はデバッグをする上でバグの再現を困難にする
  • +
  • 対処法としては、乱数生成器を無効にするか、定数でシードする
  • +
+
+ +
+

Cell Broadband Engine

+
    +
  • SCE と 東芝、IBM によって開発されたCPU
  • +
  • 2 thread の PPE(PowerPC Processor Element) と 8 個の + SPE(Synergistic Processor Element)を持つ
  • +
  • 各 CPU 間は高速リングバスであるEIB(Element Interface Bus)で + 繋がっている
  • +
+
+ +
+
+ +
+

Game Framework Cerium

+
+
SceneGraph
+
オブジェクトのパラメータやポリゴン情報を tree 構造のノードで管理
+
Rendering Engine
+
3 種類の Task によって並列に描画処理を行う
+
TaskManager
+
Task を動的に SPE へ割り振るカーネルとして振舞う
+
+
+ +
+

Task Manager

+
    +
  • Task と呼ばれる分割されたプログラムを管理する
  • +
  • Task の単位はサブルーチンまたは関数とする。
  • +
  • 生成された Task を依存関係を考慮しながら SPE に転送したり、実行する
  • +
+
+ +
+

Task 生成時に使用できる API

+
+ + + + + + +
create_taskTask を生成する
set_inDataTask への入力データのアドレスを追加
set_outDataTask からの出力先アドレスを追加
set_paramTask に 32 bit の情報を追加
set_postTask が終了した後に PPE 側で実行される関数を登録
+
+
+ +
+

シューティングゲーム SuperDandy

+ + + + + +
    +
  • 我々が PlayStation 上でのゲーム開発を行っていた 1998 年に開発
  • +
  • タイトルからゲーム本編中の敵機の登場、ステージクリア、エンディングと + ゲーム的な要素が多い
  • +
  • PlayStation, PlayStation2 Linux, OpenGL と伝統的に移植されてきた
  • +
+ +
+
+ +
+

Super Dandy 移植の利点

+
    +
  • 全 5 ステージという、ある程度のボリュームのあるゲーム
  • +
  • 衝突判定やオブジェクト判定、ステージクリアによるシーン切り替えと、基本的なゲームの要素が入っている
  • +
  • 動作結果を過去の環境と比較することによる新たな環境のチューニングができる
  • +
+
+ +
+

Super Dandy Cerium version

+
    +
  • 最初に Cerium に対応したバージョン
  • +
  • 描画処理に Cerium の Rendering Engine を用いており、その箇所のみ + Task で処理される
  • +
  • 基本的なゲームの処理は変わらない
  • +
+
+ +
+

Task Dandy(Super Dandy Task version)

+ + + + + +
    +
  • オブジェクトの Move や Collision を Task 化
  • +
  • オブジェクトの描画は SceneGraph tree の形成、Rendering Task の + 生成といった Cerium の描画処理を使用
  • +
  • できるだけ Super Dandy のコードやデータ構造を流用
  • +
+ +
+
+ +
+

Task Dandy のデータ構造

+
+
player
+
プレイヤーの操作する機体。xy 座標、残機数、無敵時間、 + コンテニュー回数などを持つ。
+
CHARACTER
+
敵機や敵の弾。xy 座標とその方向の速さ、体力、倒したときのスコア、 + オブジェクトの種類を表すキャラナンバーを持つ。
+
tama_lv1〜lv3、laser_lv1〜lv3
+
プレイヤーが撃った弾。xy 座標をもつ。プレイヤーが射撃ボタンを押すと + 弾が配列に格納され、敵に当たるか画面外にいくと消滅する。
+
+
+ +
+

データ転送に用いる Property

+ + + + + +
    +
  • 必要なパラメータをまとめて Property にコピーして set_inData
  • +
  • 複数のデータをまとめることによって Task の inData を簡略化
  • +
+ +
+
+ +
+

ステートパターン

+ + + + + +
    +
  • オブジェクトの Move と Collision を行う
  • +
  • オブジェクトが関数ポインタを持ち、そのポインタが示す関数が + Move や Collision の処理を行う
  • +
  • 関数ポインタのアドレスを他の関数ポインタのアドレスに書き換え、 + オブジェクトの状態遷移をする
  • +
  • しかし、メモリが独立している SPE 上で状態遷移をする場合、 + ステートパターンで使用した関数ポインタのアドレスは使えない
  • +
+ +
+
+ +
+

SPE における状態遷移

+ + + + + +
    +
  • SPE 上では Task の ID を変更
  • +
  • PPE 上で変更された ID を見て次に生成する Task の種類を変更
  • +
  • オブジェクトの状態遷移が成立
  • +
+ +
+
+ +
+

目標とするテスト環境

+
    +
  • プレイヤーの入力や乱数などの非決定的な要素の固定化
  • +
  • 現在動作中の OpenGL バージョンと Cerium バージョン、そして Task Dandy + の動作が同一かどうか確認できるテストログの出力
  • +
  • 高速なテスト環境
  • +
+
+ +
+

Capture モードと Trace モード

+
    +
  • プレイヤーからの入力を 1 フレーム毎に記録する
  • +
  • 記録した入力をバイナリデータとして書き出す
  • +
  • 書き出したファイルを読み込むことで過去のプレイヤー入力を再現できる
  • +
  • 実行ファイルにオプションとファイル名を付けて起動する
  • +
  • 3 つの Super Dandy 全てで使える
  • +
  • 旧バージョンの入力を記録し、新バージョンで読み出すことができる
  • +
  • 入力が同じでも動作が違えばそこにバグが潜んでいると考えられる
  • +
+
+ +
+

入力を記録するバッファのデータ構造

+
    +
  • 単方向リスト型のバッファ
  • +
  • Capture モードではバッファが足りなくなると新たなバッファを確保する
  • +
  • Trace モードでは必要なバッファサイズを計算し、あらかじめバッファを確保しておく
  • +
+
+ +
+
+ +
+

SPE における乱数生成

+
    +
  • シーケンシャルプログラムでは 1 つの乱数列から順番に乱数を取得
  • +
  • Cell における並列プログラムでは各 SPE 内で 独自の乱数列を生成
  • +
  • SPE に送られた Task は SPE 固有の乱数列を使用
  • +
  • SPE 内では依存関係を持たない Task は実行順序が不定
  • +
  • シーケンシャルと並列で異なる結果が出る
  • +
+
+ +
+
+ +
+

SPE 内での予測可能な乱数の使用

+ + + + + +
    +
  • あらかじめ PPE 内で乱数列を生成しておく
  • +
  • inData として Task に渡す
  • +
  • Move Task や Collision Task の生成タイミングは Super Dandy の + Move や Collision のタイミングと同じ
  • +
  • Super Dandy と同じ乱数が使用できる
  • +
+ +
+
+ +
+

並列処理をすることによって発生するバグ

+ + + + + +
    +
  • Task 間のデータの同期による衝突判定のバグ
  • +
  • Task の実行順序の違いによる衝突判定のバグ
  • +
  • Task の実装の違い
  • +
  • 主にオブジェクトの衝突判定でバグが発生
  • +
  • 衝突時のログを見ることでバグを発見する
  • +
+ +
+
+ +
+

出力されるテストログ

+
+F64: CREATE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -128.000000
+                                             vx= 0.000000  vy= 4.000000
+F85: DELETE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -44.000000
+                                             vx= 0.000000  vy= 4.000000
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+
+
F64, F85
+
生成、被弾した時の経過フレーム
+
CREATE, DELETE
+
CREATE はオブジェクトの生成、DELETE はオブジェクトの被弾
+
NAME
+
オブジェクトの種類と ID
+
+
+ +
+

出力されるテストログ

+
+F64: CREATE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -128.000000
+                                             vx= 0.000000  vy= 4.000000
+F85: DELETE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -44.000000
+                                             vx= 0.000000  vy= 4.000000
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+
+
COORD
+
オブジェクトの xy 座標と速度
+
BULLET
+
その瞬間に画面内に存在した弾丸の数。
+
+
+ +
+

Cerium における画面の描画処理

+ + + + + +
    +
  • ビデオモードの選択(SDL, OpenGL)
  • +
  • 描画処理を行う画面バッファの領域の確保
  • +
  • ゲーム処理の実行
  • +
  • レンダリング Task による画面バッファへの描画
  • +
+ +
+
+ +
+

本研究のテスト環境における描画処理

+ + + + + +
    +
  • プレイヤーの入力の自動化により、プログラムを実行するだけでテストが可能
  • +
  • 描画処理が不要となる
  • +
  • 描画用 Task の生成を行わない事により、テストの高速化ができる
  • +
  • また、画面バッファの確保も不要
  • +
+ +
+
+ +
+

描画処理を行わないモード

+
    +
  • ビデオモードの選択時に選ぶ
  • +
  • Task を生成する処理をスルーしてゲーム処理を実行
  • +
+
+ +
+
+ +
+

本研究のテスト環境におけるバグの検出方法

+
    +
  • OpenGL バージョンを Capture モードでプレイし、入力を記録
  • +
  • Cerium バージョン、Task Dandy を Trace モードで実行
  • +
  • 各バージョンで得られたテストログを比較、考察
  • +
  • テストログの違いにより、バグの発生している箇所を特定
  • +
+
+ +
+

OpenGL と Cerium のテストログの比較

+
+ + + + +
大きさ行数単語数
OpenGL349486 byte341137194
Cerium349471 byte341137195
+
+
    +
  • Cerium バージョンは描画を行わないモードで実行
  • +
  • エンディングまでプレイした入力データを仕様
  • +
  • テストログのデータに unix コマンドの wc(word count) コマンドを実行して検証
  • +
  • 各バージョンで得られたテストログを比較、考察
  • +
+
+ +
+

OpenGL と Cerium のテストログの比較

+
+ + + + +
大きさ行数単語数
OpenGL349486 byte341137194
Cerium349471 byte341137195
+
+
    +
  • 2 つのログに大きな差は無い
  • +
  • Super Dandy をエンディングまでプレイしたときに得られるテストログの大きさ + は約 350 KB
  • +
  • 単語数と大きさに僅かな差
  • +
+
+ +
+

diff によるテストログの比較

+
+% diff log/demo_log log/dandy_log
+1a2
+> Use Joystick
+3410,3411c3411,3412
+< 83.308451 FPS
+< move: average:49usec, peak:1091usec
+---
+> 0.000000 FPS
+> game end
+
+
    +
  • 表示されているメッセージは OpenGL や Cerium 依存のメッセージ
  • +
  • 0.000000 FPS は Cerium 側のメッセージで描画を行わないビデオモードにより + 正しく FPS の計算ができなかったため
  • +
  • wc の単語数はスペース区切りで判別するため、Cerium=6,OpenGL=5
  • +
  • よって両バージョンの動作は同じである
  • +
+
+ +
+

OpenGL バージョンと Task Dandy の比較

+
+super dandy(OpenGL) >>
+F64: CREATE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -128.000000 ...
+F85: DELETE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -44.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F96: CREATE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -128.000000 ...
+F96: CREATE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= -128.000000 ...
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F117: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= 40.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+<< task dandy
+F64: CREATE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -128.000000 ...
+F85: DELETE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -44.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F96: CREATE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -128.000000 ...
+F96: CREATE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= -128.000000 ...
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F109: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+
+ +
+

ログからのバグの洗い出し

+
+super dandy(OpenGL) >>
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F117: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= 40.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+<< task dandy
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F109: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+
    +
  • OpenGL では別フレームで死んだ 2 つの敵オブジェクトが Task Dandy では + 同フレームで死亡
  • +
  • この時の弾丸の数が一致
  • +
  • 片方が死んだ後、弾丸のオブジェクトの除去がされてない
  • +
  • 弾丸データが取れていない、という仮説を立てた
  • +
+
+ +
+

Collision Task 間でのデータの同期

+ + + + + +
    +
  • Collision Task を同じ CPU に送る
  • +
  • 予め衝突判定に必要なパラメータの領域を確保する
  • +
  • その領域のパラメータで衝突判定を行う
  • +
  • SPE 内で変更されたパラメータをメインメモリ側に反映させる
  • +
+ +
+
+ +
+

Collision Task の改良後の比較

+
+super dandy>>
+F64: CREATE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -128.000000 ...
+F85: DELETE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -44.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F96: CREATE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -128.000000 ...
+F96: CREATE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= -128.000000 ...
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F117: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= 40.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+<< task dandy
+F64: CREATE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -128.000000 ...
+F85: DELETE  [NAME]enemy_greenclab_0  [COORD]x= 120.000000  y= -44.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F96: CREATE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -128.000000 ...
+F96: CREATE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= -128.000000 ...
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F117: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= 40.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+
+ +
+

Collision Task の改良後の比較

+
+super dandy(OpenGL) >>
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F117: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= 40.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+<< task dandy
+F109: DELETE  [NAME]enemy_greenclab_1  [COORD]x= 56.000000  y= -24.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+F117: DELETE  [NAME]enemy_greenclab_2  [COORD]x= 184.000000  y= 40.000000 ...
+             [BULLET]tlv1 = 2, tlv2 = 0 llv1 = 0
+
+
    +
  • 2 つのバージョンのログがフレーム単位で同じ
  • +
  • Collision Task のデータ同期が有効に働いている
  • +
+
+ +
+

Task への乱数受け渡しの検証

+
    +
  • 多数の隕石オブジェクトが生成されるステージで全ての隕石オブジェクトが + 生成されるのを観察
  • +
  • 隕石オブジェクトの初期配置は乱数によるランダム配置
  • +
  • 隕石オブジェクト生成後の座標と速度を出力
  • +
+
+ +
+

隕石オブジェクトの実装

+
+  int sf;
+
+  sf = random() % 4;
+  if((sf == 0) || (sf == 1))
+    {
+      p->x = -35;
+      p->y = random() % (120 - 35);
+      p->vx = (random() % 4 + 1);
+      p->vy = random() % 3 + 1;
+      p->state = chara_state23;
+    }
+  if((sf == 2))
+    {
+      p->x = random() % 290;
+      p->y = -30;
+      p->vx = random() % 3 - 1;
+      p->vy = (random() % 4 + 1);
+      p->state = chara_state23;
+    }
+  if(sf == 3)
+    {
+      .....
+
+
+ +
+

Task Dandy 側の実装

+
+    int rand1 = (int)smanager->get_param(0);
+    int rand2 = (int)smanager->get_param(1);
+    int rand3 = (int)smanager->get_param(2);
+    int rand4 = (int)smanager->get_param(3);
+
+    CHARACTER *p = (CHARACTER*)smanager->get_input(rbuf, 0);
+
+      int sf = rand1 % 4;
+      if((sf == 0) || (sf == 1))
+      {
+         p->x = -35;
+	  p->y = rand2 % (120 - 35);
+	  p->vx = (rand3 % 4 + 1);
+	  p->vy = rand4 % 3 + 1;
+      }
+      if((sf == 2))
+      {
+	  p->x = rand2 % 290;
+	  p->y = -30;
+	  p->vx = rand3 % 3 - 1;
+	  p->vy = (rand4 % 4 + 1);
+          .....
+
+
+ +
+

実行結果

+
+demolog >>
+[COORD]x= 320.000000  y= 66.000000  vx= -2.000000  vy= 0.000000
+[COORD]x= -35.000000  y= 20.000000  vx= 3.000000  vy= 1.000000
+[COORD]x= -35.000000  y= 36.000000  vx= 3.000000  vy= 2.000000
+[COORD]x= 89.000000  y= -30.000000  vx= 1.000000  vy= 3.000000
+[COORD]x= -35.000000  y= 81.000000  vx= 1.000000  vy= 2.000000
+[COORD]x= 320.000000  y= 8.000000  vx= -4.000000  vy= -1.000000
+[COORD]x= 220.000000  y= -30.000000  vx= 1.000000  vy= 4.000000
+....
+
+<< tdandylog
+[COORD]x= 320.000000  y= 66.000000  vx= -2.000000  vy= 0.000000
+[COORD]x= -35.000000  y= 20.000000  vx= 3.000000  vy= 1.000000
+[COORD]x= -35.000000  y= 36.000000  vx= 3.000000  vy= 2.000000
+[COORD]x= 89.000000  y= -30.000000  vx= 1.000000  vy= 3.000000
+[COORD]x= -35.000000  y= 81.000000  vx= 1.000000  vy= 2.000000
+[COORD]x= 320.000000  y= 8.000000  vx= -4.000000  vy= -1.000000
+[COORD]x= 220.000000  y= -30.000000  vx= 1.000000  vy= 4.000000
+....
+
+% diff demolog tdandylog
+%
+
+
+ +
+

乱数受け渡しによる実行結果の検証

+
    +
  • 生成された隕石オブジェクトのパラメータが両バージョンで一致している
  • +
  • Task への乱数受け渡しによるバグの再現性の低下防止は有効である
  • +
+
+ +
+

ビデオモードによる実行時間の比較

+
    +
  • 実行時間の計測には unix の time コマンドを使用
  • +
  • 3 バージョンの描画無しモードを使用(OpenGL は 1x1)
  • +
  • 描画ありバージョンは 1200x800 で統一して計測
  • +
+
+ +
+

実行結果

+ + + +
OpenGL(w=1,h=1)Cerium(no video)Task(no video)OpenGLCeriumTask
実行時間335.06 sec334.21 sec385.17 sec336.09 sec5066.11 sec6643.16 sec
+
    +
  • OpenGL バージョンと Cerium バージョンではほとんど差がない
  • +
  • 描画処理を除けば 2 つのバージョンには殆ど差がない為と考えられる
  • +
  • TaskDandy では Cerium における Task の処理が発生したため、実行時間が大きく増加したと考えられる
  • +
+
+ +
+

実行結果

+ + + +
OpenGL(w=1,h=1)Cerium(no video)Task(no video)OpenGLCeriumTask
実行時間335.06 sec334.21 sec385.17 sec336.09 sec5066.11 sec6643.16 sec
+
    +
  • OpenGL では描画無しバージョンとの差がほとんど無い
  • +
  • Cerium バージョンや Task バージョンは劇的に処理時間が増加
  • +
  • 描画処理の Task の処理時間が非常に大きいと考えられる
  • +
  • 描画処理の Task に比べればゲームの Task は処理が小さい
  • +
+
+ +
+

結論

+

本研究では並列環境におけるゲームプログラムのテスト手法を提案した

+
    +
  • 衝突判定時のテストログ出力によるデバッグは OpenGL バージョンと Task + Dandy の実行結果が同じであることから、効果的であった
  • +
  • Task への乱数受け渡しによるバグの再現性は 同様にして有効であることが + わかった
  • +
  • 描画をしないビデオモードによるテスト時間の高速化は、描画をする場合に + 比べて 非常に効果があった
  • +
+
+ +
+

今後の課題

+
    +
  • 描画処理におけるバグの修正
  • +
  • Cerium におけるメモリアロケータの実装
  • +
+
+ +
+ + diff -r 4524ba5be532 -r d39c452010ea poster/ui/CVS/Entries --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/CVS/Entries Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,1 @@ +D/default//// diff -r 4524ba5be532 -r d39c452010ea poster/ui/CVS/Repository --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/CVS/Repository Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,1 @@ +member/koba/document/presentation/ui diff -r 4524ba5be532 -r d39c452010ea poster/ui/CVS/Root --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/CVS/Root Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,1 @@ +koba@firefly.cr.ie.u-ryukyu.ac.jp:/home/one/CVS_DB diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/CVS/Entries --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/CVS/Entries Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,12 @@ +/blank.gif/1.1/Fri Nov 21 07:36:57 2008// +/bodybg.gif/1.1/Fri Nov 21 07:36:57 2008// +/framing.css/1.1/Fri Nov 21 07:36:57 2008// +/iepngfix.htc/1.1/Fri Nov 21 07:36:57 2008// +/opera.css/1.1/Fri Nov 21 07:36:57 2008// +/outline.css/1.1/Fri Nov 21 07:36:57 2008// +/pretty.css/1.1/Fri Nov 21 07:36:57 2008// +/print.css/1.1/Fri Nov 21 07:36:57 2008// +/s5-core.css/1.1/Fri Nov 21 07:36:57 2008// +/slides.css/1.1/Fri Nov 21 07:36:57 2008// +/slides.js/1.1/Fri Nov 21 07:36:57 2008// +D diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/CVS/Repository --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/CVS/Repository Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,1 @@ +member/koba/document/presentation/ui/default diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/CVS/Root --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/CVS/Root Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,1 @@ +koba@firefly.cr.ie.u-ryukyu.ac.jp:/home/one/CVS_DB diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/blank.gif Binary file poster/ui/default/blank.gif has changed diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/bodybg.gif Binary file poster/ui/default/bodybg.gif has changed diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/framing.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/framing.css Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,23 @@ +/* The following styles size, place, and layer the slide components. + Edit these if you want to change the overall slide layout. + The commented lines can be uncommented (and modified, if necessary) + to help you with the rearrangement process. */ + +/* target = 1024x768 */ + +div#header, div#footer, .slide {width: 100%; top: 0; left: 0;} +div#header {top: 0; height: 3em; z-index: 1;} +div#footer {top: auto; bottom: 0; height: 2.5em; z-index: 5;} +.slide {top: 0; width: 92%; padding: 3.5em 4% 4%; z-index: 2; list-style: none;} +div#controls {left: 50%; bottom: 0; width: 50%; z-index: 100;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0;} +#currentSlide {position: absolute; width: 10%; left: 45%; bottom: 1em; z-index: 10;} +html>body #currentSlide {position: fixed;} + +/* +div#header {background: #FCC;} +div#footer {background: #CCF;} +div#controls {background: #BBD;} +div#currentSlide {background: #FFC;} +*/ diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/iepngfix.htc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/iepngfix.htc Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,42 @@ + + + + + \ No newline at end of file diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/opera.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/opera.css Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,7 @@ +/* DO NOT CHANGE THESE unless you really want to break Opera Show */ +.slide { + visibility: visible !important; + position: static !important; + page-break-before: always; +} +#slide0 {page-break-before: avoid;} diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/outline.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/outline.css Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,15 @@ +/* don't change this unless you want the layout stuff to show up in the outline view! */ + +.layout div, #footer *, #controlForm * {display: none;} +#footer, #controls, #controlForm, #navLinks, #toggle { + display: block; visibility: visible; margin: 0; padding: 0;} +#toggle {float: right; padding: 0.5em;} +html>body #toggle {position: fixed; top: 0; right: 0;} + +/* making the outline look pretty-ish */ + +#slide0 h1, #slide0 h2, #slide0 h3, #slide0 h4 {border: none; margin: 0;} +#slide0 h1 {padding-top: 1.5em;} +.slide h1 {margin: 1.5em 0 0; padding-top: 0.25em; + border-top: 1px solid #888; border-bottom: 1px solid #AAA;} +#toggle {border: 1px solid; border-width: 0 0 1px 1px; background: #FFF;} diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/pretty.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/pretty.css Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,86 @@ +/* Following are the presentation styles -- edit away! */ + +body {background: #FFF url(bodybg.gif) -16px 0 no-repeat; color: #000; font-size: 2em;} +:link, :visited {text-decoration: none; color: #00C;} +#controls :active {color: #88A !important;} +#controls :focus {outline: 1px dotted #227;} +h1, h2, h3, h4 {font-size: 100%; margin: 0; padding: 0; font-weight: inherit;} +ul, pre {margin: 0; line-height: 1em;} +html, body {margin: 0; padding: 0;} + +blockquote, q {font-style: italic;} +blockquote {padding: 0 2em 0.5em; margin: 0 1.5em 0.5em; text-align: center; font-size: 1em;} +blockquote p {margin: 0;} +blockquote i {font-style: normal;} +blockquote b {display: block; margin-top: 0.5em; font-weight: normal; font-size: smaller; font-style: normal;} +blockquote b i {font-style: italic;} + +kbd {font-weight: bold; font-size: 1em;} +sup {font-size: smaller; line-height: 1px;} + +.slide code {padding: 2px 0.25em; font-weight: bold; color: #533;} +.slide code.bad, code del {color: red;} +.slide code.old {color: silver;} +.slide pre {padding: 0; margin: 0.25em 0 0.5em 0.5em; color: #533; font-size: 90%;} +.slide pre code {display: block;} +.slide ul {margin-left: 5%; margin-right: 7%; list-style: disc;} +.slide li {margin-top: 0.75em; margin-right: 0;} +.slide ul ul {line-height: 1;} +.slide ul ul li {margin: .2em; font-size: 85%; list-style: square;} +.slide img.leader {display: block; margin: 0 auto;} + +div#header, div#footer {background: #005; color: #AAB; + font-family: Verdana, Helvetica, sans-serif;} +div#header {background: #005 url(bodybg.gif) -16px 0 no-repeat; + line-height: 1px;} +div#footer {font-size: 0.5em; font-weight: bold; padding: 1em 0;} +#footer h1, #footer h2 {display: block; padding: 0 1em;} +#footer h2 {font-style: italic;} + +div.long {font-size: 0.75em;} +.slide h1 {position: absolute; top: 0.7em; left: 87px; z-index: 1; + margin: 0; padding: 0.3em 0 0 50px; white-space: nowrap; + font: bold 150%/1em Helvetica, sans-serif; text-transform: capitalize; + color: #DDE; background: #005;} +.slide h3 {font-size: 130%;} +h1 abbr {font-variant: small-caps;} + +div#controls {position: absolute; left: 50%; bottom: 0; + width: 50%; + text-align: right; font: bold 0.9em Verdana, Helvetica, sans-serif;} +html>body div#controls {position: fixed; padding: 0 0 1em 0; + top: auto;} +div#controls form {position: absolute; bottom: 0; right: 0; width: 100%; + margin: 0; padding: 0;} +#controls #navLinks a {padding: 0; margin: 0 0.5em; + background: #005; border: none; color: #779; + cursor: pointer;} +#controls #navList {height: 1em;} +#controls #navList #jumplist {position: absolute; bottom: 0; right: 0; background: #DDD; color: #227;} + +#currentSlide {text-align: center; font-size: 0.5em; color: #449;} + +#slide0 {padding-top: 3.5em; font-size: 90%;} +#slide0 h1 {position: static; margin: 1em 0 0; padding: 0; + font: bold 2em Helvetica, sans-serif; white-space: normal; + color: #000; background: transparent;} +#slide0 h2 {font: bold italic 1em Helvetica, sans-serif; margin: 0.25em;} +#slide0 h3 {margin-top: 1.5em; font-size: 1.5em;} +#slide0 h4 {margin-top: 0; font-size: 1em;} + +ul.urls {list-style: none; display: inline; margin: 0;} +.urls li {display: inline; margin: 0;} +.note {display: none;} +.external {border-bottom: 1px dotted gray;} +html>body .external {border-bottom: none;} +.external:after {content: " \274F"; font-size: smaller; color: #77B;} + +.incremental, .incremental *, .incremental *:after {color: #DDE; visibility: visible;} +img.incremental {visibility: hidden;} +.slide .current {color: #B02;} + + +/* diagnostics + +li:after {content: " [" attr(class) "]"; color: #F88;} + */ \ No newline at end of file diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/print.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/print.css Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,1 @@ +/* The following rule is necessary to have all slides appear in print! DO NOT REMOVE IT! */ .slide, ul {page-break-inside: avoid; visibility: visible !important;} h1 {page-break-after: avoid;} body {font-size: 12pt; background: white;} * {color: black;} #slide0 h1 {font-size: 200%; border: none; margin: 0.5em 0 0.25em;} #slide0 h3 {margin: 0; padding: 0;} #slide0 h4 {margin: 0 0 0.5em; padding: 0;} #slide0 {margin-bottom: 3em;} h1 {border-top: 2pt solid gray; border-bottom: 1px dotted silver;} .extra {background: transparent !important;} div.extra, pre.extra, .example {font-size: 10pt; color: #333;} ul.extra a {font-weight: bold;} p.example {display: none;} #header {display: none;} #footer h1 {margin: 0; border-bottom: 1px solid; color: gray; font-style: italic;} #footer h2, #controls {display: none;} /* The following rule keeps the layout stuff out of print. Remove at your own risk! */ .layout, .layout * {display: none !important;} \ No newline at end of file diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/s5-core.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/s5-core.css Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,9 @@ +/* Do not edit or override these styles! The system will likely break if you do. */ + +div#header, div#footer, div#controls, .slide {position: absolute;} +html>body div#header, html>body div#footer, + html>body div#controls, html>body .slide {position: fixed;} +.handout {display: none;} +.layout {display: block;} +.slide, .hideme, .incremental {visibility: hidden;} +#slide0 {visibility: visible;} diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/slides.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/slides.css Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,3 @@ +@import url(s5-core.css); /* required to make the slide show run at all */ +@import url(framing.css); /* sets basic placement and size of slide components */ +@import url(pretty.css); /* stuff that makes the slides look better than blah */ \ No newline at end of file diff -r 4524ba5be532 -r d39c452010ea poster/ui/default/slides.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/poster/ui/default/slides.js Tue Feb 15 18:21:02 2011 +0900 @@ -0,0 +1,553 @@ +// S5 v1.1 slides.js -- released into the Public Domain +// +// Please see http://www.meyerweb.com/eric/tools/s5/credits.html for information +// about all the wonderful and talented contributors to this code! + +var undef; +var slideCSS = ''; +var snum = 0; +var smax = 1; +var incpos = 0; +var number = undef; +var s5mode = true; +var defaultView = 'slideshow'; +var controlVis = 'visible'; + +var isIE = navigator.appName == 'Microsoft Internet Explorer' && navigator.userAgent.indexOf('Opera') < 1 ? 1 : 0; +var isOp = navigator.userAgent.indexOf('Opera') > -1 ? 1 : 0; +var isGe = navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('Safari') < 1 ? 1 : 0; + +function hasClass(object, className) { + if (!object.className) return false; + return (object.className.search('(^|\\s)' + className + '(\\s|$)') != -1); +} + +function hasValue(object, value) { + if (!object) return false; + return (object.search('(^|\\s)' + value + '(\\s|$)') != -1); +} + +function removeClass(object,className) { + if (!object) return; + object.className = object.className.replace(new RegExp('(^|\\s)'+className+'(\\s|$)'), RegExp.$1+RegExp.$2); +} + +function addClass(object,className) { + if (!object || hasClass(object, className)) return; + if (object.className) { + object.className += ' '+className; + } else { + object.className = className; + } +} + +function GetElementsWithClassName(elementName,className) { + var allElements = document.getElementsByTagName(elementName); + var elemColl = new Array(); + for (var i = 0; i< allElements.length; i++) { + if (hasClass(allElements[i], className)) { + elemColl[elemColl.length] = allElements[i]; + } + } + return elemColl; +} + +function isParentOrSelf(element, id) { + if (element == null || element.nodeName=='BODY') return false; + else if (element.id == id) return true; + else return isParentOrSelf(element.parentNode, id); +} + +function nodeValue(node) { + var result = ""; + if (node.nodeType == 1) { + var children = node.childNodes; + for (var i = 0; i < children.length; ++i) { + result += nodeValue(children[i]); + } + } + else if (node.nodeType == 3) { + result = node.nodeValue; + } + return(result); +} + +function slideLabel() { + var slideColl = GetElementsWithClassName('*','slide'); + var list = document.getElementById('jumplist'); + smax = slideColl.length; + for (var n = 0; n < smax; n++) { + var obj = slideColl[n]; + + var did = 'slide' + n.toString(); + obj.setAttribute('id',did); + if (isOp) continue; + + var otext = ''; + var menu = obj.firstChild; + if (!menu) continue; // to cope with empty slides + while (menu && menu.nodeType == 3) { + menu = menu.nextSibling; + } + if (!menu) continue; // to cope with slides with only text nodes + + var menunodes = menu.childNodes; + for (var o = 0; o < menunodes.length; o++) { + otext += nodeValue(menunodes[o]); + } + list.options[list.length] = new Option(n + ' : ' + otext, n); + } +} + +function currentSlide() { + var cs; + if (document.getElementById) { + cs = document.getElementById('currentSlide'); + } else { + cs = document.currentSlide; + } + cs.innerHTML = '' + snum + '<\/span> ' + + '\/<\/span> ' + + '' + (smax-1) + '<\/span>'; + if (snum == 0) { + cs.style.visibility = 'hidden'; + } else { + cs.style.visibility = 'visible'; + } +} + +function go(step) { + if (document.getElementById('slideProj').disabled || step == 0) return; + var jl = document.getElementById('jumplist'); + var cid = 'slide' + snum; + var ce = document.getElementById(cid); + if (incrementals[snum].length > 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + removeClass(incrementals[snum][i], 'current'); + removeClass(incrementals[snum][i], 'incremental'); + } + } + if (step != 'j') { + snum += step; + lmax = smax - 1; + if (snum > lmax) snum = lmax; + if (snum < 0) snum = 0; + } else + snum = parseInt(jl.value); + var nid = 'slide' + snum; + var ne = document.getElementById(nid); + if (!ne) { + ne = document.getElementById('slide0'); + snum = 0; + } + if (step < 0) {incpos = incrementals[snum].length} else {incpos = 0;} + if (incrementals[snum].length > 0 && incpos == 0) { + for (var i = 0; i < incrementals[snum].length; i++) { + if (hasClass(incrementals[snum][i], 'current')) + incpos = i + 1; + else + addClass(incrementals[snum][i], 'incremental'); + } + } + if (incrementals[snum].length > 0 && incpos > 0) + addClass(incrementals[snum][incpos - 1], 'current'); + ce.style.visibility = 'hidden'; + ne.style.visibility = 'visible'; + jl.selectedIndex = snum; + currentSlide(); + number = 0; +} + +function goTo(target) { + if (target >= smax || target == snum) return; + go(target - snum); +} + +function subgo(step) { + if (step > 0) { + removeClass(incrementals[snum][incpos - 1],'current'); + removeClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos],'current'); + incpos++; + } else { + incpos--; + removeClass(incrementals[snum][incpos],'current'); + addClass(incrementals[snum][incpos], 'incremental'); + addClass(incrementals[snum][incpos - 1],'current'); + } +} + +function toggle() { + var slideColl = GetElementsWithClassName('*','slide'); + var slides = document.getElementById('slideProj'); + var outline = document.getElementById('outlineStyle'); + if (!slides.disabled) { + slides.disabled = true; + outline.disabled = false; + s5mode = false; + fontSize('1em'); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'visible'; + } + } else { + slides.disabled = false; + outline.disabled = true; + s5mode = true; + fontScale(); + for (var n = 0; n < smax; n++) { + var slide = slideColl[n]; + slide.style.visibility = 'hidden'; + } + slideColl[snum].style.visibility = 'visible'; + } +} + +function showHide(action) { + var obj = GetElementsWithClassName('*','hideme')[0]; + switch (action) { + case 's': obj.style.visibility = 'visible'; break; + case 'h': obj.style.visibility = 'hidden'; break; + case 'k': + if (obj.style.visibility != 'visible') { + obj.style.visibility = 'visible'; + } else { + obj.style.visibility = 'hidden'; + } + break; + } +} + +// 'keys' code adapted from MozPoint (http://mozpoint.mozdev.org/) +function keys(key) { + if (!key) { + key = event; + key.which = key.keyCode; + } + if (key.which == 84) { + toggle(); + return; + } + if (s5mode) { + switch (key.which) { + case 10: // return + case 13: // enter + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + if(number != undef) { + goTo(number); + break; + } + case 32: // spacebar + case 34: // page down + case 39: // rightkey + case 40: // downkey + if(number != undef) { + go(number); + } else if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + break; + case 33: // page up + case 37: // leftkey + case 38: // upkey + if(number != undef) { + go(-1 * number); + } else if (!incrementals[snum] || incpos <= 0) { + go(-1); + } else { + subgo(-1); + } + break; + case 36: // home + goTo(0); + break; + case 35: // end + goTo(smax-1); + break; + case 67: // c + showHide('k'); + break; + } + if (key.which < 48 || key.which > 57) { + number = undef; + } else { + if (window.event && isParentOrSelf(window.event.srcElement, 'controls')) return; + if (key.target && isParentOrSelf(key.target, 'controls')) return; + number = (((number != undef) ? number : 0) * 10) + (key.which - 48); + } + } + return false; +} + +function clicker(e) { + number = undef; + var target; + if (window.event) { + target = window.event.srcElement; + e = window.event; + } else target = e.target; + if (target.getAttribute('href') != null || hasValue(target.rel, 'external') || isParentOrSelf(target, 'controls') || isParentOrSelf(target,'embed') || isParentOrSelf(target,'object')) return true; + if (!e.which || e.which == 1) { + if (!incrementals[snum] || incpos >= incrementals[snum].length) { + go(1); + } else { + subgo(1); + } + } +} + +function findSlide(hash) { + var target = null; + var slides = GetElementsWithClassName('*','slide'); + for (var i = 0; i < slides.length; i++) { + var targetSlide = slides[i]; + if ( (targetSlide.name && targetSlide.name == hash) + || (targetSlide.id && targetSlide.id == hash) ) { + target = targetSlide; + break; + } + } + while(target != null && target.nodeName != 'BODY') { + if (hasClass(target, 'slide')) { + return parseInt(target.id.slice(5)); + } + target = target.parentNode; + } + return null; +} + +function slideJump() { + if (window.location.hash == null) return; + var sregex = /^#slide(\d+)$/; + var matches = sregex.exec(window.location.hash); + var dest = null; + if (matches != null) { + dest = parseInt(matches[1]); + } else { + dest = findSlide(window.location.hash.slice(1)); + } + if (dest != null) + go(dest - snum); +} + +function fixLinks() { + var thisUri = window.location.href; + thisUri = thisUri.slice(0, thisUri.length - window.location.hash.length); + var aelements = document.getElementsByTagName('A'); + for (var i = 0; i < aelements.length; i++) { + var a = aelements[i].href; + var slideID = a.match('\#slide[0-9]{1,2}'); + if ((slideID) && (slideID[0].slice(0,1) == '#')) { + var dest = findSlide(slideID[0].slice(1)); + if (dest != null) { + if (aelements[i].addEventListener) { + aelements[i].addEventListener("click", new Function("e", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "if (e.preventDefault) e.preventDefault();"), true); + } else if (aelements[i].attachEvent) { + aelements[i].attachEvent("onclick", new Function("", + "if (document.getElementById('slideProj').disabled) return;" + + "go("+dest+" - snum); " + + "event.returnValue = false;")); + } + } + } + } +} + +function externalLinks() { + if (!document.getElementsByTagName) return; + var anchors = document.getElementsByTagName('a'); + for (var i=0; i' + + '