void Robo::getViewMatrix( Matrix34* vm ) const { //まず正面方向ベクタを作成 Vector3 d( 0.0, 0.0, 1.0 ); Matrix34 m; m.setRotationY( mAngleY ); m.multiply( &d, d ); //こいつを前方にmCameraTargetDistanceZだけ伸ばす Vector3 t; t.setMul( d, mCameraTargetDistanceZ ); //ロボが高いところにいるならちょっと下を見てやる。これはパラメータにないその場工夫。 t.y -= mPosition.y * 0.12; //このへんの調整も適当 //こいつを後方にmCameraDistacneZだけ伸ばす Vector3 p; p.setMul( d, -mCameraDistanceZ ); //YにmCameraDistanceYをプラス p.y += mCameraDistanceY; //ロボが高いところにいるならちょっと高目にして下を見てやる。これはパラメータにないその場工夫。 p.y += mPosition.y * 0.12; //このへんの調整も適当 //ロボ現在位置をプラス t += mPosition; p += mPosition; //ビュー行列作成 vm->setViewTransform( p, t ); }
void Robo::update( const Vector3& enemyPos ){ Pad* pad = Pad::instance(); if ( pad->isOn( Pad::TURN, mId ) ){ if ( pad->isOn( Pad::LEFT, mId ) ){ mAngleY += 1.0; if ( mAngleY > 180.0 ){ //-PIからPIにおさめる mAngleY -= 360.0; } } if ( pad->isOn( Pad::RIGHT, mId ) ){ mAngleY -= 1.0; if ( mAngleY < -180.0 ){ //-PIからPIにおさめる mAngleY += 360.0; } } }else{ //移動処理。まず視点を考慮しない加速度を出す Vector3 move( 0.0, 0.0, 0.0 ); if ( pad->isOn( Pad::UP, mId ) ){ move.z = -1.0; } if ( pad->isOn( Pad::DOWN, mId ) ){ move.z = 1.0; } if ( pad->isOn( Pad::LEFT, mId ) ){ move.x = -1.0; } if ( pad->isOn( Pad::RIGHT, mId ) ){ move.x = 1.0; } //視線方向を加味して回転 Matrix34 m; m.setRotationY( mAngleY + 180.0 ); m.multiply( &move, move ); mPosition += move; } //武器生成 if ( pad->isOn( Pad::FIRE, mId ) ){ //空き武器を探す for ( int i = 0; i < mBulletNumber; ++i ){ if ( mBullets[ i ].isEmpty() ){ mBullets[ i ].create( mDatabase, "bullet", mPosition, 45.0, mAngleY ); break; } } } //武器更新 for ( int i = 0; i < mBulletNumber; ++i ){ if ( !mBullets[ i ].isEmpty() ){ mBullets[ i ].update( enemyPos ); } } }
void Bullet::update( const Vector3& enemyPos ){ //敵の方に向ける。 Vector3 dir; Vector3 p = *mModel->getPosition(); Vector3 a = *mModel->getAngle(); if ( mHoming ){ dir.setSub( enemyPos, *mModel->getPosition() ); //自分から敵へ //Y軸角度はatan2( x, z )。 float ty = atan2( dir.x, dir.z ); //180度以上差があれば+-360度して逆回し if ( ty - a.y > 180.f ){ ty -= 2.f * 180.f; }else if ( a.y - ty > 180.f ){ ty += 2.f * 180.f; } //X軸角度はY/(X,Z)。 float zxLength = sqrt( dir.x * dir.x + dir.z * dir.z ); float tx = atan2( dir.y, zxLength ); //X軸角度はそもそも範囲が(-90,90)で180度以上離れることはない。そのままで良い。 float hx = mHomingX; float hy = mHomingY; //ホーミング範囲内ならそのものに if ( tx - a.x < hx && a.x - tx < hx ){ a.x = tx; }else if ( tx < a.x ){ a.x -= hx; }else{ a.x += hx; } if ( ty - a.y < hy && a.y - ty < hy ){ a.y = ty; }else if ( ty < a.y ){ a.y -= hy; }else{ a.y += hy; } } //おもろいのでz回転つけとくか a.z += 20.0; //角度更新 mModel->setAngle( a ); //位置はこの方向の回転行列で(0,0,1)を変換して足してやる Vector3 v( 0.0, 0.0, mSpeed ); Matrix34 m; m.setRotationY( a.y ); m.rotateX( -a.x ); m.mul( &v, v ); p += v; mModel->setPosition( p ); ++mCount; if ( mCount >= mLife ){ mCount = -1; } }
void Framework::update(){ WindowCreator wc = WindowCreator::instance(); if ( gFirst ){ const char* filename = wc.commandLineString(); if ( filename && filename[ 0 ] != '\0' ){ load( filename ); } gFirst = false; }else{ //ドラッグアンドドロップを処理する int dropN = wc.droppedItemNumber(); if ( dropN > 0 ){ const char* filename = wc.droppedItem( 0 ); //0番以外無視 load( filename ); wc.clearDroppedItem(); //これを呼ぶとfilenameもこわれるので最後に。 } } //カメラ入力反映 Input::Manager im = Input::Manager::instance(); Input::Mouse mouse = im.mouse(); Input::Keyboard keyboard = im.keyboard(); if ( mouse.isOn( Input::Mouse::BUTTON_MIDDLE ) ){ Graphics::Manager().captureScreen( "capture.tga" ); } //ビュー行列を作ろう Vector3 eyePosition = gEyeTarget; eyePosition.z += gEyeDistance; Matrix34 rm; rm.setRotationY( gAngleY ); rm.rotateX( gAngleX ); Vector3 tv( 0.f, 0.f, 1.f ); rm.mul( &tv, tv ); eyePosition.setMadd( gEyeTarget, tv, gEyeDistance ); Matrix34 zrm; zrm.setRotationZ( gAngleZ ); Vector3 up( 0.f, 1.f, 0.f ); zrm.mul( &up, up ); Matrix34 vm; vm.setViewTransform( eyePosition, gEyeTarget, up ); if ( gContainer ){ float x = static_cast< float >( mouse.velocityX() ); float y = static_cast< float >( mouse.velocityY() ); if ( mouse.isOn( Input::Mouse::BUTTON_LEFT ) && mouse.isOn( Input::Mouse::BUTTON_RIGHT ) ){ //両ボタンでZ回転 gAngleZ -= 0.2f * x; gAngleZ -= 0.2f * y; }else if ( mouse.isOn( Input::Mouse::BUTTON_LEFT ) ){ //左ボタン回転 gAngleX -= 0.2f * y; if ( gAngleX > 89.9f ){ gAngleX = 89.9f; }else if ( gAngleX < -89.9f ){ gAngleX = -89.9f; } gAngleY -= 0.5f * x; }else if ( mouse.isOn( Input::Mouse::BUTTON_RIGHT ) ){ //右ボタン、注視点移動 Vector3 xv( vm.m00, vm.m01, vm.m02 ); xv *= x; Vector3 yv( vm.m10, vm.m11, vm.m12 ); yv *= y; gEyeTarget.madd( xv, -0.003f * gEyeDistance ); gEyeTarget.madd( yv, 0.003f * gEyeDistance ); } int w = mouse.wheel(); if ( w < 0 ){ gEyeDistance *= 0.9f; }else if ( w > 0 ){ gEyeDistance *= 1.1f; } } //透視変換行列 Matrix44 pm; pm.setPerspectiveTransform( 60.f, static_cast< float >( width() ), static_cast< float >( height() ), gEyeDistance * 0.01f, gEyeDistance * 10.f ); //次にPVを作る pm *= vm; if ( keyboard.isOn( 'G' ) ){ gAngleX = gAngleY = gAngleZ = 0.f; gEyeTarget = 0.f; } //ライトでもうごかそか Graphics::Manager gm = Graphics::Manager::instance(); gm.setProjectionViewMatrix( pm ); gm.setLightingMode( LIGHTING_PER_PIXEL ); gm.enableDepthTest( true ); gm.enableDepthWrite( true ); gm.setLightColor( 0, Vector3( 1.f, 1.f, 1.f ) ); //白 gm.setLightColor( 1, Vector3( 1.f, 0.7f, 0.7f ) ); //赤 gm.setLightColor( 2, Vector3( 0.7f, 1.f, 0.7f ) ); //緑 gm.setLightColor( 3, Vector3( 0.7f, 0.7f, 1.f ) ); //青 gm.setAmbientColor( Vector3( 0.2f, 0.2f, 0.2f ) ); gm.setEyePosition( eyePosition ); float t = gEyeDistance * 0.4f; float lightIntensity[ 4 ]; for ( int i = 0; i < 4; ++i ){ lightIntensity[ i ] = t; } Vector3 lightPositions[ 4 ]; for ( int i = 0; i < 4; ++i ){ float t = static_cast< float >( gCount * ( i + 1 ) ) / 5.f; float d = gEyeDistance * 2.f; lightPositions[ i ].set( sin( t )*cos( t ) * d, sin( t )*sin( t ) * d, cos( t ) * d ); lightPositions[ i ] += gEyeTarget; } for ( int i = 0; i < 4; ++i ){ gm.setLightPosition( i, lightPositions[ i ] ); gm.setLightIntensity( i, lightIntensity[ i ] ); } for ( int i = 0; i < gModels.size(); ++i ){ gModels[ i ].draw(); } //アニメ切り替え if ( keyboard.isTriggered( ' ' ) ){ if ( gContainer.animationNumber() > 0 ){ ++gAnimationIndex; if ( gAnimationIndex >= gContainer.animationNumber() ){ gAnimationIndex = 0; } for ( int i = 0; i < gTrees.size(); ++i ){ gTrees[ i ].setAnimation( gContainer.animation( gAnimationIndex ) ); } } } for ( int i = 0; i < gTrees.size(); ++i ){ gTrees[ i ].updateAnimation(); gTrees[ i ].draw(); } if ( isEndRequested() ){ gModels.clear(); gTrees.clear(); gContainer.release(); } ++gCount; }
void Robo::update( Robo* enemy ){ //死んでる if ( mHitPoint <= 0 ){ return; } //角度範囲補正 if ( mAngleY > 180.0 ){ mAngleY -= 360.0; }else if ( mAngleY < -180.0 ){ mAngleY += 360.0; } //AIの思考。プレイヤーの場合は入力を取得して返すだけ bool iJump; bool iFire; bool iTurn; bool iLeft; bool iRight; bool iUp; bool iDown; think( &iJump, &iFire, &iTurn, &iLeft, &iRight, &iUp, &iDown ); //以下もらった入力を使って行動 const Vector3& enemyPos = *enemy->position(); ++mCount; //ジャンプ押されてる? double t; //字句解析に似た書き方をしてみよう。コードの重複が増えるがブロック単位で見ればシンプルになる。 //普通の書き方とどちらが良いか比べてみよう。 switch ( mMode ){ case MODE_JUMP_UP: //カメラが回りきっていないならカメラ回転継続 if ( mCameraCount < mCameraDelayCount ){ mAngleY += mAngleVelocityY; ++mCameraCount; } //上昇 t = mJumpHeight / static_cast< double >( mJumpUpTime ); mVelocity.y = t; if ( !iJump ){ //ジャンプ入力がないので下降に変化 mMode = MODE_JUMP_FALL; mCount = 0; }else if ( mCount >= mJumpUpTime ){ //上昇終了 mMode = MODE_JUMP_STAY; mCount = 0; } mVelocity.x = mVelocity.z = 0.0; //X,Z移動を抹殺 break; case MODE_JUMP_STAY: //カメラが回りきっていないならカメラ回転継続 if ( mCameraCount < mCameraDelayCount ){ mAngleY += mAngleVelocityY; ++mCameraCount; } mVelocity.y = 0.0; if ( !iJump ){ //ジャンプ入力がないので下降に変化 mMode = MODE_JUMP_FALL; mCount = 0; }else if ( mCount >= mJumpStayTime ){ //下降へ mMode = MODE_JUMP_FALL; mCount = 0; } break; case MODE_JUMP_FALL: //カメラが回りきっていないならカメラ回転継続 if ( mCameraCount < mCameraDelayCount ){ mAngleY += mAngleVelocityY; ++mCameraCount; } //下降 t = mJumpHeight / static_cast< double >( mJumpFallTime ); mVelocity.y = -t; //接地判定は最終的には衝突処理でやるのでここではやらない。 break; case MODE_ON_LAND: if ( iJump ){ mMode = MODE_JUMP_UP; mCount = 0; mCameraCount = 0; //敵の方に向ける。 Vector3 dir; dir.setSub( enemyPos, mPosition ); //自分から敵へ //Y軸角度はatan2( x, z )。 t = GameLib::atan2( dir.x, dir.z ); //180度以上差があれば+-360度して逆回し if ( t - mAngleY > 180.0 ){ t -= 360.0; }else if ( mAngleY - t > 180.0 ){ t += 360.0; } mAngleVelocityY = ( t - mAngleY ) / static_cast< double >( mCameraDelayCount ); }else if ( iTurn ){ turn( iLeft, iRight ); //コードが長くなるので関数に飛ばす }else{ move( iLeft, iRight, iUp, iDown ); //コードが長くなるので関数に飛ばす } mVelocity.y = 0.0; break; } //ここから下は衝突処理が入るとその後になる。 mPosition += mVelocity; if ( mPosition.y < 0.0 ){ mPosition.y = 0.0; mMode = MODE_ON_LAND; } //武器生成 if ( iFire ){ //上昇、下降中は撃てない if ( ( mMode != MODE_JUMP_FALL ) && ( mMode != MODE_JUMP_UP ) ){ //エネルギー足りてる? if ( mEnergy >= mEnergyPerBullet ){ //空き武器を探す for ( int i = 0; i < mBulletNumber; ++i ){ if ( mBullets[ i ].isEmpty() ){ unsigned c = ( mId == 0 ) ? 0xcc0088ff : 0xccff8800; mBullets[ i ].create( mDatabase, "bullet", mPosition, 15.0, mAngleY, mLockOn, c ); mEnergy -= mEnergyPerBullet; break; } } } } } //武器更新 for ( int i = 0; i < mBulletNumber; ++i ){ if ( !mBullets[ i ].isEmpty() ){ mBullets[ i ].update( enemyPos ); //衝突処理 Vector3 t; t.setSub( *mBullets[ i ].position(), enemyPos ); if ( t.squareLength() < 4.0 ){ enemy->setDamage( 1 ); //1点減らしてみた。 mBullets[ i ].die(); //弾消えます。 } } } //武器エネルギーチャージ mEnergy += mEnergyCharge; if ( mEnergy > mMaxEnergy ){ mEnergy = mMaxEnergy; } //ロックオン処理 //まずは角度を測ってみよう。 //角度は何で測るか?そう、内積だ。 Vector3 toEnemy; toEnemy.setSub( enemyPos, mPosition ); Vector3 myDir( 0.0, 0.0, -1.0 ); Matrix34 m; m.setRotationY( mAngleY + 180.0 ); m.multiply( &myDir, myDir ); toEnemy *= 1.0 / toEnemy.length(); //長さを1に double dotProduct = toEnemy.dot( myDir ); //角度に直すと、 double angle = GameLib::acos( dotProduct ); if ( mLockOn ){ //ロックオンしてるなら外れるかどうか調べる if ( angle > mLockOnAngleOut ){ mLockOn = false; } }else{ //入るかどうか調べる if ( angle < mLockOnAngleIn ){ mLockOn = true; } } }
void Robo::move( bool left, bool right, bool up, bool down ){ //移動処理。まず視点を考慮しない加速度を出す Vector3 move( 0.0, 0.0, 0.0 ); if ( up ){ move.z = -1.0; } if ( down ){ move.z = 1.0; } if ( left ){ move.x = -1.0; } if ( right ){ move.x = 1.0; } //視線方向を加味して回転 Matrix34 m; m.setRotationY( mAngleY + 180.0 ); m.multiply( &move, move ); //今止まっているなら話は早い。適当に加速してやるだけだ if ( mVelocity.x == 0.0 && mVelocity.z == 0.0 ){ //加速にかかる時間で最大速度を割れば1フレームあたりの加速度が出る。 double accel = mMaxMoveSpeed / static_cast< double >( mMoveAccelEndCount ); mVelocity.setMul( move, accel ); }else{ //すでに動いている場合 if ( move.x == 0.0 && move.z == 0.0 ){ //移動がゼロ mVelocity.set( 0.0, 0.0, 0.0 ); //移動はとまる。 }else{ //すでに動いている場合かなり面倒である。 //45度だけ方向転換した時にゼロから加速しなおしというのはストレスだ。 //だから、「今の速度と方向が合わない成分だけをゼロからやり直し」にする。 //90度以上のターンなら一旦速度を0にする。 //慣性が働く方がいいゲームもあるが、きびきび動かしたいなら慣性は邪魔だろう。 //90度以上のターンなら現速度と加速の内積がマイナスのはずだ double dp = mVelocity.dot( move ); if ( dp <= 0.0 ){ mVelocity.set( 0.0, 0.0, 0.0 ); }else{ //90度未満 //現在の移動速度と水平な成分のみ取り出す //水平成分は、移動方向単位ベクタとの内積を、移動方向単位ベクタにかければいい。 //移動単位ベクタE、現速度ベクタVとして、新しい速度ベクタV'すなわち平行成分は //V' = dot(V,E) * E //この時、Eは移動ベクタMを使ってE=M/|M|と書けるから、 //V' = dot(V,M) * M / ( |M|^2 ) //書け、単位ベクタを作る際の平方根を除ける。|M|より|M|^2の方が計算は速いのだ。 double moveSquareLength = move.x * move.x + move.z * move.z; double dp = mVelocity.dot( move ); mVelocity.setMul( move, dp / moveSquareLength ); } //加速を加える。 //移動速度は最大速度/加速時間である。 double accel = mMaxMoveSpeed / static_cast< double >( mMoveAccelEndCount ); mVelocity.madd( move, accel ); //最大速度でストップ double speed = mVelocity.length(); if ( speed > mMaxMoveSpeed ){ mVelocity *= mMaxMoveSpeed / speed; } } } }