敵キャラの実装例

キャラデータ
※この圧縮ファイルを解凍して、できたファイルを下の図のように、 Tuto11で作ったプロジェクトのDebug フォルダにコピーします。


以下プログラムの改造点です。

キャラ実装クラス(新規ファイル名:Enemy01.cpp)

/*
 *    class CEnemy01
 *        敵キャラの実装クラス
 *
 *        First edition: February.19.2004
 *
*/
#include "stdafx.h"

#include <tchar.h>
#include <d3dx9.h>
#include "D3DQuickLib.h"
#include "Mesh.h"
#include "Enemy01.h"

/*==================================================
*    共有メッシュ
*    複数の敵キャラインスタンスから参照できるしくみ
*    を提供する。
====================================================*/
CMesh    *g_pEnemy01Mesh=NULL;

//-------------------------------------------------------------
//    Name: InitEnemy01
//  Desc: 共有メッシュへのポインタの初期化
//-------------------------------------------------------------
void    InitEnemy01Graphics(CD3DEnv *pEnv)
{
    DeleteEnemy01Graphics();
    if (g_pEnemy01Mesh == NULL)
        g_pEnemy01Mesh = new CMesh(pEnv,"smile.x");
}


//-------------------------------------------------------------
//    Name: DeleteEnemy01
//  Desc: 共有メッシュへのポインタの初期化
//-------------------------------------------------------------
void    DeleteEnemy01Graphics()
{
    if (g_pEnemy01Mesh){
        delete    g_pEnemy01Mesh;
        g_pEnemy01Mesh = NULL;
    }
}


//-------------------------------------------------------------
//    Name: CEnemy01
//  Desc: コンストラクタ
//-------------------------------------------------------------
CEnemy01::CEnemy01(float x, float z)
{
    float    f;
    f = (float)rand();
    f /= (float)(RAND_MAX+1);
    m_fX = x;
    m_fZ = z;
    m_fAngle = D3DX_PI * 2.0f * ((float)rand() / (float)(RAND_MAX + 1));
    D3DXMatrixRotationY(&m_matDir,m_fAngle);
    D3DXMatrixIdentity(&m_matWorld);
}

CEnemy01::~CEnemy01()
{
}

//-------------------------------------------------------------
//    Name: Update
//  Desc: フレーム毎処理(アニメーション)
//        timeElapsed: 前フレームからの経過時間
//        pPos: プレーヤの位置
//        死亡時 true を返す
//-------------------------------------------------------------
BOOL    CEnemy01::Update(float    timeElapsed, D3DXVECTOR3 *pPos)
{
    D3DXMATRIX matTmp;
    D3DXVECTOR3    vecDir = D3DXVECTOR3(0.0f,0.0f,-1.0f);    
    D3DXVECTOR3    vecCross, vecDot;
    D3DXVECTOR3    vecPlayer;
    FLOAT        fDiff;

    //
    //    アルゴリズム:単にプレーヤを追いかけるだけ
    //

    //    敵キャラから見たプレーヤの位置を算出
    vecPlayer.x = pPos->x - m_fX;
    vecPlayer.z = pPos->z - m_fZ;
    vecPlayer.y = 0.0f;
    //    ベクトルの長さを1.0 にして、
    //    プレーヤの位置−>プレーヤのいる方向
    D3DXVec3Normalize(&vecPlayer,&vecPlayer);

    //    キャラクタの向きをベクトルで表す。
    D3DXVec3TransformCoord(&vecDir,&vecDir,&m_matDir);
    
    //    キャラクタの向きとプレーヤへの向きから、
    //    プレーヤを旋回させる角度を算出する。
    
    //    方法:キャラの方向ベクトルとプレーヤへの方向ベクトルとの
    //    ベクトル積(外積)をとり、
    //    結果のベクトルが上向きなら、時計回り
    //    結果のベクトルが下向きなら、反時計回り

    D3DXVec3Cross(&vecCross,&vecDir, &vecPlayer);    //    外積を算出

    FLOAT    fDot = D3DXVec3Dot(&vecDir,&vecPlayer);    //    内積を算出

    static const FLOAT    fError = sinf(D3DX_PI / 180.0f);
    if ((fabs(vecCross.y) < fError)&&(fDot > 0)){    //    誤差±1度
        fDiff = 0.0f;
    }else{
        fDiff = timeElapsed * (D3DX_PI / 400.0f);
        if (vecCross.y < 0.0f)
            fDiff = -fDiff;
    }
    m_fAngle += fDiff;
    D3DXMatrixRotationY(&m_matDir, m_fAngle);
    fDiff = 0.01f * timeElapsed;

    //    キャラクタの向きに一定量移動する。
    vecDir.y = 0;
    D3DXVECTOR3 vecMotion = vecDir * fDiff;

    m_fX += vecMotion.x;
    m_fY += vecMotion.y;
    m_fZ += vecMotion.z;

    D3DXMatrixTranslation(&matTmp,m_fX,m_fY,m_fZ);

    m_matWorld = m_matDir * matTmp;

    return    false;
}

//-------------------------------------------------------------
//    Name: Render
//  Desc: 描画処理
//-------------------------------------------------------------
void    CEnemy01::Render(LPDIRECT3DDEVICE9 lpd3ddev)
{
    lpd3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
    lpd3ddev->SetRenderState(D3DRS_LIGHTING, TRUE );
    lpd3ddev->SetTransform( D3DTS_WORLD, &m_matWorld );
    g_pEnemy01Mesh->Render(lpd3ddev);
}

キャラ実装クラスの宣言(新規ファイル名:Enemy01.h)

/*
 *    class CEnemy
 *        敵キャラの実装クラス
 *
 *        First edition: February.19.2004
 *
*/
#ifndef __ENEMY01_H__
#define    __ENEMY01_H__

class CEnemy01
{
public:
    CEnemy01(float x, float y);
    virtual ~CEnemy01();
    BOOL    Update(float timeElapsed,D3DXVECTOR3 *pPos);
    void    Render(LPDIRECT3DDEVICE9 lpd3ddev);
private:
    D3DXMATRIX    m_matWorld;    //    ワールド座標への変換行列
    D3DXMATRIX    m_matDir;    //    キャラクタの向きを表す行列

    FLOAT        m_fAngle;    //    キャラクタの向き
    FLOAT        m_fX;        //    キャラクタの座標
    FLOAT        m_fY;        //    キャラクタの座標
    FLOAT        m_fZ;        //    キャラクタの座標
};

//    グラフィックスデータは敵キャラクタクラスとは別に初期化する。
extern void    InitEnemy01Graphics(CD3DEnv    *pEnv);
extern void    DeleteEnemy01Graphics();

#endif

Tuto11プログラムの改造点

#include "stdafx.h"
#include <stdio.h>
#include <d3dx9.h>
#include "D3DQuickLib.h"
#include "Enemy01.h"

CD3DEnv    *g_pD3DEnv = NULL; // D3DQuickLib が提供する描画環境。
CFloor *g_pFloor = NULL;
CEnhancedMesh *g_pMesh = NULL; // D3DQuickLib によるモデリングクラス。
float   g_fTime = 0;       // アニメーションで使用する
CEnemy01    *g_pEnemy01 = NULL;

D3DXVECTOR3    g_vPos = D3DXVECTOR3(0,0,0);
D3DXVECTOR3    g_vDir = D3DXVECTOR3(0,0,1.0f);
FLOAT    g_fAngle = 0.0f;
LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);

//------------------------------------------------------------
// Name: FrameMove
// Desc: アニメーションを行う。
//        UpdateScene 関数 からコールバックされる。
//------------------------------------------------------------
void FrameMove(float timeElapsed)
{
    g_fTime += timeElapsed;
    if (g_fTime > 600.0f)
        g_fTime -= 600.0f;

    D3DXMATRIX    *pView;
    pView = g_pD3DEnv->GetSystemView();
    D3DXVECTOR3    vecDir = D3DXVECTOR3(0,0,0);
    if (g_pD3DEnv->GetDI8KeyState(DIK_LEFT)){
        vecDir.x -= 1.0f;
        g_vPos.x -= 0.1f * timeElapsed;
    }
    if (g_pD3DEnv->GetDI8KeyState(DIK_RIGHT)){
        vecDir.x += 1.0f;
        g_vPos.x += 0.1f * timeElapsed;
    }
    if (g_pD3DEnv->GetDI8KeyState(DIK_UP)){
        vecDir.z += 1.0f;
        g_vPos.z += 0.1f * timeElapsed;
    }
    if (g_pD3DEnv->GetDI8KeyState(DIK_DOWN)){
        vecDir.z -= 1.0f;
        g_vPos.z -= 0.1f * timeElapsed;
    }

    FLOAT    fTmp = vecDir.x * vecDir.x;
    fTmp += vecDir.z * vecDir.z;
    if (fTmp > 0){
        D3DXMATRIX    matRot;
        FLOAT    fDot;
        D3DXVECTOR3    vCross;
        D3DXVec3Normalize(&vecDir,&vecDir);
        D3DXVec3Cross(&vCross,&vecDir,&g_vDir);
        fDot = D3DXVec3Dot(&vecDir,&g_vDir);
        static const FLOAT    fError = sinf(D3DX_PI / 180.0f);
        if ((fabs(vCross.y) > fError)||(fDot < 0)){
            if (vCross.y > 0){
                fTmp = -0.1f * timeElapsed;
            }else{
                fTmp = 0.1f * timeElapsed;
            }
            D3DXMatrixRotationY(&matRot,fTmp);
            D3DXVECTOR4    vec4Tmp;
            D3DXVec3Transform(&vec4Tmp,&g_vDir,&matRot);
            g_vDir = (D3DXVECTOR3)vec4Tmp;
            D3DXVec3Normalize(&g_vDir,&g_vDir);
        }else{
            g_vDir = vecDir;
        }
    }
    D3DXVECTOR3    vecMin, vecMax, vecNormal;
    FLOAT    fAlt,fDist;
    g_pFloor->GetBoundingBox(&vecMin, &vecMax);    //    かならず当たり判定を行う。
    g_pFloor->ProbeTheGroundAltitude(&g_vPos,&vecMin,&vecMax,&vecNormal,&fAlt,&fDist);
    g_vPos.y = fAlt;

    D3DXMatrixLookAtLH(pView,&D3DXVECTOR3(g_vPos.x,g_vPos.y+2.0f,g_vPos.z-10.0f),            //    カメラの位置
                             &D3DXVECTOR3( g_vPos.x, g_vPos.y, g_vPos.z ),        //    カメラを向ける位置
                             &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );    //    カメラの上を示す
    if (g_pEnemy01){
        g_pEnemy01->Update(timeElapsed,&g_vPos);
    }
}    

//------------------------------------------------------------
// Name: RenderScene
// Desc: レンダリングを行う。
//        UpdateScene 関数 からコールバックされる。
//------------------------------------------------------------
void RenderScene(LPDIRECT3DDEVICE9 lpd3ddev)
{
    D3DXMATRIX    matWorld;
    D3DXMatrixIdentity(&matWorld);
    lpd3ddev->SetTransform( D3DTS_WORLD, &matWorld );
    lpd3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);
    lpd3ddev->SetRenderState(D3DRS_LIGHTING, TRUE );
    g_pFloor->Render(lpd3ddev);

    D3DXMatrixTranslation(&matWorld,g_vPos.x,g_vPos.y,g_vPos.z);
    D3DXMATRIX    matRotation;

    matRotation._11 = g_vDir.z;
    matRotation._12 = 0;
    matRotation._13 = -g_vDir.x;
    matRotation._14 = 0;
    matRotation._21 = 0;
    matRotation._22 = 1.0f;
    matRotation._23 = 0;
    matRotation._24 = 0;
    matRotation._31 = g_vDir.x;
    matRotation._32 = 0;
    matRotation._33 = g_vDir.z;
    matRotation._34 = 0;
    matRotation._41 = 0;
    matRotation._42 = 0;
    matRotation._43 = 0;
    matRotation._44 = 1.0f;

    matWorld = matRotation * matWorld;
    lpd3ddev->SetTransform( D3DTS_WORLD, &matWorld);
    g_pMesh->Render(lpd3ddev);
    if (g_pEnemy01){
        g_pEnemy01->Render(lpd3ddev);
    }
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow )
{
    WNDCLASSEX wcex;
    wcex.cbSize = sizeof(WNDCLASSEX); 
    wcex.style            = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = (WNDPROC)WndProc;
    wcex.cbClsExtra        = 0;
    wcex.cbWndExtra        = 0;
    wcex.hInstance        = hInstance;
    wcex.hIcon            = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
    wcex.hCursor        = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
    wcex.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName    = NULL;
    wcex.lpszClassName    = "Game";
    wcex.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);
    RegisterClassEx(&wcex);

    HWND hWnd;
    hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,wcex.lpszClassName,"Game",
                WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_MAXIMIZEBOX,
                CW_USEDEFAULT,0,640,480,NULL,NULL,hInstance,NULL);
    if( !hWnd )
        return FALSE;

    RECT    bounds,client;
    GetWindowRect(hWnd,&bounds);
    GetClientRect(hWnd,&client);
    MoveWindow(hWnd,bounds.left,bounds.top,
        640 * 2 - client.right,
        480 * 2 - client.bottom,
        false );

    ShowWindow( hWnd, nCmdShow );
    UpdateWindow( hWnd );

    //    D3DQuickLib の初期化
    g_pD3DEnv = new CD3DEnv();
    if (FAILED(g_pD3DEnv->InitD3D(hWnd))){
        DestroyWindow(hWnd);
    }else{
        g_pFloor = new CFloor(g_pD3DEnv,"ground\\ground.x");
        g_pMesh = new CEnhancedMesh(g_pD3DEnv,"ground\\dog.x");
        g_pMesh->SetNumSegs(0);    //    ポリゴン分割を行わない。
        InitEnemy01Graphics(g_pD3DEnv);
        g_pEnemy01 = new CEnemy01(0,0);
        g_pD3DEnv->ReloadGraphics();
    }

    MSG        msg;
    while(true){
        if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){
            if(msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }else{
            g_pD3DEnv->UpdateScene(FrameMove,RenderScene);
        }
    }
    //    D3DQuickLib の終了
    SAFE_DELETE(g_pEnemy01);
    DeleteEnemy01Graphics();
    SAFE_DELETE(g_pMesh);
    SAFE_DELETE(g_pFloor);
    SAFE_DELETE(g_pD3DEnv);
    
    return (int)msg.wParam;
}


LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    switch (message){
        case    WM_SIZE:
            //    ウインドウが最大化されたら、フルスクリーンモードにする。
            if (SIZE_MAXIMIZED == wParam){
                if (g_pD3DEnv->IsWindowed())
                    g_pD3DEnv->ToggleFullscreen();
            }
            break;
        case    WM_SETCURSOR:
            //    フルスクリーン時はカーソルを表示させない。
            if (g_pD3DEnv->IsWindowed())
                ::SetCursor(::LoadCursor(NULL,IDC_ARROW));
            else
                ::SetCursor(NULL);
            break;
        case    WM_SYSCHAR:
            //    Alt-Enter でフルスクリーンモードへ切り替える
            if (lParam&0x20000000){
                if (wParam == '\x0d'){
                    g_pD3DEnv->ToggleFullscreen();
                    break;
                }
            }
            return DefWindowProc( hWnd, message, wParam, lParam );
        case    WM_SYSCOMMAND:
            if (!g_pD3DEnv->IsWindowed()){    //    フルスクリーン時
                if (wParam != SC_CLOSE)
                    break;

                //    Windowが閉じられる前にWindowモードに戻しておく。
                g_pD3DEnv->ToggleFullscreen();
                Sleep(100);
            }
            return DefWindowProc( hWnd, message, wParam, lParam );

        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return    0;
}