スライド 1

プロジェクト演習Ⅱ
インタラクティブゲーム制作
イントロダクション2
第6回
はじめての3Dプログラミング
ジャンプ(色んな意味で)編
今日の内容
• 跳びたい!
– 状態による条件分岐
– 速度と加速度
– 衝突判定の効率化
• そろそろmain()が膨らむのがつらい…
– クラスと関数を導入したプログラム
• こちらも結構な「ジャンプ」かも……
今週のプロジェクト
• 授業資料ページからダウンロードします
– 落としたZipファイルを解凍して、出てきた
フォルダを好きなところに配置
ちょびっとProcessingチックに書いてみようか
プログラムの構造を整理しよう
そろそろ行数辛くないですか?
• main()がどんどん
伸びるよ!
– やったね!…ではない
int main(int argc, char *argv[])
{
// 準備処理がごちゃー!
…
// ここからループ!
while(update() ==true) {
// 条件分岐がごちゃー!
…
}
return 0;
• 100行超えた関数を
うじうじいじるのは
ちょっとイケてない
– と言いつつ私も顔を
背ける……
}
なので、関数化したい
• 準備でやることは
setup()
• 毎フレーム処理する
ことはdraw()
• あれあれ、これって
Processingチック?
void setup()
{
// ここで準備
};
void draw()
{
// ここでループ
while(update() == true) {
// 処理本体
}
};
setup()やdraw()の中身も
用途に応じて関数に分ける
• やることが増えると
結局setup()やdraw()
が爆発する
• 処理のカタマリごとで
関数に分けると見通し
が良くなる
• 関数の基本
– 名前を呼ばれたらそこへ
ジャンプ
– 終わったらもどの場所へ
ジャンプ
void draw()
{
while(update() == true) {
are();
sore();
}
};
void are()
{
// 超複雑なアレに関する処理
};
void sore()
{
// 超難解なソレに関する処理
};
そんなプログラミングを
実現するために
• 「クラス」を導入
– privateのところに
プログラムで使いたい
オブジェクトや変数を
用意する
• 関数内で使い捨てるもの
は関数内でもいい
– publicのところに関数を
書く
• 準備はsetup()
• ループはdraw()
• それ以外にも関数は
作ってよい
• main()関数から、
きちんとsetup()と
draw()を呼んでいる
のを確認しておこう
– ゲームがオブジェクトに
なってる?と思った人は
なかなか鋭い
• 今のやり方だと結局
1ファイルは長くなるが、
これを分割する方法は
いずれまた
前回の課題でやりたかった人もいるでしょう
キーを押したら勝手に動く、は
どーやるの?
以前に教えた条件分岐で
できたこと
• キーを「押してる間」こうしろ
• キーを「押した瞬間」こうしてね
だけど
• キーを押したら「こう動き始めてね」
– で、一定時間過ぎたら動作終了するような
フラグ、立ってますか?
1. キーが押された!フラグが立った!
if(キー押された) { フラグON }
2. フラグ立ってる間はこうしなさい!
アクションが終了したら、
フラグをベキッ!と折りなさい
if(フラグON) {
アクション1コマ分の処理
if(アクション終わり?) { フラグOFF }
}
飛べないアクションはただのアクションだ
お前ちょっと跳んでみろよ
ジャンプはアクションの命
• ジャンプもフラグに
よって管理できる
アクションのひとつ
– 右図は驚異的な
跳躍力でお馴染みの
配管工Mさん
フラグ(モード)管理の流れ
•
•
•
•
普通の状態を0とする
キーを押されたら上昇モード(1)とする
頂点に達したら下降モード(2)とする
着地したら0に戻す
上昇と下降ってどういう処理?
• 単純に一定距離ぶん上下移動させる
– glTranslate()で動かせばいいのだが…
– ずっと一定値だと単調で気持ち悪い
• 速度と加速度の関係を思いだそう
– 速度をいじってやることで動きに緩急が付く
– 重力加速度とかに則ってもいいけど、
そのままだとあんまり面白くない
– 気持ちいい動きを探してみよう
モードの境目
• 通常→上昇
– キーが押された瞬間
• 上昇→下降
– y方向の速度がマイナスになった時
• 下降→着地
– 地面とぶつかった時
• 先週覚えた「衝突判定」が使えそう!
• 通常→下降
– 足元に地面が無くなったとき!
今回の当たり判定
• 障害物はfkut_BlockModel
• プレイヤーはfkut_SphereModel
• 当たり判定には、fkut_BlockModelの変数に対し
てcheckToMovingSphere()を使う
– block.checkToMovingSphere(other, spd, back);
• otherの部分に操作しているSphereModelを、
spdに動かす予定の速度を、backには空のfk_Vectorを渡す
– if(命令 == true)でYesなら衝突している
• backの部分に渡したfk_Vectorには、spdをどれだけ
減算すれば衝突寸前まで戻せるかを表すベクトルが入る
真の衝突判定とは
• ある瞬間にぶつかってるかどうか、を
調べるだけでは「干渉判定」でしかない
• 「これくらい動く予定なんだけど、
どこかでぶつかっちゃう?」を調べる
!!!!
!
?
!
spdが非常に重要な変数になる
• まずは次のフレームでどれだけ動かした
いかをspdに蓄積し、当たり判定で修正
• 修正が済んだspdをモデルにglTranslate()
で適用する
• Block対Blockなら
checkToMovingBlock()で同じように
判定できる
用途に応じて使い分け
• キャラクターは球
– 見た目はゴージャスに
作った形状でもいい
– それらを覆うような球
を当たり判定用に用意
して、親子関係を結べ
ばいい
• entry()しなければ表示
されない
• マップはブロック
– 球だとつるつるすべっ
て操作性に難がある
その他もろもろ
カメラを一緒に動かす
• 自分で用意したfk_Modelの変数をカメラ
としてセットすると、物体同様に動きを
コントロールすることが可能
• キャラクターの動きと同期すれば、
FPS的な画面も作れる
Modelシリーズの回転制御命令
• 今のところ「方向」を指定するやり方し
か教えていないが、他にも色々あります
– XYZ軸それぞれについてこのくらいの角度
で!などといった姿勢の指定が可能
• glAngle()
– 現在位置からここを中心に回転しろ、なんて
命令も
• glRotateWithVec()
• リファレンスを見て実験して確かめよう
ぶつかるためじゃない当たり判定
• ゲームでは「ある地点に到達したら何か
がおきる」という処理も必要
• fkut_BlockModelを配置して、そこに接
触したらイベント発生、という使い方も
できる
– entryしなければ表示はされない
今日の課題
• BASIC
– Xキーを押すと、5秒間(300フレーム)球が赤くなり、
3倍の速さで走るようにしてみよう
• フラグとして秒数をカウントする変数を用意
• フラグが立っているかいないかで処理を分岐
• フラグが立っている場合の処理で、残り時間を減算していく
• ADVANCED
– 2段ジャンプをできるようにしてみよう
• EXTREME
– ジャンプで上っていけるフィールドを作り、
ゴールに到達したらクリアを褒めるメッセージを
出して終了するゲームを作ってみよう
複雑なフィールドを作ってしまった人へ
当たった場合の戻し方
戻し方の罠
• 動物体の次フレームでの位置を仮定する
• その動きを阻害する可能性のある物体全
てと衝突判定を行う
• 戻しベクトルが発生したら動物体に適用
する
• 全物体と判定を終えたら終了…
ではない
以下のようなシチュを考える
• 右図のような状況の
場合、Aに衝突して発
生した戻しベクトル
によって、Bに衝突す
る危険性がある
• A→Bの順に判定でき
ればいいが、そうな
らなかったらどうす
る?
真上から見た図
解決案
• 全部の物体と判定して、 do {
「戻しベクトルが発生
戻しベクトル初期化
しなかったら」判定終
for(障害物全部) {
了とする
当たり判定する
• 色々効率化の余地はあ
戻しベクトル加算
るだろうが、とりあえ
}
ずはこうしておこう
動物体に適用
} while(適用したベクト
ルがゼロじゃなかった
らもう1回ループ);
重力の扱い
• 基本的には常時下向
きの力を働かせる
• そうすると、同じ高
さで並べたブロック
でつっかかる
– 足下のブロックから先
に判定できればいいが、
そうもいかない
• 速度をXZ方向とY方
向に分解して2回判定
真横から見た図
A
B
物体を押せるようにした場合
• 押されて動いた物体vs全物体での
判定も必要
– 判定回数が爆発的に増加する
• 押して動かせるのは限定的にするべき
– でも関数を上手く作れば処理はコンパクトに
書けるはず