このプログラムは、Tuto12 のプログラムを改造して下さい。
/* /* * 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); m_fTime = 0; } 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; m_fTime += timeElapsed; if (m_fTime > 600.0f) return true; 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); }
/* * class CEnemy * 敵キャラの実装クラス * * First edition: February.19.2004 * */ #ifndef __ENEMY01_H__ #define __ENEMY01_H__ class CEnemy01 { public: CEnemy01 *m_pPrev; CEnemy01 *m_pNext; CEnemy01(float x, float y); virtual ~CEnemy01(); BOOL Update(float timeElapsed,D3DXVECTOR3 *pPos); void Render(LPDIRECT3DDEVICE9 lpd3ddev); private: D3DXMATRIX m_matWorld; // ワールド座標への変換行列 D3DXMATRIX m_matDir; // キャラクタの向きを表す行列 // ※上記2メンバ、高速化の為には、方向をベクトルで表した方が望ましい。 // (角度値で処理するのは、三角関数が必要なので、速度的に不利) FLOAT m_fAngle; // キャラクタの向き FLOAT m_fX; // キャラクタの座標 FLOAT m_fY; // キャラクタの座標 FLOAT m_fZ; // キャラクタの座標 FLOAT m_fTime; // 累積生存時間 }; // グラフィックスデータは敵キャラクタクラスとは別に初期化する。 extern void InitEnemy01Graphics(CD3DEnv *pEnv); extern void DeleteEnemy01Graphics(); #endif
#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; CEnemy01 *g_pRoot = 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 / 90.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 ) ); // カメラの上を示す CEnemy01 *p = g_pRoot->m_pNext; CEnemy01 *next; while(p != g_pRoot){ next = p->m_pNext; if (p->Update(timeElapsed, &g_vPos)){ p->m_pNext->m_pPrev = p->m_pPrev; p->m_pPrev->m_pNext = p->m_pNext; delete p; } p = next; } } //------------------------------------------------------------ // 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); CEnemy01 *p = g_pRoot->m_pNext; while(p != g_pRoot){ p->Render(lpd3ddev); p = p->m_pNext; } } 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_pRoot = new CEnemy01(0,0); g_pRoot->m_pPrev = g_pRoot->m_pNext = g_pRoot; // ダミーノード CEnemy01 *p; FLOAT fAngle; for (int i = 0; i < 10; ++i){ fAngle = (float)D3DX_PI * i * 0.2f; p = new CEnemy01(cosf(fAngle)*3.0f, sinf(fAngle) * 3.0f ); p->m_pPrev = g_pRoot->m_pPrev; p->m_pNext = g_pRoot; g_pRoot->m_pPrev->m_pNext = p; g_pRoot->m_pPrev = p; } 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 の終了 CEnemy01 *tmp, *next; if (g_pRoot != NULL){ tmp = g_pRoot->m_pNext; while(tmp != g_pRoot){ next = tmp->m_pNext; tmp->m_pPrev->m_pNext = tmp->m_pNext; tmp->m_pNext->m_pPrev = tmp->m_pPrev; delete tmp; tmp = next; } delete g_pRoot; g_pRoot = NULL; } 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; }