図0022a
1、Rigidbodyクラス(構造体)の実装 2、Rigidbodyマネージャの実装
struct Rigidbody { //オーナー weak_ptr<GameObject> m_Owner; //重力加速度 Vec3 m_Gravity; //質量 float m_Mass; //現在のフォース Vec3 m_Force; //速度 Vec3 m_Velocity; //コリジョンのタイプ CollType m_CollType; //Fixedかどうか bool m_IsFixed; //スケール Vec3 m_Scale; //回転 Quat m_Quat; //位置 Vec3 m_Pos; //1つ前のスケール Vec3 m_BeforeScale; //1つ前の回転 Quat m_BeforeQuat; //1つ前の位置 Vec3 m_BeforePos; //中略 };
enum class CollType { typeNone, typeSPHERE, typeCAPSULE, typeOBB, };
void Player::OnCreate() {
vector<VertexPositionNormalTexture> vertices;
vector<uint16_t> indices;
MeshUtill::CreateSphere(1.0f, 18, vertices, indices);
//メッシュの作成(変更できない)
m_SphereMesh = MeshResource::CreateMeshResource(vertices, indices, false);
//タグの追加
AddTag(L"Player");
//Rigidbodyの初期化
auto PtrGameStage = GetStage<GameStage>();
Rigidbody body;
body.m_Owner = GetThis<GameObject>();
body.m_Mass = 1.0f;
body.m_Scale = Vec3(0.25f);
body.m_Quat = Quat();
body.m_Pos = m_Posision;
body.m_CollType = CollType::typeSPHERE;
body.SetToBefore();
PtrGameStage->AddRigidbody(body);
}
class GameStage : public Stage {
Vec4 m_LightDir; ///<ライト向き
Camera m_Camera; ///<カメラ
//RigidbodyManager
shared_ptr<RigidbodyManager> m_RigidbodyManager;
public:
//中略
};
void Player::OnUpdate() {
auto& body = GetStage<GameStage>()->GetOwnRigidbody(GetThis<GameObject>());
//前回のターンからの経過時間を求める
float ElapsedTime = App::GetApp()->GetElapsedTime();
//コントローラの取得
auto CntlVec = App::GetApp()->GetInputDevice().GetControlerVec();
if (CntlVec[0].bConnected) {
if (!m_JumpLock) {
//Aボタン
if (CntlVec[0].wPressedButtons & XINPUT_GAMEPAD_A) {
body.m_BeforePos.y += 0.01f;
body.m_Pos.y += 0.01f;
body.m_Velocity += Vec3(0, 4.0f, 0);
m_JumpLock = true;
}
}
Vec3 Direction = GetMoveVector();
if (length(Direction) < 0.1f) {
body.m_Velocity.x *= 0.9f;
body.m_Velocity.z *= 0.9f;
}
else {
//フォースで変更する場合は以下のように記述
//body.m_Force += Direction * 10.0f;
//速度で変更する場合は以下のように記述
body.m_Velocity += Direction * 0.5f;
Vec2 TempVelo(body.m_Velocity.x, body.m_Velocity.z);
TempVelo = XMVector2ClampLength(TempVelo, 0, 5.0f);
body.m_Velocity.x = TempVelo.x;
body.m_Velocity.z = TempVelo.y;
}
}
body.m_Force += body.m_Gravity * body.m_Mass;
}
auto& body = GetStage<GameStage>()->GetOwnRigidbody(GetThis<GameObject>());
//速度で変更する場合は以下のように記述 body.m_Velocity += Direction * 0.5f;
body.m_Force += Direction * 10.0f;
Vec3 accel = m_Force * m_Mass; m_Velocity += accel * ElapsedTime;
加速度 = Force / 質量
body.m_Force += body.m_Gravity * body.m_Mass;
Force = 質量 * 加速度
void Player::OnUpdate2() {
auto& body = GetStage<GameStage>()->GetOwnRigidbody(GetThis<GameObject>());
if (body.m_Pos.y <= m_BaseY) {
body.m_Pos.y = m_BaseY;
body.m_Velocity.y = 0;
m_JumpLock = false;
}
auto& StateVec = GetStage<GameStage>()->GetCollisionStateVec();
for (auto& v : StateVec) {
if (v.m_Src == &body) {
Vec3 Normal = v.m_SrcHitNormal;
Normal.normalize();
Vec4 v = (Vec4)XMVector3AngleBetweenNormals(Vec3(0, 1, 0), Normal);
if (v.x < 0.1f) {
m_JumpLock = false;
break;
}
}
if (v.m_Dest == &body) {
Vec3 Normal = v.m_SrcHitNormal;
Normal.normalize();
Vec4 v = (Vec4)XMVector3AngleBetweenNormals(Vec3(0, 1, 0), Normal);
if (v.x < 0.1f) {
m_JumpLock = false;
break;
}
}
}
auto LenVec = body.m_Pos - body.m_BeforePos;
LenVec.y = 0;
auto Len = LenVec.length();
if (Len > 0) {
Vec3 Cross = cross(Vec3(0, 1, 0), LenVec);
Quat Span(Cross, Len / 0.5f);
body.m_Quat *= Span;
}
}
class RigidbodyManager : public GameObject { //Rigidbodyの配列 vector<Rigidbody> m_RigidbodyVec; //衝突判定 void CollisionDest(Rigidbody& Src); bool CollisionStateChk(Rigidbody* p1, Rigidbody* p2); bool CollisionTest(Rigidbody& Src, Rigidbody& Dest, CollisionState& state); //衝突ステートの配列 vector<CollisionState> m_CollisionStateVec; public: //-------------------------------------------------------------------------------------- /*! @brief コンストラクタ @param[in] StagePtr ステージのポインタ */ //-------------------------------------------------------------------------------------- RigidbodyManager(const shared_ptr<Stage>& StagePtr); //-------------------------------------------------------------------------------------- /*! @brief デストラクタ */ //-------------------------------------------------------------------------------------- virtual ~RigidbodyManager(); //-------------------------------------------------------------------------------------- /*! @brief Rigidbodyの配列を得る @return Rigidbodyの配列 */ //-------------------------------------------------------------------------------------- const vector<Rigidbody>& GetRigidbodyVec()const { return m_RigidbodyVec; } vector<Rigidbody>& GetRigidbodyVec() { return m_RigidbodyVec; } //-------------------------------------------------------------------------------------- /*! @brief 衝突情報の配列を得る @return 衝突情報の配列 */ //-------------------------------------------------------------------------------------- const vector<CollisionState>& GetCollisionStateVec()const { return m_CollisionStateVec; } vector<CollisionState>& GetCollisionStateVec(){ return m_CollisionStateVec; } //-------------------------------------------------------------------------------------- /*! @brief 指定のオーナーのRigidbodyを得る @param[in] OwnerPtr オーナーのポインタ @return 指定のオーナーのRigidbody */ //-------------------------------------------------------------------------------------- Rigidbody& GetOwnRigidbody(const shared_ptr<GameObject>& OwnerPtr) { for (auto& v : m_RigidbodyVec) { auto shptr = v.m_Owner.lock(); if (shptr == OwnerPtr) { return v; } } throw BaseException( L"指定のRigidbodyが見つかりません", L"!Rigidbody", L"RigidbodyManager::GetOwnRigidbody()" ); } //-------------------------------------------------------------------------------------- /*! @brief 初期化 @return なし */ //-------------------------------------------------------------------------------------- virtual void OnCreate() override {} //-------------------------------------------------------------------------------------- /*! @brief フォースを初期化する @return なし */ //-------------------------------------------------------------------------------------- void InitRigidbody(); //-------------------------------------------------------------------------------------- /*! @brief SrcのDestからのエスケープ @param[in] Src Srcのポインタ @param[in] Dest Destのポインタ @return なし */ //-------------------------------------------------------------------------------------- void Escape(Rigidbody* Src, Rigidbody* Dest); //-------------------------------------------------------------------------------------- /*! @brief 更新 @return なし */ //-------------------------------------------------------------------------------------- virtual void OnUpdate()override; //-------------------------------------------------------------------------------------- /*! @brief 最終更新 @return なし */ //-------------------------------------------------------------------------------------- virtual void OnUpdate2()override; //-------------------------------------------------------------------------------------- /*! @brief 描画 @return なし */ //-------------------------------------------------------------------------------------- virtual void OnDraw()override {} };
//Rigidbodyの配列 vector<Rigidbody> m_RigidbodyVec; //中略 //衝突ステートの配列 vector<CollisionState> m_CollisionStateVec;
struct CollisionState { Rigidbody* m_Src; Vec3 m_SrcHitNormal; Rigidbody* m_Dest; Vec3 m_DestHitNormal; float m_HitTime; };
void GameStage::OnCreate() { //Rigidbodyマネージャの初期化 m_RigidbodyManager = ObjectFactory::Create<RigidbodyManager>(GetThis<GameStage>()); //以下略 }
void GameStage::OnUpdateStage() { //ターン毎の初期化 m_RigidbodyManager->InitRigidbody(); for (auto& v : GetGameObjectVec()) { //各オブジェクトの更新 v->OnUpdate(); } //Rigidbodyマネージャの更新(衝突判定など) m_RigidbodyManager->OnUpdate(); for (auto& v : GetGameObjectVec()) { //各オブジェクトの最終更新 v->OnUpdate2(); } //自分自身の更新(カメラ) this->OnUpdate(); //Rigidbodyマネージャの最終更新(衝突判定情報のクリア) m_RigidbodyManager->OnUpdate2(); }
//ターン毎の初期化 void RigidbodyManager::InitRigidbody() { //1つ前の位置にセットとフォースの初期化 for (auto& v : m_RigidbodyVec) { v.SetToBefore(); v.m_Force = Vec3(0); } }
void RigidbodyManager::OnUpdate() { //前回のターンからの経過時間を求める float ElapsedTime = App::GetApp()->GetElapsedTime(); //フォースから速度に変換 for (auto& v : m_RigidbodyVec) { Vec3 accel = v.m_Force * v.m_Mass; v.m_Velocity += accel * ElapsedTime; } //衝突判定を行い、ヒットがあれば速度を変更する if (m_RigidbodyVec.size() >= 2) { //衝突判定 for (auto& v : m_RigidbodyVec) { CollisionDest(v); } } if (m_CollisionStateVec.size() >= 2) { //ヒットタイムでソート(ヒットタイムが)近いものに対応 auto func = [&](CollisionState& Left, CollisionState& Right)->bool { return (Left.m_HitTime < Right.m_HitTime); }; std::sort(m_CollisionStateVec.begin(), m_CollisionStateVec.end(), func); } //衝突応答 for (auto& v : m_CollisionStateVec) { if (!v.m_Src->m_IsFixed) { v.m_Src->Move(v.m_HitTime); v.m_Src->m_Velocity.slide(v.m_SrcHitNormal); } if (!v.m_Dest->m_IsFixed) { v.m_Dest->Move(v.m_HitTime); v.m_Dest->m_Velocity.slide(v.m_DestHitNormal); } } //設定された速度をもとに衝突無しのオブジェクトの位置の決定 for (auto& v : m_RigidbodyVec) { v.Move(ElapsedTime); } //エスケープ処理 for (auto& v : m_CollisionStateVec) { if (!v.m_Src->m_IsFixed) { Escape(v.m_Src, v.m_Dest); } if (!v.m_Dest->m_IsFixed) { Escape(v.m_Dest ,v.m_Src); } } }
//前回のターンからの経過時間を求める float ElapsedTime = App::GetApp()->GetElapsedTime(); //フォースから速度に変換 for (auto& v : m_RigidbodyVec) { Vec3 accel = v.m_Force * v.m_Mass; v.m_Velocity += accel * ElapsedTime; }
//衝突判定を行い、ヒットがあれば速度を変更する if (m_RigidbodyVec.size() >= 2) { //衝突判定 for (auto& v : m_RigidbodyVec) { CollisionDest(v); } }
if (m_CollisionStateVec.size() >= 2) { //ヒットタイムでソート(ヒットタイムが)近いものに対応 auto func = [&](CollisionState& Left, CollisionState& Right)->bool { return (Left.m_HitTime < Right.m_HitTime); }; std::sort(m_CollisionStateVec.begin(), m_CollisionStateVec.end(), func); } //衝突応答 for (auto& v : m_CollisionStateVec) { if (!v.m_Src->m_IsFixed) { v.m_Src->Move(v.m_HitTime); v.m_Src->m_Velocity.slide(v.m_SrcHitNormal); } if (!v.m_Dest->m_IsFixed) { v.m_Dest->Move(v.m_HitTime); v.m_Dest->m_Velocity.slide(v.m_DestHitNormal); } }
1、同じターン内で複数の衝突があった場合、本来なら、最初の衝突の応答を計算して、 その後それに合わせた判定を行わなければならないが、簡略化している。 2、本来なら、回転も応答処理に入れるべきだが、その処理は行っていない 3、回転要素を加味しない代わりに、スライドと呼ばれる処理で移動速度を変更している
Escape(v.m_Src, v.m_Dest);
void RigidbodyManager::OnUpdate2() { //衝突情報のクリア m_CollisionStateVec.clear(); }
//プレイヤーの作成 AddGameObject<Player>( L"TRACE_TX", true, Vec3(0.0f, 0.125f, 0.0f) ); //カプセルプレイヤーの作成 //AddGameObject<CapsulePlayer>( // L"TRACE_TX", // true, // Vec3(0.0f, 0.25f, 0.0f) // ); //OBBプレイヤーの作成 //AddGameObject<ObbPlayer>( // L"TRACE_TX", // true, // Vec3(0.0f, 0.125f, 0.0f) // );