アクションゲームなどでよくある敵をロックオンするような機能を実装していきたいと思います。
ロックオンする敵
プレイヤーの正面ベクトルとプレイヤーからエネミーに伸びるベクトル間の角度θを求めて、
プレイヤーとエネミーの距離を掛けます。出た値が一番低い、すなわち距離が近くてプレイヤーの正面に居る敵をロックオンするようにします。
a = (θの絶対値)×(プレイヤーとエネミーの距離)
aの値が一番低い敵をロックオンする
Player.cpp
std::vector<Enemy*> m_enemyList; //エネミーの配列
Vector3 m_position //プレイヤーの座標
bool m_isRockOn = false;
//プレイヤーの向いている角度を計算します
float degreep = atan2f(forward.x, forward.z);
//計算して出た暫定的に一番小さい角度を記憶する変数です
float degreemum= M_PI * 2;
for(Enemy* enemy : m_enemyList)
{
Vector3 pos = m_position - enemy->GetPosition();
//プレイヤーとエネミーの距離が一定外だったら下の処理をスキップします。
if (pos.Length() >= 500)
continue;
//プレイヤーとエネミーを結ぶベクトルを出します
CVector3 pos2 = enemy->GetPosition() - m_position;
//y座標、すなわち高さを0にします
pos2.y = 0.0f;
//ベクトルを正規化します
pos2.Normalize();
//プレイヤーとエネミーを結ぶベクトルの角度を計算します
float degree = atan2f(pos2.x, pos2.z);
//ここら辺のif文要らない可能性が微レ存、「プレイヤーの正面のベクトルの角度」と
//「プレイヤーとエネミーを結ぶベクトルの角度」の差を計算します
if (M_PI <= (degreep - degree)) {
degree = degreep - degree - M_PI * 2;
}
else if (-M_PI >= (degreep - degree)) {
degree = degreep - degree + M_PI * 2;
}
else {
degree = degreep - degree;
}
//求めた角度にプレイヤーとエネミーの距離に応じて補正をかけます、距離が長いほど補正は大きいです(値が大きくなります)
degree = degree + degree * (pos.Length() / 500) * 0.3f;
//求めた値を比較していき、一番小さい値を決めていきます
if (fabs(degreemum) >= fabs(degree)) {
degreemum = degree;
}
}
//求めた一番小さい値が一定値より小さい場合、ターゲッティングをオンにします
if (fabs(degreemum) <= M_PI / 3) {
m_isRockOn = true;
}
//逆に一定値より大きい場合、ターゲッティングをオフにします
else {
m_isRockOn = false;
}
カメラ
ロックオンした時のカメラの挙動ですが、カメラの注視点を敵の座標に、カメラ自体の座標はプレイヤーより敵から離れた座標に設定します。
Camera.cpp
Vector3 m_position; //カメラの座標
Vector3 m_target; //カメラの注視点
Vector3 m_playerPosition; //プレイヤーの座標
Vector3 m_enemyPosition; //ロックオンするエネミーの座標
//注視点はエネミーの座標にします。
m_target = m_enemyPosition;
//エネミーからプレイヤーに伸びるベクトルを求めます。
Vector3 pos = m_playerPosition - m_enemyPosition;
//カメラの高さは一定にしたいので、y成分を0にします。
pos.y = 0.0f;
//ベクトルを正規化します。
pos.Normalize();
//スカラーを掛けます
pos *= 50.0f;
//カメラがどれだけプレイヤーの座標より高いかを設定します。
pos.y = 50.0f;
//プレイヤーの座標に求めたベクトルを足して、カメラの座標とします。
m_position = m_playerPosition + pos;