ねこと触れ合えるCGゲーム
目次
概要
説明ビデオ
依存関係
インストール
操作方法
コントローラ
プログラム構成図
制作過程
プログラムを実行すると, 猫がランダムに運動している. 猫は前進, 右左折, 跳躍, 静止などの動作が可能で, それに合わせて表情も変化する. ユーザはキーボードで前後左右と上下の視点を操作できるほか, 次の7つのモードでインタラクションを行える.
- WATCH – ユーザの視点移動のみ.
- BREED – クリックした 2 匹から猫を生成.
- CARRY – クリックした猫を運ぶ.
- COLOR – クリックした猫の色を変える.
- BALL – ボールを投げて猫にとって来させる.
- LINE – 地面に白線を引く.
- FUSION – 錬成陣を発動させる.
画面右上には, 猫と視点の位置を点で示すマップと視点の高度を表すメータを表示した. サーチライトを模した三角形で視線方向もわかる.
video.mp4
fusion-mode.mp4
- OpenGL 3.0
- GLUT
- このリポジトリをクローン
jishupro/glview
ディレクトリに移動- 次のコマンドを実行
$ make
$ ./main
- W - 前進
- S - 後退
- A - 左折
- D - 右折
- Z - 上昇
- X - 降下
- P - モード切替
- N - LINEモードで描画の開始、終了(保存)を切替, 削除の操作
- R, G, B - COLORモードで色と±の変更
パソコンへのキー入力、マウス入力をコントローラから行える。
ケースの設計図はcontroller_case
、配線はcontroller/controller.ino
を参照。
- Arduino Leonardo
- 2軸ジョイスティック(センタープッシュ有)× 2
- 押しボタンスイッチ(モーメンタリ)× 7
- アクリル板 (5mm 厚)
プログラムはキーボード入力で操作を行えるように実装したが, キーボード入力をより簡単にするためにコントローラを製作した. スイッチとジョイスティックで入力を行い, 受け取ったマイコンが USB でパソコンに 指令を送る. ケースは CAD で設計し (図 3), アクリル板をレーザーカットして組み立てた. ボタンの配置は上図のようにし, 使用できる機能は以下の通りである.
ジョイスティック
- 左 – レバーを動かして前後方向の移動と左右回転. プッシュでマウスクリック
- 右 – マウスカーソルの移動
ボタン
- 黄 ×2 – 視点の上下移動
- 赤, 緑, 青 – COLOR モードで RGB と +-の切り替え
- 黒 (左) – モードを切り替える
- 黒 (右) – LINE モードで描画の開始と終了 (保存), 削除の操作
1 猫の基本部分
2 行列計算
3 二次元オブジェクト
4 ユーザ入力機能
5 コントローラの作成
まず, 1 匹の猫を描画する関数 drawCat を実装した. 原点を中心に胴体となる直方体を作り, 首の位置まで移動してから頭部となる直方体, 両耳に円錐を描画する.
複数体の猫を出現させるために, 描画位置と色を乱数で決めてループで猫の数だけ繰り返しdrawCatを実行したが, display
関数を回すごとに出現位置が変わってしまう. そこで, 猫の位置, 回転角度, 色, 大きさ等を
記憶する配列を定義した. 仮想的に二次元配列と見なして必要な要素ずつ猫に割り当てる. 二次元配列にしなかったのは, 同時変換行列を入れた16要素のみを取り出す際にエラーが生じたためである.
しかし, 途中から構造体の配列で表す仕様に変更した. マクロ置換を使って列数を文字列で表してはいたが, 同時変換行列まで加わると可読性が大変悪く, 新しい変数を割り当てたくなったときに列の追加もやりにくい という問題があった. 構造体 Cat は int や double の変数に加えて同時変換行列も matrix[16] として格納し, 変数を増やすときは構造体の定義を書き換えるだけで済むようになった.
猫全体は構造体 Cat の配列catsとし, 配列の長さをあらかじめ決めなければならないので, それを最大出現可能数とした. 何番目の猫かを指定すれば, catsからその猫の情報を取り出し, drawCat
に代入して描画できる.
プログラム起動時にテクスチャマッピング用の画像を作り, バッファへ格納している. 表情ごとに画像を作ろうとしていたが, マッピングする画像の切り替えが上手くできなかったため, 最終的には 32 × 32 ピクセルの画像を四分割して顔を描き, マッピングに切り出す領域を変えることで猫の表情を変えられるようになった.
猫に様々な動きをさせるためには, 乱数で直進や回転を指定すればよい. 乱数で次の行動の確率を調整しながら実装してみたが,
display
関数を実行するごとに遷移するため, 急に90度回転するなど違和感があった.
より生き物らしい自然な動作にするためには, 一定時間関連のある動作を続ける必要があるし, 走っていた猫が急に眠らないように, 次に行う動作はある程度限定されるはずである.
解決策として, 猫の状態を表す task とその経過を示す duration, 速度のパラメータを構造体に追加した. task には基本の WALK(歩行), STAY(静止), SLEEP(眠る), TURN(右左折) のほか, ユーザ入力があったときに行う一時的なものがある. display関数を実行すると, 猫の状態を更新するupdateFunc関数が呼び出され, 各猫の task に基づいて移動や回転が加えられる.
例えば task が WALK になったとき, 最初に duration が設定され, 表情が WALK 用のものに変化する.
その後は, duration が 1 になるまで, updateFunc()
が呼ばれる度に速度のパラメータに基づいて直進し,
duration を 1 ずつ減らしていく. duration=1 になると次の task を割り当てられる.
duration や速度のパラメータ, 次の task は乱数を利用した確率で指定され, task ごとに重みづけが異なる.
前項で述べた猫の移動は同時変換行列の書き換えによって実装される. 当初はオブジェクトを並進させる
glTranslatef()
や回転させる glRotatef()
関数を使おうとしていたが, 回転と並進をした後の猫の座標を
覚えておくだけだと, 次の描画で猫の体の向きがわからなくなってしまうという問題に気がついた. 解決方法
として, glMultMatrixf()
という関数に同時変換行列を代入すると正しい位置姿勢を復元できた.
(x, y, z) だけ並進したいときには tlMat()
, angle 度だけ y 軸周りに回転させたいときには y_rtMat()
を現在の同時変換行列にかければよい.
カメラ位置はキーボード入力によって操作できる. カメラの移動は, 世界全体がカメラと逆向きに動くこと を意味する. カメラの運動に反対の符号を与えて同時変換行列を計算しなければならない. 求まる行列は, カメ ラが動くと考えたときの逆行列である.
レイキャストは, ある点から特定の方向に直線を引いたとき, その直線と物体との衝突を検知する機能であ
る. BREED モードと COLOR モードでは, 猫をカーソルでクリックする際の当たり判定が必要になる. レイ
キャストを行う関数 getWorldCood()
では, モデルビュー行列, 透視投影行列, ビューポートの三種類の行列
を取得する. これとクリックした位置のウィンドウ座標, デプスバッファの値などを gluUnProject()
関数に
入れると, 対応するオブジェクト座標を返してくれる. 視点近くの物体はレイキャスティングが上手くできた
が, 遠くの猫はクリックしても反応しないことがあった.
緑色の地面を作るまで, 真っ暗な世界が無限に広がっているため視点を移動させて進んでいくと元の場所に 戻れなかった. そこで, 画面に地図を表示するようにした. 猫の位置は原点を同時変換行列で写した先であるか ら, 行列の要素 12 番目が x 座標, 14 番目が z 座標である. 猫の色も点に反映させることで見やすくなった.
視点の位置については, 描画で使う視点移動の行列の逆行列を求めれば, カメラが猫同様に原点から同時変換 行列で移動していると考えられる. よって逆行列の 12, 14 番目を使用する. 視線方向を表す逆三角形は, カメ ラから見て斜め前の位置 (±5, 15, 0) にカメラの同時変換行列をかけて変換した.
雲を地面領域の周りにしか表示させていないため, 見栄えが悪くならない範囲に視点の高度を制限した. 現 在の高度はウィンドウ右上に棒グラフ状の高度計として表示させた. 高度に応じて長方形の高さが決まる仕組 みとなっている.
LINE モードでは地面に白線を引くことができる. 描画を開始すると, カメラが並進から回転に切り替わる毎 にその位置の座標が頂点として保存され, 頂点をつなげて線が描かれる. この条件のために, 最後に保存した頂 点と, 視点の現在地を結ぶ直線を表示させるのが難しかった. 紐の一端を地面に杭で打ち込み, 反対の端は手で 持って歩き回るイメージである. 現在位置も頂点として逐一保存して描画に含めておき, 並進運動をする度に 最後に保存した頂点を現在位置と置き換えることで解決した.
BALL モードではボールを遠くに投げることができる. モードを切り替えるとまずボールが画面に表示され, マウスでクリックしたまま押し続けると, 投球準備に入りボールの色が変化する. 色はボールの初速度を表し, マウスクリックを離した瞬間の色に対応する速度でボールが投げられる. ボールは重力の影響を受けながら放 物弧を描いて地面に落ちる.
ボールをクリックして投球準備に入ると, カメラ位置に最も近い猫が反応してこちらを見上げる. ボールを 投げると, 落下予想地点に向かって進み, ボールを背負って帰ってくる. ボールが地面に落ちた後は実際のボー ルの位置に向かい, ボールとの距離が基準より近くなればボールに到達したという判定にしたが, まだバグが多 く解決していない. 絶対座標の原点からボールを投げる場合のみ成功する. カメラが移動するとボールを追跡 できなくなるため, ボールの位置を猫から見た座標系に変換する処理に誤りがあると考えられる. ボールを持って帰る際にはカメラ位置を追跡するようにしてあり, これは上手くいった. ボールを背負った 猫から逃げようとしても, 猫は方向転換しながらついてくる.
FUSION モードは LINE モードで閉じた図形を描くと発動する. 図形を描き終えた後にキー入力を行うと, 保存した頂点情報をもとに図形が閉じているかの判定が行われる. 判定が正しいと, 図形の重心座標と重心か らの平均距離が計算される. 次に, 重心を中心に, 平均距離を半径にとる円の内側にいる猫が記録される. 猫が 1 匹以上いる場合にイベントが始まり, 空の色が徐々に暗く変化すると同時に, 地面が揺れ始める. 地面で円の 描画が始まると, 円弧が描かれるにつれて内側の猫が 1 匹ずつ黒くなり, 空へ上昇していく. 円の描画が終わる と地面から巨大な黒い猫がせり上がってくる. 猫の大きさは, 錬成に使われた猫の数に比例している.
パソコンのキーボードも変わらず使える ように, コントローラによってキーボード入力を代替させることにした. Arduino ボードで ATmega32U4 を搭載した機種は, USB 接続したときにキーボードとして動作できる. 今 回使用した KEYSTUDIO Leonardo R3 は Arduino Leonardo の互換である. スイッチやジョイスティック の配線はアーケードコントローラの自作方法を参考にした. ジョイスティッ クにマウス移動を実装していた際にプログラムを間違えてしまい, 修正したくてもパソコンに繋ぐとカーソル が勝手に動いてしまったのは苦労したが, リセットボタンの長押しで解決できた.
ケースはアクリル板から部品を切り出して組み立てた. 上面と下面はねじ固定で取り外しで きるようするため, 側面の四つ角に三角形を接着し, ねじ切りをした. アクリル板の接着手順としては, 接着するものを外側からセロハンテープで固 定し, 内側から接着剤を流し込む. 時間が経つと接着剤を塗ったアクリル面が溶けて接着され る. 接着は上手くいったが, 切断面が斜め*1だったのとアクリル板が少し縮んでしまったせいで ねじ穴の位置が合わなかった. バカ穴にして何とか調整し, 幸いねじがぐらつくことも無かった.