ゲームプログラムのメモ書き

DirectX

丸いぞ!バウンディングスフィア

今回は当たり判定を作ります。アクションでもシューティングでもRPGでも、 ゲームには何かしらの当たり判定が入っています。まずはスフィア(球)での当たり判定です。



struct SPHERE
{
    D3DXVECTOR3  vCenter;   //中心座標
    FLOAT        fRadius;   //半径
};

struct THING
{
    LPD3DXMESH       pMesh;
    LPD3DXMESH       pShereMesh;
    D3DMATERIAL9*    pShereMaterials;
    D3DXVECTOR3      vPos;
    SPHERE           Sphere;
};

今まで使用していたThing構造体にSPHERE、pShereMesh,pShereMaterialsを追加します。 この二つはSPHERE構造体に放り込んでも良いのですが、今回はこうしておきます。





まずは、スフィアの作成をします。

HRESULT InitSphere(LPDIRECT3DDEVICE9 pDevice, THING *pThing)
{
    HRESULT hr;
    
    LPDIRECT3DVERTEXBUFFER9  pVB = NULL;        //頂点バッファ
    VOID*                    pVertices = NULL;  //メモリバッファのポインタ
    D3DXVECTOR3              vCenter;           //中心座標
    FLOAT                    fRadius;           //半径

    //頂点バッファ内のデータを取得する
    if(FAILED(hr = pThing->pMesh->GetVertexBuffer(&pVB)))
    {
        return hr;
    }
    
    if(FAILED(hr = pVB->Lock(
        0,              //全体をロック
        0,              //全体をロック
        &pVertices,
        0)))
    {
        SAFE_RELEASE(pVB);
        return hr;
    }
        
    hr = D3DXComputeBoundingSphere(
        (D3DXVECTOR3*)pVertices,
        pThing->pMesh->GetNumVertices(),    //頂点数
        D3DXGetFVFVertexSize(pThing->pMesh->GetFVF()),
        &vecCenter, &fRadius);  //返される中心座標と半径

    //ロック解除
    pVB->Unlock();
    SAFE_RELEASE(pVB);

    if(FAILED(hr)) return hr;
        
    pThing->Sphere.vCenter = vCenter;   //中心
    pThing->Sphere.fRadius = fRadius;   //半径

    //得られた中心と半径を基にメッシュとしてのスフィアを作成する
    if(FAILED(hr = D3DXCreateSphere(
        pDevice,
        fRadius,    //半径
        24,         //主軸を回転軸としたスライスの数
        24,         //主軸に沿ったスタックの数
        &pThing->pShereMesh,
        NULL)))
    {
        return hr;
    }
        
    //スフィアメッシュのマテリアル白色、半透明、光沢有)
    pThing->pShereMaterials = new D3DMATERIAL9; //初期化
    pThing->pShereMaterials->Diffuse.a = 0.5f;  //半透明
    pThing->pShereMaterials->Diffuse.r = 1.0f;
    pThing->pShereMaterials->Diffuse.g = 1.0f;
    pThing->pShereMaterials->Diffuse.b = 1.0f;

    pThing->pShereMaterials->Ambient = pThing->pShereMaterials->Diffuse;

    pThing->pShereMaterials->Specular.r = 1.0f;
    pThing->pShereMaterials->Specular.g = 1.0f;
    pThing->pShereMaterials->Specular.b = 1.0f;

    pThing->pShereMaterials->Emissive.r = 0.1f;
    pThing->pShereMaterials->Emissive.g = 0.1f;
    pThing->pShereMaterials->Emissive.b = 0.1f;

    //光沢の度合い
    pThing->pShereMaterials->Power = 120.0f;

    return S_OK;
}

D3DXComputeBoundingSphere関数はメッシュの外接円の中心と半径を計算しています。
第二引数の頂点数はXファイルの場合、数が不明なので関数を使用し頂点数を取得しています。
また、D3DXCreateSphere関数から後は当たり判定には直接関係ありませんが、
スフィアを視認出来る様にスフィアを作成しています。





次に、実際に当たり判定をする関数を作ります。

//当たり判定(衝突判定)
BOOL Impact(THING *pThingA, THING *pThingB)
{
    //2つの物体の中心間の距離を求める
    D3DXVECTOR3 vecLength = pThingB->vecPosition - pThingA->vecPosition;
    FLOAT fLength = D3DXVec3Length(&vecLength);

    if(fLength < pThingA->Sphere.fRadius + pThingB->Sphere.fRadius)
    {
        return TRUE;    //衝突している
    }

    return fALSE;       //衝突していない
}

対象となる物体を引数として呼び出し、物体同士の中心間の距離(ベクトル)を求めています。 次にそのベクトルをD3DXVec3Length関数で長さに直します。





if(fLength < pThingA->Sphere.fRadius + pThingB->Sphere.fRadius)
{
    return TRUE;    //衝突している
}

return fALSE;       //衝突していない

ここで衝突判定をしています。fLengthが2つの物体の半径を足したものと比較して小さければ衝突していることになります。




VOID Render()
{
    //スフィアの表示
    for(int i = 0 ; i < THING_AMOUNT ; i++)
    {
        pDevice->SetMaterial(Thing[i].pShereMaterials);
        Thing[i].pShereMesh->DrawSubset(0);
    }
    
    //当たり判定
    if(Impact(&Thing[0], &Thing[1]))
    {
        //当たったときの処理
    }
}

スフィアの作成こそ面倒なものの、実際の当り判定は結構楽なものです。