Chào mừng đến với Diễn đàn lập trình - Cộng đồng lập trình.
Trang 1 của 3 123 CuốiCuối
Kết quả 1 đến 10 của 28
  1. #1
    Ngày tham gia
    Sep 2015
    Đang ở
    Hà Nội
    Bài viết
    0

    Thực hành lập trình Game với VC++6.0 sử dụng DirectX : Tank2D

    NGÀY 1 :
    Mình không được học chuyên ngành, cũng không rành tiếng Anh, đến với lập trình chỉ là một sự ngẫu nhiên, một đam mê theo kiểu "tay trái". Không muốn múa rìu qua mắt thợ, Game này mình viết chỉ để học tập, để thử lòng kiên trì, xin các bạn góp ý thêm trong quá trình xây dựng dự án. Vì là một Newbee nên kiểu Code của mình có nhiều ngờ nghệch, chỉ xin các bạn đừng cười.
    Trước tiên, chương trình Tank2D tạo ra, thêm bớt, xóa rất nhiều các đối tượng, vì vậy trước tiên mình sẽ mã hóa một lớp có tên CPtrArray. Trong thư viện MFC của Microsoft cũng có những lớp tương tự nhưng mình không muốn sử dụng, vì nó sẽ làm chương trình thực thi của bạn tăng kích thước đáng kể. Hơn nữa, mình muốn viết một chương trình theo kiểu "copy là chạy", giảm thiểu tới mức dễ dàng nhất cho người sử dụng.

    Mã:
    #include <windows.h>class CPtrArray{private:    int     cCount;    PDWORD      pVoid;    BOOL        bClass;public:    CPtrArray(BOOL isClass) { cCount = 0; pVoid = NULL; bClass = isClass;}    ~CPtrArray()        { cCount = 0; if(pVoid) {RemoveAll(); free(pVoid); pVoid = NULL;}}    int GetSize()       { return cCount;}    PVOID GetAt(int iIndex)     {        if(iIndex >= cCount || cCount < 1)            return NULL;        return (PVOID)pVoid[iIndex];    }    BOOL Add(PVOID pThis)    {        PDWORD  pNew;        if(pNew = (PDWORD)malloc(sizeof(PVOID)*(cCount+1)))        {            for(int i=0;i<cCount;i++)                pNew[i] = (DWORD)pVoid[i];            free(pVoid);            pNew[i] = (DWORD)pThis;            pVoid = pNew;            cCount++;            return TRUE;        }        return FALSE;    }    BOOL RemoveAt(int iIndex)    {        if(iIndex < cCount)        {            PDWORD  pNew;            if(pNew = (PDWORD)malloc(sizeof(PVOID)*(cCount-1)))            {                int i;                for(i=0;i<iIndex;i++)                    pNew[i] = (DWORD)pVoid[i];                for(i=iIndex;i<cCount-1;i++)                    pNew[i] = (DWORD)pVoid[i+1];                if(bClass)  delete ((PVOID)pVoid[iIndex]);                else        free((PVOID)pVoid[iIndex]);                free(pVoid);                pVoid = pNew;                cCount--;                return TRUE;            }        }        return FALSE;    }    void RemoveAll()        {        PVOID   p;        for(int i=0;i<GetSize();i++)            if(p = GetAt(i))            {                if(bClass)  delete p;                else        free(p);            }        free(pVoid);        pVoid = NULL;        cCount = 0;    }};
    [IMG]images/smilies/thinking.gif[/IMG]

  2. #2
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 2: Tạo lớp CWaveSound
    Trong trò chơi Tank2D, chúng ta cần phải xử lý âm thanh trong các ngữ cảnh sau:
    - Thực hiện âm nhạc nền khi ở trong các màn hình hội thoại, khi trong chế độ [ PAUSE ], khi chờ nhận sự kiện nhập liệu từ người chơi. Âm nhạc nền có thể chơi từ điều khiển cửa sổ MCI của Windows, chương trình này sẻ chơi nhạc nền từ tập tin BackSound.mp3, BackSound.mid, BackSound.wav nếu nó tìm được theo thứ tự vừa rồi.
    - Các tiếng động tương tác : Tank khai hỏa, đạn nổ, hết 1 vòng, thua cuộc, v.v... Các tiếng động này có thể được phát cùng một thời điểm và có thể chồng lấp lên nhau nên không thể đơn giản dùng hàm PlaySound(), thay vào đó chúng ta sẽ sử dụng DirectSound cho mục đích này. Sau đây là mã hóa lớp CWaveSound ( Để tiện cho sau này, mình cũng đặt tất cả các chỉ thị tiền xử lý #include và #pragma ở đầu tập tin cpp. Lưu ý là dự án chỉ có duy nhất 1 tập tin mã nguồn Tank2D.cpp, 1 tập tin tài nguyên Tank2D.rc)

    Mã:
    #define INITGUID#include<windows.h>#include<windowsx.h>#include<commctrl.h>#include<shlobj.h>#include<ddraw.h>#include<dinput.h>#include<dsound.h>#include<time.h>#include<math.h>#include<vfw.h>#include"resource.h"#pragma comment(lib,"comctl32.lib")#pragma comment(lib,"ddraw.lib")#pragma comment(lib,"dinput.lib")#pragma comment(lib,"dsound.lib")#pragma comment(lib,"winmm.lib")#pragma comment(lib,"vfw32.lib")#pragma comment(lib,"msimg32.lib")// Các định nghĩa #define khác ở đây// Các kiểu liệt kê enum đặt ở đây// Các định nhgĩa kiểu cấu trúc typedef struct đặt ở đây// Đặt khai báo của lớp CPtrArray của ngày 1 ở đâyclass CWaveSound{private:    BOOL Open(char *pszFileName,HMMIO *phmmioIn,WAVEFORMATEX **ppwfxInfo,MMCKINFO *pckInRIFF)    {        HMMIO           hmmioIn;        MMCKINFO        ckIn;           // chunk info. for general use.        PCMWAVEFORMAT   pcmWaveFormat;  // Temp PCM structure to load in.               WORD            cbExtraAlloc;   // Extra bytes for waveformatex         BOOL            bTrue;         *ppwfxInfo = NULL;        hmmioIn = NULL;        if((hmmioIn = mmioOpen(pszFileName, NULL, MMIO_ALLOCBUF | MMIO_READ)) == NULL)            goto ERROR_READING_WAVE;        if((int)mmioDescend(hmmioIn, pckInRIFF, NULL, 0) != 0)            goto ERROR_READING_WAVE;        if((pckInRIFF->ckid != FOURCC_RIFF) || (pckInRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')))            goto ERROR_READING_WAVE;        ckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');        if((int)mmioDescend(hmmioIn, &ckIn, pckInRIFF, MMIO_FINDCHUNK) != 0)            goto ERROR_READING_WAVE;        if(ckIn.cksize < (long) sizeof(PCMWAVEFORMAT))            goto ERROR_READING_WAVE;        if(mmioRead(hmmioIn, (HPSTR) &pcmWaveFormat, (long) sizeof(pcmWaveFormat)) != (long) sizeof(pcmWaveFormat))            goto ERROR_READING_WAVE;        if(pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM)            cbExtraAlloc = 0;           else if(mmioRead(hmmioIn, (LPSTR) &cbExtraAlloc,(long) sizeof(cbExtraAlloc)) != (long) sizeof(cbExtraAlloc))            goto ERROR_READING_WAVE;        if((*ppwfxInfo = (WAVEFORMATEX *)GlobalAlloc(GMEM_FIXED, sizeof(WAVEFORMATEX)+cbExtraAlloc)) == NULL)            goto ERROR_READING_WAVE;        memcpy(*ppwfxInfo, &pcmWaveFormat, sizeof(pcmWaveFormat));        (*ppwfxInfo)->cbSize = cbExtraAlloc;        if(cbExtraAlloc != 0)            if(mmioRead(hmmioIn, (LPSTR) (((BYTE*)&((*ppwfxInfo)->cbSize))+sizeof(cbExtraAlloc)),(long) (cbExtraAlloc)) != (long) (cbExtraAlloc))                goto ERROR_READING_WAVE;        if(mmioAscend(hmmioIn, &ckIn, 0) != 0)            goto ERROR_READING_WAVE;        goto TEMPCLEANUP;    ERROR_READING_WAVE:        bTrue = FALSE;        if(*ppwfxInfo != NULL)        {            GlobalFree(*ppwfxInfo);            *ppwfxInfo = NULL;        }                       if(hmmioIn != NULL)        {            mmioClose(hmmioIn, 0);            hmmioIn = NULL;        }    TEMPCLEANUP:        *phmmioIn = hmmioIn;        return bTrue;    }    BOOL Data(HMMIO *phmmioIn,MMCKINFO *pckIn,MMCKINFO *pckInRIFF)    {        mmioSeek(*phmmioIn, pckInRIFF->dwDataOffset + sizeof(FOURCC), SEEK_SET);        pckIn->ckid = mmioFOURCC('d', 'a', 't', 'a');        if(mmioDescend(*phmmioIn, pckIn, pckInRIFF, MMIO_FINDCHUNK)==0)            return TRUE;        return FALSE;    }    BOOL Read(HMMIO hmmioIn,UINT cbRead,BYTE *pbDest,MMCKINFO *pckIn,UINT *cbActualRead)    {        MMIOINFO    mmioinfoIn;         // current status of <hmmioIn>        UINT        cT,cbDataIn;         if(mmioGetInfo(hmmioIn,&mmioinfoIn,0) != 0)            goto ERROR_CANNOT_READ;        cbDataIn = cbRead;        if(cbDataIn > pckIn->cksize)             cbDataIn = pckIn->cksize;               pckIn->cksize -= cbDataIn;        for(cT=0;cT<cbDataIn;cT++)        {            if(mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)            {                if(mmioAdvance(hmmioIn, &mmioinfoIn, MMIO_READ) != 0)                    goto ERROR_CANNOT_READ;                if(mmioinfoIn.pchNext == mmioinfoIn.pchEndRead)                    goto ERROR_CANNOT_READ;            }            *((BYTE*)pbDest+cT) = *((BYTE*)mmioinfoIn.pchNext++);        }        if(mmioSetInfo(hmmioIn, &mmioinfoIn, 0) != 0)            goto ERROR_CANNOT_READ;        *cbActualRead = cbDataIn;        return TRUE;    ERROR_CANNOT_READ:        *cbActualRead = 0;        return FALSE;    }    BOOL Load(LPSTR szFile,UINT *cbSize,WAVEFORMATEX **ppwfxInfo,BYTE **ppbData)    {        HMMIO       hmmioIn;                MMCKINFO    ckInRiff;        MMCKINFO    ckIn;        UINT        cbActualRead;        BOOL        bTrue=TRUE;         *ppbData = NULL;        *ppwfxInfo = NULL;        *cbSize = 0;        if(!this->Open(szFile,&hmmioIn,ppwfxInfo,&ckInRiff))            goto ERROR_LOADING;        if(!this->Data(&hmmioIn,&ckIn,&ckInRiff))            goto ERROR_LOADING;        if((*ppbData = (BYTE *)GlobalAlloc(GMEM_FIXED,ckIn.cksize)) == NULL)            goto ERROR_LOADING;        if(!this->Read(hmmioIn,ckIn.cksize,*ppbData,&ckIn,&cbActualRead))            goto ERROR_LOADING;        *cbSize = cbActualRead;        goto DONE_LOADING;    ERROR_LOADING:        bTrue = FALSE;        if(*ppbData != NULL)        {            GlobalFree(*ppbData);            *ppbData = NULL;        }        if(*ppwfxInfo != NULL)        {            GlobalFree(*ppwfxInfo);            *ppwfxInfo = NULL;        }           DONE_LOADING:        if(hmmioIn != NULL)        {            mmioClose(hmmioIn, 0);            hmmioIn = NULL;        }        return bTrue;    }public:    BOOL                bLoop;    LPDIRECTSOUNDBUFFER lpdsb;     CWaveSound()    { lpdsb = NULL; bLoop = FALSE;}    ~CWaveSound()   { if(lpdsb) { lpdsb->Stop(); lpdsb->Release();}}    BOOL Play(LPSTR szFile,LPDIRECTSOUND lpds,BOOL loop)    {        DSBUFFERDESC        dsbd;        WAVEFORMATEX *      pwfx=NULL;        PBYTE               pdata=NULL;        UINT                uSize;         if(!this->Load(szFile,&uSize,&pwfx,&pdata))            return FALSE;        memset(&dsbd, 0, sizeof(DSBUFFERDESC));        dsbd.dwSize         = sizeof(DSBUFFERDESC);        dsbd.dwFlags        = DSBCAPS_STATIC|DSBCAPS_CTRLDEFAULT|DSBCAPS_GETCURRENTPOSITION2;        dsbd.dwBufferBytes  = uSize;        dsbd.lpwfxFormat    = pwfx;        if(SUCCEEDED(lpds->CreateSoundBuffer(&dsbd,&this->lpdsb,NULL)))        {            BYTE    *pbData = NULL;            BYTE    *pbData2 = NULL;            DWORD   dwLength;            DWORD   dwLength2;             this->lpdsb->Lock(0,uSize,(void**)&pbData,&dwLength,(void**)&pbData2,&dwLength2,0L);            memcpy(pbData,pdata,uSize);            this->lpdsb->Unlock(pbData,uSize,NULL,0);            this->lpdsb->SetVolume(0);            this->lpdsb->SetPan(0);            this->bLoop = loop;            this->lpdsb->Play(0,0,loop?DSBPLAY_LOOPING:0);            GlobalFree(pwfx);            GlobalFree(pdata);            return TRUE;        }        GlobalFree(pwfx);        GlobalFree(pdata);        return FALSE;    }};

  3. #3
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 3: Các tác vụ với những giá trị của Cells trong ma trận của Game.
    Trong Tank2D vùng chơi là tập hợp của 1 ma trận 2 chiều 13x13 LargeCells, mỗi LargeCell có kích thước 32Pixels x 32Pixels. (Pixel - Đơn vị điểm ảnh trên màn hình)
    Mỗi LargeCell lại là tập hợp của ma trận 2x2 MediumCells, mỗi MediumCell có kích thước 16Pixels x 16Pixels.
    Mỗi MediumCell là tập hợp của ma trận 2x2 SmallCells, mỗi SmallCell có kích thước 8Pixels x 8Pixels.
    Chúng ta có tất cả 7 kiểu giá trị cho SmallCells là : Free,Brick,Steel,River,Cover,Glide,House gọi theo tiếng Việt là ô trống, gạch, thép, sông, ngụy trang, ô trượt và bộ chỉ huy.
    Thêm vào phần định nghĩa của tập tin Tank2D.cpp các định nghĩa và liệt kê sau

    Mã:
    #define     SMALL       8#define     MEDIUM      (SMALL*2)#define     LARGE       (SMALL*4)enum    {TC_FREE=0,TC_BRICK,TC_STEEL,TC_RIVER,TC_COVER,TC_GLIDE,TC_HOUSE};
    Các hàm sau đây sẽ tạo các giá trị ngẫu nhiên cho một ma trận SmallCells 52x52 - Tức là toàn bộ khung chơi

    Mã:
    void Cells_CreateValueDefault(int pCells[52][52]){    int             x,y;     for(x=0;x<4;x++)    for(y=0;y<4;y++)        pCells[x][y] = TC_FREE;         // Vi tri xuat phat ben trai cua EnemyTank    for(x=24;x<28;x++)    for(y=0;y<4;y++)        pCells[x][y] = TC_FREE;         // Vi tri xuat phat chinh giua cua EnemyTank    for(x=48;x<52;x++)    for(y=0;y<4;y++)        pCells[x][y] = TC_FREE;         // Vi tri xuat phat ben phai cua EnemyTank    for(x=16;x<36;x++)    for(y=44;y<52;y++)        pCells[x][y] = TC_FREE;         // Tao vung trong xung quanh bo chi huy    for(x=22;x<30;x++)    for(y=46;y<52;y++)        pCells[x][y] = TC_BRICK;        // Tao vung gach xung quanh bo chi huy    for(x=24;x<28;x++)    for(y=48;y<52;y++)        pCells[x][y] = TC_HOUSE;        // Vi tri cua bo chi huy}void Cells_CreateValueRandom(int pCells[52][52]){    int             x,y,m,n,iValue;     for(x=0;x<52;x+=4)    for(y=0;y<52;y+=4)    {        if(rand()%6==0)         iValue = TC_BRICK;        else if(rand()%6==0)    iValue = TC_STEEL;        else if(rand()%10==0)   iValue = TC_RIVER;        else if(rand()%6==0)    iValue = TC_COVER;        else if(rand()%6==0)    iValue = TC_SLIDE;        else                    iValue = TC_FREE;        for(m=x;m<x+4;m++)        for(n=y;n<y+4;n++)            pCells[m][n] = iValue;    }    Cells_CreateValueDefault(pCells);   // Gan cac gia tri mac dinh cho mot so Cells}
    - - - Nội dung đã được cập nhật ngày 20-02-2014 lúc 02:17 PM - - -</font>

    NGÀY 4: Dữ liệu của mỗi vòng chơi.
    Chúng ta cần thêm vài khai báo vào phần đầu của tập tin Tank2D.cpp

    Mã:
    #include<shlwapi.h>#pragma comment(lib,"shlwapi.lib")typedef struct{    int     iCells[52][52];         // Ma tran gia tri    int     iStyleTank[30];         // Kieu Tank cua 30 EnemyTank    int     iStyleCard[5];          // Kieu Card cua 5 BonusCard}DATALEVEL,*PDATALEVEL;
    Trong Tank2D, chúng ta qui định 1 vòng chơi sẽ có 30 Tank địch xuất hiện, Tank địch có rất nhiều kiểu, trường iStyleTank lưu định danh kiểu của từng Tank.
    Chúng ta cũng qui định các Tank thứ 6,12,18,24,30 (index là 5,11,17,23,29) khi bị Tank ta bắn trúng sẽ xuất hiện các thẻ tăng cường, trường iStyleCard lưu định danh kiểu từng Card.

    Để thống nhất nguồn lưu và nạp dữ liệu trong trò chơi, chúng ta sẽ điều hướng tất cả các tác vụ tập tin vào thư mục Tank2D, thư mục này nằm trong thư mục chứa tập tin thực thi
    Tank2D.exe khi đã được biên dịch. Ở đây, để tránh gây khó hiểu cho các bạn mới làm quen với VC++6.0, mình sơ lược lại cách tạo các tập tin trong dự án này.
    1.Khởi động Microsoft Visual C++6.0, trình đơn File->New.
    2.Trong hộp thoại New chọn Tab Projects.
    3.Chọn Win32 Application, nhập tên của dự án là Tank2D, điều hướng nơi cấp phát thư mục, check Create new workspace, check Win32 trong Platforms. Nhấn OK. Nhấn Finish, Nhấn OK.
    4.File->New->Files. Chọn C++ Source File, điền tên tập tin là Tank2D, nhấn OK. Chúng ta đã tạo ra tập tin Tank2D.cpp trong dự án.
    5.File->New->Files. Chọn Resource Script, điền tên tập tin là Tank2D, nhấn OK. Chúng ta đã tạo ra tập tin Tank2D.rc trong dự án.

    Dữ liệu của mỗi vòng chơi sẽ được truy xuất theo các tập tin " Tank2D/Level%i.lev ", %i được thay thế bằng Level + 1 ( Vì các tập tin sẽ bắt đầu từ 1 còn biến lưu lại bắt đầu từ 0).
    Sau đây là cài đặt của 2 hàm lưu và nạp dữ liệu vòng chơi.


    Mã:
    BOOL Level_LoadData(PDATALEVEL pDataLevel,int iLevel){    CHAR        szFolder[MAX_PATH],szFile[MAX_PATH];    HANDLE      hFile;    DATALEVEL   Data;    DWORD       dw;    BOOL        bFlag;     GetModuleFileName(NULL,szFolder,MAX_PATH);    PathRemoveFileSpec(szFolder);    lstrcat(szFolder,"\\Tank2D");    if(!PathIsDirectory(szFolder))        return FALSE;    wsprintf(szFile,"%s\\Level%i.lev",szFolder,iLevel+1);    hFile = CreateFile(szFile,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);    if(hFile == INVALID_HANDLE_VALUE)        return FALSE;    if(GetFileSize(hFile,NULL) != sizeof(DATALEVEL))    {        CloseHandle(hFile);        return FALSE;    }    bFlag = ReadFile(hFile,&Data,sizeof(DATALEVEL),&dw,NULL);    CloseHandle(hFile);    if(!bFlag || dw != sizeof(DATALEVEL))        return FALSE;    // Sau nay se them phan kiem tra su hop le cua cac truong trong cau truc Data o day    CopyMemory(pDataLevel,&Data,sizeof(DATALEVEL));    return TRUE;}BOOL Level_SaveData(PDATALEVEL pDataLevel,int iLevel){    CHAR        szFolder[MAX_PATH],szFile[MAX_PATH];    HANDLE      hFile;    DWORD       dw;    BOOL        bFlag;     GetModuleFileName(NULL,szFolder,MAX_PATH);    PathRemoveFileSpec(szFolder);    lstrcat(szFolder,"\\Tank2D");    if(!PathIsDirectory(szFolder))        return FALSE;    wsprintf(szFile,"%s\\Level%i.lev",szFolder,iLevel+1);    hFile = CreateFile(szFile,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);    if(hFile == INVALID_HANDLE_VALUE)        return FALSE;    bFlag = WriteFile(hFile,pDataLevel,sizeof(DATALEVEL),&dw,NULL);    CloseHandle(hFile);    if(!bFlag || dw != sizeof(DATALEVEL))    {        DeleteFile(szFile);        return FALSE;    }    return TRUE;}
    - - - Nội dung đã được cập nhật ngày 20-02-2014 lúc 02:50 PM - - -

    Đính chính: Trong hàm Cells_CreateValueRandom() của ngày 3, bạn sửa dòng lệnh

    Mã:
            else if(rand()%6==0)    iValue = TC_SLIDE;
    thành dòng

    Mã:
            else if(rand()%6==0)    iValue = TC_GLIDE;
    Các bạn thông cảm cho lỗi đánh máy nhé ![IMG]images/smilies/clap_grin.gif[/IMG]

    - - - Nội dung đã được cập nhật ngày 21-02-2014 lúc 02:04 PM - - -

    <font color="#0000FF">NGÀY 5:
    Dữ liệu được thiết lập trước của game Tank2D
    Chúng ta thêm vào một kiểu cấu trúc trong phần khai báo của Tank2D.cpp

    Mã:
    typedef struct{    COLORREF    crScreen;               // Mau chu dao cua man hinh    COLORREF    crBack;                 // Mau nen khung choi    COLORREF    crFrame;                // Mau khung choi    COLORREF    crTitle;                // Mau Title    COLORREF    crText;                 // Mau text     BOOL        sndBackSound;           // Cho phep choi am thanh nen ?    BOOL        sndWinSound;            // Cho phep choi am thanh thang vong choi ?    BOOL        sndLostSound;           // Cho phep choi am thanh thua vong choi ?    BOOL        sndBonusSound;          // Cho phep choi am thanh thuong diem hoac nhan the tang cuong ?    BOOL        sndMineSound;           // Cho phep choi am thanh min no ?    BOOL        sndEnemyShoot;          // Cho phep choi am thanh EnemyShoot ?    BOOL        sndEnemySmallBullet;    // Cho phep choi am thanh EnemySmallBullet ?    BOOL        sndEnemyLargeBullet;    // Cho phep choi am thanh EnemyLargeBullet ?    BOOL        sndOurShoot;            // Cho phep choi am thanh OurShoot ?    BOOL        sndOurSmallBullet;      // Cho phep choi am thanh OurBullet ?    BOOL        sndOurLargeBullet;      // Cho phep choi am thanh OurLargeBullet ?}DATASETUP,*PDATASETUP;
    Các trường trong cấu trúc này là dễ hiểu như chú thích của nó thôi.
    Tiếp sau đây là các hàm lưu và nạp dữ liệu cho cấu trúc này.( Chú ý đường dẫn của tập tin là " Tank2D/Setup.set ").

    Mã:
    BOOL Setup_LoadData(PDATASETUP pDataSetup){    CHAR        szText[MAX_PATH];    HANDLE      hFile;    DATASETUP   Data;    DWORD       dw;    BOOL        bFlag;     GetModuleFileName(NULL,szText,MAX_PATH);    PathRemoveFileSpec(szText);    lstrcat(szText,"\\Tank2D");    if(!PathIsDirectory(szText))        return FALSE;    lstrcat(szText,"\\Setup.set");    hFile = CreateFile(szText,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);    if(hFile == INVALID_HANDLE_VALUE)        return FALSE;    if(GetFileSize(hFile,NULL) != sizeof(DATASETUP))    {        CloseHandle(hFile);        return FALSE;    }    bFlag = ReadFile(hFile,&Data,sizeof(DATASETUP),&dw,NULL);    CloseHandle(hFile);    if(!bFlag || dw != sizeof(DATASETUP))        return FALSE;    CopyMemory(pDataSetup,&Data,sizeof(DATASETUP));    return TRUE;}BOOL Setup_SaveData(PDATASETUP pDataSetup){    CHAR        szText[MAX_PATH];    HANDLE      hFile;    DWORD       dw;    BOOL        bFlag;     GetModuleFileName(NULL,szText,MAX_PATH);    PathRemoveFileSpec(szText);    lstrcat(szText,"\\Tank2D");    if(!PathIsDirectory(szText))        return FALSE;    lstrcat(szText,"\\Setup.set");    hFile = CreateFile(szText,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);    if(hFile == INVALID_HANDLE_VALUE)        return FALSE;    bFlag = WriteFile(hFile,pDataSetup,sizeof(DATASETUP),&dw,NULL);    CloseHandle(hFile);    if(!bFlag || dw != sizeof(DATASETUP))    {        DeleteFile(szText);        return FALSE;    }    return TRUE;}
    [IMG]images/smilies/thinking.gif[/IMG]

  4. #4
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 8: Một số tham khảo về DirectDraw DirectX.

    Trong lập trình Windows thông thường, khi muốn vẽ ra cửa sổ, lập trình viên sẽ sử dụng các API GDI để đáp ứng cho thông điệp WM_PAINT như sau:
    case WM_PAINT:
    hdc = BeginPaint(hwnd,&ps); // ps đã được khai báo là biến cấu trúc PAINTSTRUCT
    // Mã lệnh thao tác vẽ ở đây
    EndPaint(hwnd,&ps);
    return 0;
    và trong các thông điệp khác như sau:
    hdc = GetDC(hwnd);
    // Mã lệnh vẽ ở đây
    ReleaseDC(hwnd,hdc);
    return 0;

    điều này là thỏa đáng cho các chương trình ở dạng tĩnh. Khi cần các tác vụ vẽ liên tục, Windows đôi khi không vẽ gì cho đến khi đến lệnh vẽ cuối nó sẽ tổng hợp và vẽ lại 1 lần, điều này gây ra cảm giác chương trình có lỗi. Để tránh điều này, các lập trình viên sẽ đưa các tác vụ vẽ này vào luồng việc riêng. Tuy nhiên, trong các chương trình Game tương tác và hành động, một số lượng rất lớn các hình ảnh cần phải vẽ sao cho đồng bộ với thời gian, đồng bộ với nhập liệu, điều này phải nhờ đến Directdraw của DirectX.

    Các hệ điều hành hiện nay của Windows thường được cài đặt DirectX với các phiên bản lớn hơn 10, DirectDraw đã được bao gồm trong DirectGraphic. Để trung thành với tiêu chí " copy là chạy", mình sử dụng Directdraw nguyên thủy. Bạn có thể làm khác nhưng nhớ rằng, nếu lập trình theo " hàng mới", bạn có thể phải nạp bộ " DirectX SDK", bạn phải thay đổi vài cái gì đó trong
    dẫn hướng Tools -> Options -> Directories. Và khi đó, bạn cũng phải để ý tới sự bực tức của người chơi khi Windows báo lỗi hoặc bắt họ phải cài đặt một cái gì đó ( thông thường là các tập tin thư viện liên kết động, từ xa xưa đã có thuật ngữ " địa ngục thư viện liên kết động " mà).

    DirectDraw sử dụng các bề mặt để đẩy hình ảnh lên màn hình, phần lớn chúng ta cần làm là vẽ tất cả những gì cần vẽ lên bề mặt đó.

    Sau đây là một hàm cần thiết cho các tác vụ DirectX sau này.


    Mã:
    DWORD DirectX_ColorMatch(LPDIRECTDRAWSURFACE pdds,COLORREF rgb){    COLORREF        rgbT;    HDC             hdc;    DWORD           dw = CLR_INVALID;    DDSURFACEDESC   ddsd;    HRESULT         hres;     if(rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)    {        rgbT = GetPixel(hdc,0,0);        SetPixel(hdc,0,0,rgb);        pdds->ReleaseDC(hdc);    }    ddsd.dwSize = sizeof(ddsd);    while((hres = pdds->Lock(NULL,&ddsd,0,NULL)) == DDERR_WASSTILLDRAWING);    if(hres == DD_OK)    {        dw  = *(DWORD *)ddsd.lpSurface;        if(ddsd.ddpfPixelFormat.dwRGBBitCount < 32)            dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount)-1;        pdds->Unlock(NULL);    }    if(rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)    {        SetPixel(hdc,0,0,rgbT);        pdds->ReleaseDC(hdc);    }    return dw;}
    Hàm này mình lấy từ MSDN.[IMG]images/smilies/dont_know.gif[/IMG][IMG]images/smilies/dont_know.gif[/IMG]

    - - - Nội dung đã được cập nhật ngày 26-02-2014 lúc 03:45 PM - - -</font>

    <font color="#0000FF">NGÀY 9:
    Các kiểm tra trước khi cho Game hoạt động.

    Chúng ta sẽ kiểm tra và thoát chương trình sớm trong các trường hợp sau:
    1. Đã có 1 thể hiện khác của Tank2D.exe.
    2. Chương trình được khởi động từ nơi chứa không phải là ổ đĩa cứng của máy và không phải từ USB.


    Mã:
    BOOL App_EnableRun(){    UINT    uType;     if(FindWindow("Tank2D","Tank2D"))       // Đã có 1 thể hiện khác, cho Game thoát ra sớm.        return FALSE;    uType = GetDriveType(NULL);    if(uType!=DRIVE_FIXED)    {        if(uType == DRIVE_REMOVABLE)            MessageBox(NULL,"Chuong trinh co the bi cac trinh Anti_virus khong cho hoat dong.
    Neu gap truong hop nay, ban hay copy Tank2D.exe vao may, khoi dong lai !","Tank2D",MB_OK|MB_ICONINFORMATION);        else if(uType == DRIVE_CDROM)        {            MessageBox(NULL,"Chuong trinh khong the chay tu CDROM, copy Tank2D.exe vao trong may, khoi dong lai !","Tank2D",MB_OK|MB_ICONEXCLAMATION);            return FALSE;        }        else        {            MessageBox(NULL,"Ban phai copy Tank2D.exe vao trong may roi khoi dong lai !","Tank2D",MB_OK|MB_ICONEXCLAMATION);            return FALSE;        }    }    return TRUE;}
    Muốn tìm 1 thể hiện khác có hiện hữu hay không thì có rất nhiều cách, cách sử dụng ở đây là đơn giản nhất. Logic ở đây là nếu có một cửa sổ mức đỉnh có tên lớp và tên cửa sổ trùng với tên lớp khi chúng ta đăng ký lớp cho cửa sổ trong trương trình thì điều đó có nghĩa là " nó đang chạy".

    Vì chúng ta sẽ kiểm tra và có thể tạo các tập tin trong thư mục <Tank2D> ở cùng thư mục chương trình. Vì vậy chúng ta phải ngăn ngừa việc ghi vào các nơi cấp phát không thể ghi được
    như ổ CD hay các ổ đĩa không xác định kiểu. Đối với USB, chúng ta có thể cho phép ghi nhưng một số chương trình Anti_Virus thì không. Truy xuất ghi ra các nơi lưu trữ ngoại vi sẽ bị xem
    là " mã độc" và sẽ bị "Tiên trảm hậu tấu","Giết lầm hơn bỏ sót" ( đương nhiên không phải tất cả là thế). Vì vậy chúng ta chỉ cảnh báo nhưng vẫn cho chương trình tiếp tục.

    [IMG]images/smilies/17.gif[/IMG]

  5. #5
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 10: Tiếp tục các kiểm tra trước.

    Ở đây, bạn hãy bổ sung thêm 2 tài nguyên bitmap như sau:

    1. id : IDB_BULLN, tên tập tin liên quan : BullN.bmp, 1 khung 8x8 Pixels, vẽ viên đạn tròn màu xám (Bullet Normal).
    2. id : IDB_BULLF, tên tập tin liên quan : BullF.bmp, 1 khung 8x8 Pixels, vẽ viên đạn tròn màu lửa (Bullet Fire).


    Mã:
    BOOL App_CheckBMPFile(HINSTANCE hInstance,PCHAR pszFolder,PCHAR pszName,UINT id){    CHAR        szText[MAX_PATH];    HDC         hdc;    HBITMAP     hBitmap;    BOOL        bFlag;     wsprintf(szText,"%s\\%s",pszFolder,pszName);    if(!PathFileExists(szText))    {        hBitmap = LoadBitmap(hInstance,MAKEINTRESOURCE(id));        hdc = GetDC(NULL);        bFlag = Bitmap_CreateFile(szText,Bitmap_CreateInfoStruct(hBitmap),hBitmap,hdc);        ReleaseDC(NULL,hdc);        DeleteObject(hBitmap);        if(!bFlag)              // Khong co tap tin, cung khong tao ra duoc        {            wsprintf(szText,"Loi: Khong the tao ra tap tin < %s >, chuong trinh cham dut !",pszName);            MessageBox(NULL,szText,"Tank2D",MB_OK|MB_ICONEXCLAMATION);            return FALSE;        }    }    return TRUE;}BOOL App_UpdateFiles(HINSTANCE hInstance){    CHAR        szFolder[MAX_PATH];    int         i;    PCHAR       pszName[27] =     {        "Brick.bmp","Steel.bmp","Cover.bmp","River.bmp","Glide.bmp","House.bmp","Card1.bmp","Card2.bmp","Card3.bmp","Card4.bmp",        "Card5.bmp","Card6.bmp","Card7.bmp","Card8.bmp","Card9.bmp","Card10.bmp","Card11.bmp","Fire.bmp","Smoke.bmp","Tank1.bmp",        "Tank2.bmp","Tank3.bmp","Tank4.bmp","TankL.bmp","TankR.bmp","BullN.bmp","BullF.bmp"    };    UINT        id[27] =    {        IDB_BRICK,IDB_STEEL,IDB_COVER,IDB_RIVER,IDB_GLIDE,IDB_HOUSE,IDB_CARD1,IDB_CARD2,IDB_CARD3,IDB_CARD4,        IDB_CARD5,IDB_CARD6,IDB_CARD7,IDB_CARD8,IDB_CARD9,IDB_CARD10,IDB_CARD11,IDB_FIRE,IDB_SMOKE,IDB_TANK1,        IDB_TANK2,IDB_TANK3,IDB_TANK4,IDB_TANKL,IDB_TANKR,IDB_BULLN,IDB_BULLF    };     GetModuleFileName(NULL,szFolder,MAX_PATH);    PathRemoveFileSpec(szFolder);    lstrcat(szFolder,"\\Tank2D");    if(!PathIsDirectory(szFolder))                  // Thu muc la khong co san        CreateDirectory(szFolder,NULL);    if(!PathIsDirectory(szFolder))    {        MessageBox(NULL,"Khong the tao ra thu muc < Tank2D > tai thu muc chuong trinh","Tank2D",MB_OK|MB_ICONEXCLAMATION);        return FALSE;    }    else                                            // Da co thu muc    {        for(i=0;i<27;i++)            if(!App_CheckBMPFile(hInstance,szFolder,pszName[i],id[i]))                return FALSE;    }    return TRUE;}
    Như bạn thấy trong mã. Tank2D.exe sẽ chấm dứt sớm khi bất kỳ 1 tập tin Bitmap nào không có sẵn trong thư mục và cũng không thể tạo ra được.

    Tạm nghỉ ở đây thôi, có bạn nào thấy cách mình trình bày là rối rắm không, hãy góp ý để mình sửa lại mã cho tốt hơn. Thân mến!

    [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG]

  6. #6
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 11: Tạo các tài nguyên tùy biến, tạo tập tin dữ liệu vòng từ tài nguyên trong chương trình.

    Trước tiên các bạn hãy sửa kiểu các trường của cấu trúc DATALEVEL như sau:

    Mã:
    typedef struct{    BYTE        iCells[52][52];         // Ma tran gia tri    BYTE        iStyleTank[30];         // Kieu Tank cua 30 EnemyTank    BYTE        iStyleCard[5];          // Kieu Card cua 5 BonusCard}DATALEVEL,*PDATALEVEL;
    Mục đích của việc này là làm giảm thiểu kích thước của tập tin dữ liệu vòng ( *.lev)

    Các bạn cũng sửa 2 hàm liên quan là;

    void Cells_CreateValueDefault(int pCells[52][52]) thành void Cells_CreateValueDefault(BYTE pCells[52][52])
    void Cells_CreateValueRandom(int pCells[52][52]) thành void Cells_CreateValueRandom(BYTE pCells[52][52])
    Từ IDE của VC++6.0, các bạn tạo 35 Custom Resources có kiểu "LEVEL"như sau:
    Chắc chắn cữa sổ "Workspace" hiển thị.
    Chọn Tab "ResourceView".
    Lặp : Nhấn chuột phải vào "Tank2D resources", chọn "Import ...", điều hướng tới thư mục chứa 35 tập tin ".lev", chọn "level1.lev", nhấn <Import>
    Trong hộp thoại "Custom Resource Type", gõ tên kiểu tài nguyên là "LEVEL", nhấn OK. IDE sẽ tạo ra kiểu tài nguyên "LEVEL" và id của tài nguyên này là IDR_LEVEL1.
    Lặp lại từ "Lặp" để tạo ra tất cả các id cho tới IDR_LEVEL35. ( Chú thích 1).

    Trước khi cho Game hiển thị trên màn hình, chúng ta sẽ kiểm tra và tạo ra ( nếu cần) 35 tập tin từ "Level1.lev" cho tới "Level35.lev" trong thư mục <Tank2D>


    Mã:
    void App_CheckLevelFile(){    CHAR        szFolder[MAX_PATH];    CHAR        szFile[MAX_PATH];    HANDLE      hFile;    DWORD       dw,dwSize;    HRSRC       hFind,hLoad;    PCHAR       pLock;    BOOL        bFlag;    int         i;     GetModuleFileName(NULL,szFolder,MAX_PATH);    PathRemoveFileSpec(szFolder);    lstrcat(szFolder,"\\Tank2D");    for(i=0;i<35;i++)    {        wsprintf(szFile,"%s\\Level%i.lev",szFolder,i+1);        if(!PathFileExists(szFile))        {            if(INVALID_HANDLE_VALUE!=(hFile=CreateFile(szFile,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL)))            {                bFlag = TRUE;                if(hFind = FindResource(GetModuleHandle(NULL),MAKEINTRESOURCE(IDR_LEVEL1+i),"LEVEL"))                if(hLoad = (HRSRC)LoadResource(GetModuleHandle(NULL),hFind))                if(pLock = (PCHAR)LockResource(hLoad))                {                    dwSize = SizeofResource(GetModuleHandle(NULL),hFind);                    if(!WriteFile(hFile,pLock,dwSize,&dw,NULL) || dwSize != dw)                        bFlag = FALSE;                }                CloseHandle(hFile);                if(!bFlag)                    DeleteFile(szFile);            }        }    }}
    Chú thích 1
    35 tập tin Level ( Level1.lev -> Level35.lev) do mình tự tạo ra dựa theo nguyên bản của "Battle City". Mỗi tập tin khoảng 3Kb.
    Bạn nào biết cách thì xin chỉ cho mình cách gắn kèm 35 tập tin này vào bài viết để cùng tham khảo. Cám ơn !

    [IMG]images/smilies/17.gif[/IMG][IMG]images/smilies/17.gif[/IMG][IMG]images/smilies/17.gif[/IMG]

  7. #7
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 12: Copy các tập tin âm thanh của Windows làm âm thanh của chương trình.

    Chúng ta sẽ tạo ra (nếu chưa có) 8 tập tin âm thanh cho Tank2D là :
    "BackSound.mid", Âm thanh chơi nền
    "Bonus.wav", Thưởng điểm
    "TankShoot.wav", Tank ta bắn
    "ETankShoot.wav", Tank địch bắn
    "BullN.wav", Đạn thường của ta nổ
    "BullF.wav", Đạn lửa của ta nổ
    "EBullN.wav", Đạn thường của địch nổ
    "EBullF.wav", Đạn lửa của địch nổ
    Các âm thanh trên sẽ được Copy từ thư mục Media của Windows.
    Các âm thanh khác sẽ được người chơi thêm vào khi cần tới.


    Mã:
    void App_CheckSoundFile(){    CHAR        szDestFolder[MAX_PATH],szSourFolder[MAX_PATH];    CHAR        szDestFile[MAX_PATH],szSourFile[MAX_PATH];    int         i;    PCHAR       pszSour[] =     {        "onestop.mid",          // Back        "chimes.wav",           // Bonus        "start.wav",            // TankShoot        "start.wav",            // ETankShoot        "recycle.wav"           // Burn        "recycle.wav"           // Burn        "recycle.wav"           // Burn        "recycle.wav"           // Burn    };    PCHAR       pszDest[] =    {        "BackSound.mid",        "Bonus.wav"        "TankShoot.wav",        "ETankShoot.wav",        "BullN.wav",        "BullF.wav",        "EBullN.wav",        "EBullF.wav"    };     GetWindowsDirectory(szSourFolder,MAX_PATH);    lstrcat(szSourFolder,"\\Media");    GetModuleFileName(NULL,szDestFolder,MAX_PATH);    PathRemoveFileSpec(szDestFolder);    lstrcat(szDestFolder,"\\Tank2D");    for(i=0;i<8;i++)    {        wsprintf(szSourFile,"%s\\%s",szSourFolder,pszSour[i]);        wsprintf(szDestFile,"%s\\%s",szDestFolder,pszDest[i]);        if(!PathFileExists(szDestFile))        if(CopyFile(szSourFile,szDestFile,TRUE))            SetFileAttributes(szDestFile,FILE_ATTRIBUTE_NORMAL);    }}
    Ngoại trừ âm thanh nền sẽ chơi từ cửa sổ MCI của Windows, tất cả các âm thanh khác phải ở định dạng "WAVE" để sử dụng với DirectSound.

    [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG]

    - - - Nội dung đã được cập nhật ngày 05-03-2014 lúc 04:48 PM - - -

    Trước khi cho hiển thị cửa sổ, chúng ta cũng cần tạo bảng phím tắt để sử dụng trong Game.
    Từ IDE chọn trình đơn Insert -> Resource... -> Insert Resource -> Accelerator -> New. Thay đổi id của bảng phím tắt thành IDR_ACCEL.
    Chúng ta tạo các id của từng phím tắt theo danh sách id, key và type như sau:
    1. id IDC_ESCAPE, key VK_ESCAPE, type VIRTKEY.
    2. id IDC_F1, key VK_F1, type VIRTKEY.
    3. id IDC_F2, key VK_F2, type VIRTKEY.
    4. id IDC_F3, key VK_F3, type VIRTKEY.
    5. id IDC_F4, key VK_F4, type VIRTKEY.
    6. id IDC_F5, key VK_F5, type VIRTKEY.
    7. id IDC_F6, key VK_F6, type VIRTKEY.
    8. id IDC_F7, key VK_F7, type VIRTKEY.
    9. id IDC_F8, key VK_F8, type VIRTKEY.
    10. id IDC_F9, key VK_F9, type VIRTKEY.
    11. id IDC_F10, key VK_F10, type VIRTKEY.
    12. id IDC_F11, key VK_F11, type VIRTKEY.
    13. id IDC_F12, key VK_F12, type VIRTKEY.
    14. id IDC_PAUSE, key VK_PAUSE, type VIRTKEY.
    15. id IDC_INSERT, key VK_INSERT, type VIRTKEY.
    16. id IDC_DELETE, key VK_DELETE, type VIRTKEY.
    17. id IDC_HOME, key VK_HOME, type VIRTKEY.
    18. id IDC_END, key VK_END, type VIRTKEY.
    19. id IDC_PAGEUP, key VK_PRIOR, type VIRTKEY.
    20. id IDC_PAGEDOWN, key VK_NEXT, type VIRTKEY.
    21. id IDC_ENTER, key VK_RETURN, type VIRTKEY.
    Tất cả là 21 phím đủ dùng cho hiện tại và phát triển sau này.

    Ngoài ra trong trò chơi, chúng ta sử sụng thêm một số phím và con chuột để người chơi thao tác khi chương trình ở trạng thái <PLAY>,
    các cài đặt này một số sẽ được lập trình với DirectInput, một số khác sẽ xử lý trong các thông điệp Windows.

    Phím <Space bar> chỉ định TankL (Tank của ta khởi tạo ở bên trái) khai hỏa.
    Các phím mũi tên sẽ thay đổi hướng và tịnh tiến TankL theo thời gian nhấn và giữ các phím này.
    Nút chuột trái chỉ định TankR khai hỏa.
    Nút chuột phải điều khiển TankR tịnh tiến theo hướng đã định sẵn.
    Khi di chuyển chuột, chương trình sẽ tính toán khoảng dời x và y để xác định hướng di chuyển của TankR.
    Ngoài ra chúng ta còn tạo Logic xử lý nút chuột giữa được nhấn tương đương như phím <Enter> được nhấn để tạo thuận lợi cho những người " sống chết với chuột".

    Thôi, ngày 12 đến đây thôi, mỏi tay rồi.

    [IMG]images/smilies/clap_grin.gif[/IMG] [IMG]images/smilies/clap_grin.gif[/IMG] [IMG]images/smilies/clap_grin.gif[/IMG]

  8. #8
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    Cám ơn chủ topic rất nhiều. Mình mới bắt đầu học C++ nên những bài viết thế này rất bổ ích. Mong chờ những bài viết tiếp theo.

  9. #9
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 15 : DirectSound và DirectInput

    Thêm các khai báo biến toàn cục

    Mã:
    LPDIRECTSOUND           g_lpDS;                     // DirectSoundLPDIRECTINPUT           g_lpDI;                     // DirectInputLPDIRECTINPUTDEVICE     g_lpKeyboard;               // Thiet bi KeyboardLPDIRECTINPUTDEVICE     g_lpMouse;                  // Thiet bi MouseHANDLE                  g_hEvent;                   // Event Mouse
    2 hàm sau khởi tạo và xóa DirectSound

    Mã:
    BOOL DirectX_InitDS(HWND hwnd){    if(DirectSoundCreate(NULL,&g_lpDS,NULL) != DS_OK)        return FALSE;    if(g_lpDS->SetCooperativeLevel(hwnd,DSSCL_NORMAL) != DS_OK)        return FALSE;    return TRUE;}void DirectX_DeleteDS(){    if(g_lpDS)    {        g_lpDS->Release();        g_lpDS = NULL;    }}
    2 hàm tiếp theo khởi tạo và xóa DirectInput

    Mã:
    BOOL DirectX_InitDI(HINSTANCE hInstance,HWND hwnd){    DIPROPDWORD dipdw = {sizeof(DIPROPDWORD),sizeof(DIPROPHEADER),0,DIPH_DEVICE,16};     if(DirectInputCreate(hInstance,0x0300,&g_lpDI,NULL) != DI_OK)                               return FALSE;    if(g_lpDI->CreateDevice(GUID_SysKeyboard,&g_lpKeyboard,NULL) != DI_OK)                      return FALSE;    if(g_lpDI->CreateDevice(GUID_SysMouse,&g_lpMouse,NULL) != DI_OK)                            return FALSE;    if(g_lpKeyboard->SetDataFormat(&c_dfDIKeyboard) != DI_OK)                                   return FALSE;    if(g_lpMouse->SetDataFormat(&c_dfDIMouse) != DI_OK)                                         return FALSE;    if(g_lpKeyboard->SetCooperativeLevel(hwnd,DISCL_NONEXCLUSIVE|DISCL_FOREGROUND) != DI_OK)    return FALSE;    if(g_lpMouse->SetCooperativeLevel(hwnd,DISCL_NONEXCLUSIVE|DISCL_FOREGROUND) != DI_OK)       return FALSE;    if(g_lpKeyboard->Acquire() != DI_OK)                                                        return FALSE;    if(!(g_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL)))                                        return FALSE;    if(g_lpMouse->SetEventNotification(g_hEvent) != DI_OK)                                      return FALSE;    if(g_lpMouse->SetProperty(DIPROP_BUFFERSIZE,&dipdw.diph) != DI_OK)                          return FALSE;    if(g_lpMouse->Acquire() != DI_OK)                                                           return FALSE;    return TRUE;}void DirectX_DeleteDI(){    if(g_lpDI)    {        if(g_lpKeyboard)    {g_lpKeyboard->Unacquire(); g_lpKeyboard->Release(); g_lpKeyboard = NULL;}        if(g_lpMouse)       {g_lpMouse->Unacquire();    g_lpMouse->Release();    g_lpMouse = NULL;}        if(g_hEvent)        {CloseHandle(g_hEvent);     g_hEvent = NULL;}        g_lpDI->Release();        g_lpDI = NULL;    }}
    Mở rông thêm đôi chút : Các bạn dễ nhận thấy rất nhiều máy Desktop có gắn thêm 1 bộ 2 thiết bị điều khiển cầm tay (Joystick), bộ thiết bị này chế tạo hình như để dành riêng cho
    chơi Game ( trên chợ vi tính Tôn Thất Tùng - Bùi Thị Xuân có giá dao động khoảng 100K ). Về mặt lập trình, Windows cũng hỗ trợ cho thiết bị này rất nhiệt tình và DirectInput cũng vậy,
    tuy nhiên hiện tại mình chưa nghiên cứu kỹ cho khía cạnh này. Mình sẽ lập trình cho Joystick trong các phiên bản nâng cấp của Tank2D.

    Cám ơn sự quan tâm của các bạn !

    [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG]

    - - - Nội dung đã được cập nhật ngày 09-03-2014 lúc 01:32 PM - - -</font>

    <font color="#0000FF">NGÀY 16 :
    Nhận dữ liệu và trạng thái của Chuột và Bàn phím

    Ở đây mình tạm phân chia ra 2 kiểu nhận dữ liệu là nhận Thụ động và Chủ động.
    Nhận Thụ động là nhận khi có một sự kiện nào đó được báo vào hàm xử lý thông điệp của chương trình ( WindowProc).
    Ví dụ : Người chơi nhấn phím <A>, sau nhiều thông điệp khác nhau sẽ có 1 thông điệp tới chương trình là WM_CHAR với wParam là mã Ascii của kí tự A, mã kí tự A được nhận thụ động.
    Nhận Chủ động là mã trong chương trình tự tìm kiếm dữ liệu nhập mà nó cần thiết.
    Ví dụ : Truy cập vào bộ đệm bàn phím của Windows, truy cập nơi lưu trữ trạng thái của chuột, dữ liệu lấy về là Chủ động.

    Trở lại với trò chơi, chúng ta nhận thấy một vài điều phải lưu ý như sau :

    Khi TankL khai hỏa với phím <Spacebar> được nhấn, từng viên đạn sẽ lần lượt được bắn ra nếu thuộc tính liên thanh còn hiệu lực. Nếu chúng ta nhận dữ liệu theo kiểu Thụ động
    thì chỉ bắn được duy nhất 1 viên đạn, người chơi phải nhả phím ra và nhấn lại 1 lần nữa thì viên đạn kế tiếp mới được bắn, đây không phải cách xử lý mà ta muốn. Vì vậy chúng ta nhận
    dữ liệu phím <Spacebar> theo cách Chủ động.
    Với các phím Arrow, khi nhấn TankL sẽ đổi hướng => Nhận Thụ động. Nhưng khi nhấn và giữ thì TankL sẽ tiến tới theo hướng có sẵn => Phải nhận Chủ động, như vậy là với các phím mũi tên chúng ta phải dùng cả 2 cách để nhận giữ liệu.

    Tương tự như phím <Spacebar>, Nút chuột trái sẽ nhận dữ liệu Chủ động.
    Hướng di chuyển của TankR là nhận Chủ động.
    Di chuyển tiến lên của TankR cũng là nhận Chủ động.

    Chúng ta có thể cố gắng lập trình để nhận dữ liệu nhập hoàn toàn trong các thông điệp, ví dụ : Chúng ta có thể biết một phím có đang nhấn hay không bằng cách đặt biến theo dõi,
    và trong khi xử lý WM_KEYDOWN, WM_KEYUP cho phù hợp nhưng mã sẽ rắc rối hơn và đôi khi là không kiểm soát được. DirectInput là hỗ trợ tuyệt vời cho nhận dữ liệu Chủ động từ
    Mouse và Keyboard.

    Thêm một kiểu liệt kê các hướng trong trò chơi là NULL, sang trái, lên trên, sang phải, xuống dưới

    Mã:
    enum {FM_NULL=0,FM_DOWN,FM_LEFT,FM_UP,FM_RIGHT};
    Thêm một định nghĩa kiểu cấu trúc để nhận dữ liệu Chủ động của Mouse

    Mã:
    typedef struct{    int     iRotateFrend;    BOOL    bLeftPress;    BOOL    bRightPress;}INPUTMOUSE,*PINPUTMOUSE;
    Sau đây là hàm nhận các thông tin từ chuột cho chương trình.

    Mã:
    BOOL DirectX_GetInputMouse(PINPUTMOUSE pInput){    HRESULT             hr;    DIDEVICEOBJECTDATA  od[10];    DWORD               dw,dwElements = 10;     // Nhan 10 phan tu    int                 dwX=0,dwY=0;    int                 dxABS,dyABS;    BOOL                bMoved = FALSE;     ZeroMemory(pInput,sizeof(INPUTMOUSE));    hr = g_lpMouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA),od,&dwElements,0);    if(hr != DI_OK)    {         if(hr == DIERR_INPUTLOST)            g_lpMouse->Acquire();        return FALSE;    }    for(dw=0;dw<dwElements;dw++)    {        switch(od[dw].dwOfs)         {        case DIMOFS_X:          dwX = od[dw].dwData; bMoved = TRUE; break;        case DIMOFS_Y:          dwY = od[dw].dwData; bMoved = TRUE; break;        case DIMOFS_BUTTON0:    pInput->bLeftPress = (od[dw].dwData & 0x80) ? TRUE:FALSE; break;        case DIMOFS_BUTTON1:    pInput->bRightPress = (od[dw].dwData & 0x80) ? TRUE:FALSE; break;        }    }    if(bMoved)    {        dxABS = abs(dwX);        dyABS = abs(dwY);        if(dxABS>2 || dyABS>2)        {            if(dxABS > dyABS)           // quay ngang                pInput->iRotateFrend = (dwX<0) ? FM_LEFT:FM_RIGHT;            else if(dxABS < dyABS)      // quay doc                pInput->iRotateFrend = (dwY<0) ? FM_UP:FM_DOWN;            else // dxABS == dyABS            {                if(dwX<0 && dwY<0)          pInput->iRotateFrend = (rand()%2) ? FM_LEFT:FM_UP;                else if(dwX<0 && dwY>0)     pInput->iRotateFrend = (rand()%2) ? FM_LEFT:FM_DOWN;                else if(dwX>0 && dwY<0)     pInput->iRotateFrend = (rand()%2) ? FM_UP:FM_RIGHT;                else                        pInput->iRotateFrend = (rand()%2) ? FM_DOWN:FM_RIGHT;            }        }    }    return TRUE;}
    Có lẽ mã cũng là dễ hiểu ngoại trừ đoạn cuối, mình giải thích thêm:
    Trước tiên ít nhất phải có độ dời x hoặc độ dời y phải lớn hơn 2 - điều này để tránh cho chương trình quá nhạy cảm với di chuyển chuột.
    Nếu độ dời x lớn hơn độ dời y, ta sẽ cho yêu cầu đổi hướng của TankR sang trái nếu vector là âm và ngược lại.
    Nếu độ dời x nhỏ hơn độ dời y, ta sẽ cho yêu cầu đổi hướng của TankR lên trên nếu vector là âm và ngược lại.
    Trong trường hợp 2 độ dời là bằng nhau ( gọi nôm na là trên đường phân giác của 2 trục di chuyển ) ta lấy hướng yêu cầu là ngẫu nhiên trên 2 hương này.

    Nên nhớ, Ở đây chúng ta lấy về 1 cấu trúc dữ liệu mà được yêu cầu cho TankR, còn có thể cho TankR thực hiện theo yêu cầu này hay không còn
    tùy thuộc vào các điều kiện khác ( Giả sử như đang kẹt đường thì Tank không thể tiến lên được ).

    Thôi, hôm nay chỉ tới đây thôi, thân ái !

    [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG]

  10. #10
    Ngày tham gia
    Sep 2015
    Bài viết
    0
    NGÀY 17 : Nhận dữ liệu từ bàn phím và chuột tiếp theo.

    Thêm một cấu trúc để nhận dữ liệu của Keyboard

    Mã:
    typedef struct{    BOOL        bSpace;         // Spacebar dang nhan hay tha ? TRUE:nhan  FALSE:tha    BOOL        bLeft;          // Left Arrow    BOOL        bUp;            // Up Arrow    BOOL        bRight;    BOOL        bDown;}INPUTKEYBOARD,*PINPUTKEYBOARD;
    Hàm nhận thông tin từ bàn phím cho chương trình

    Mã:
    BOOL DirectX_GetInputKeyboard(PINPUTKEYBOARD pInput){    HRESULT     hr;    CHAR        szBuf[256];     ZeroMemory(pInput,sizeof(INPUTKEYBOARD));    hr = g_lpKeyboard->GetDeviceState(sizeof(szBuf),&szBuf);    if(hr != DI_OK)    {         if(hr == DIERR_INPUTLOST)            g_lpKeyboard->Acquire();        return FALSE;    }    if(szBuf[DIK_SPACE] & 0x80)     pInput->bSpace  ^= 1;    if(szBuf[DIK_LEFT] & 0x80)      pInput->bLeft   ^= 1;    if(szBuf[DIK_UP] & 0x80)        pInput->bUp     ^= 1;    if(szBuf[DIK_RIGHT] & 0x80)     pInput->bRight  ^= 1;    if(szBuf[DIK_DOWN] & 0x80)      pInput->bDown   ^= 1;    return TRUE;}
    Cũng như dữ liệu Mouse, chúng ta nhận dữ liệu yêu cầu của TankL để tùy nghi thực hiện hay không.
    Khác chăng là ở đây ta nhận yêu cầu tịnh tiến TankL 1 cách tường minh theo 4 phím mũi tên.

    Ở cấu trúc INPUTKEYBOARD, lúc đầu mình định khai báo là một tập hợp cờ Bit đặt trong biến DWORD dwFlag và dùng các toán hạng Bitwise để tính toán
    nhưng cảm thấy sẽ khó hiểu với các bạn mới học C++ nên thôi. Các toán hạng ^= là để chuyển hệ bật/tắt các biến boolean.
    pInput->bSpace ^= 1; tương đương với pInput->bSpace = pInput->bSpace ? FALSE:TRUE;

    - - - Nội dung đã được cập nhật ngày 11-03-2014 lúc 12:12 PM - - -</font>

    NGÀY 18 : Vẽ các Cells


    Mã:
    BOOL DirectX_DrawBricks(BYTE pCells[52][52],int xFrame,int yFrame){    HRESULT     hr;    int         x,y;     for(x=0;x<52;x++)    for(y=0;y<52;y++)    if(pCells[x][y] == TC_BRICK)    {        hr = g_lpBack->BltFast(xFrame+x*SMALL,yFrame+y*SMALL,g_lpBrick,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)      // Khong phai OK va khong phai dang con ve        {            if(hr != DDERR_SURFACELOST)                     // Cung khong phai be mat da mat => các lỗi khác                return FALSE;            if(!DirectX_RestoreSurfaceAll())                // Phuc hoi sai                return FALSE;        }    }    return TRUE;}
    Tham số 2 và 3 là tọa độ LeftTop của Khung chơi.
    Hàm duyệt từng SmallCell, nếu Cell có định danh là Brick => Truyền khối Bít từ g_lpBrick tới g_lpBack.


    Mã:
    BOOL DirectX_DrawSteels(BYTE pCells[52][52],int xFrame,int yFrame){    HRESULT     hr;    int         x,y;     for(x=0;x<52;x+=2)    for(y=0;y<52;y+=2)    if(pCells[x][y] == TC_STEEL)    {        hr = g_lpBack->BltFast(xFrame+x*SMALL,yFrame+y*SMALL,g_lpSteel,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}
    Chú ý 2 vòng lặp for lồng nhau, bước tăng của x và y là 2.


    Mã:
    BOOL DirectX_DrawRivers(BYTE pCells[52][52],int xFrame,int yFrame){    HRESULT     hr;    int         x,y;     for(x=0;x<52;x+=4)    for(y=0;y<52;y+=4)    if(pCells[x][y] == TC_RIVER)    {        hr = g_lpBack->BltFast(xFrame+x*SMALL,yFrame+y*SMALL,g_lpRiver,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawCovers(BYTE pCells[52][52],int xFrame,int yFrame){    HRESULT     hr;    int         x,y;     for(x=0;x<52;x+=4)    for(y=0;y<52;y+=4)    if(pCells[x][y] == TC_COVER)    {        hr = g_lpBack->BltFast(xFrame+x*SMALL,yFrame+y*SMALL,g_lpCover,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawGlides(BYTE pCells[52][52],int xFrame,int yFrame){    HRESULT     hr;    int         x,y;     for(x=0;x<52;x+=4)    for(y=0;y<52;y+=4)    if(pCells[x][y] == TC_GLIDE)    {        hr = g_lpBack->BltFast(xFrame+x*SMALL,yFrame+y*SMALL,g_lpGlide,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}
    Chú ý bước tăng của x và y trong 3 hàm này là 4.


    Mã:
    BOOL DirectX_DrawHouse(BYTE pCells[52][52],int xFrame,int yFrame){    HRESULT     hr;     if(pCells[24][48] == TC_HOUSE)    {        hr = g_lpBack->BltFast(xFrame+24*SMALL,yFrame+48*SMALL,g_lpHouse,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}
    Với Bộ chỉ huy, chúng ta chỉ cần xét chỉ mục {24,48} nếu là TC_HOUSE ( Bộ chỉ huy chưa bị TankE bắn hạ) thì truyền khối Bít từ g_lpHouse tới g_lpBack.

    Bạn nhớ rằng tất cả các tác vụ vẽ của chúng ta là ở trên g_lpBack ( không phải ở trên bề mặt chủ Master) và
    khi truyền lên màn hình thì lại truyền từ g_lpMaster ( không phải bề mặt Back) thông qua phương thức Flip sẽ nói tới sau này.

    Ngày hôm nay xin dừng ở đây, cám ơn!

    [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG]

    - - - Nội dung đã được cập nhật ngày 11-03-2014 lúc 05:45 PM - - -

    NGÀY 19 : Vẽ các Tank

    Để vẽ các Tank, chúng ta cần có tọa độ, hướng di chuyển, khung hiện hành và một số thuộc tính khác.
    Thêm vào định nghĩa 2 cấu trúc mới cho TankE và Tank của người chơi.

    Mã:
    typedef struct{    BOOL        bCard;          // Tank nay bi ban co tao the ?    BOOL        bBullF;         // Tank nay ban dan lua ?    int         iFrame;         // Khung hien hanh trong bitmap    int         iFrend;         // Huong di chuyen hien hanh    int         iPower;         // Suc manh cua Tank tu 1->5    int         iRequest;       // Huong di chuyen duoc yeu cau    int         iShoot;         // Toc do di chuyen cua dan duoc ban tu Tank nay tu 5->10 Pixels trong 1 lan    int         iSmart;         // Muc do tinh quai cua Tank tu 0->2    int         iSpeed;         // Toc do di chuyen tu 1->3 Pixels trong 1 lan    int         iStyle;         // Kieu Tank tu 0->49    int         x,y;            // Toa do LeftTop cua TankE tinh tu goc tren trai trong khung choi}ENEMYTANK,*PENEMYTANK;typedef struct{    BOOL        bReady;         // Tank da san sang ?    BOOL        bRiver;         // Tank co the vuot song ?    BOOL        bSafety;        // Tank an toan trong che do duoc bao ve ?    BOOL        bBullF;         // Tank nay ban dan lua ?    BOOL        bRequestMove;   // Yeu cau tien toi ?    BOOL        bRequestShoot;  // Yeu cau ban ?    int         cBulletJoin;    // So dan noi tiep nhau duoc phep ban tu 1->3    int         cLife;          // So lan song cua Tank    int         iFrame;         // Khung hien hanh trong bitmap    int         iFrend;         // Huong di chuyen hien hanh    int         iPower;         // Suc manh cua Tank tu 1->5    int         iRequestFrend;  // Huong di chuyen duoc yeu cau    int         iShoot;         // Toc do di chuyen cua dan duoc ban tu Tank nay tu 5->10 Pixels trong 1 lan    int         iSpeed;         // Toc do di chuyen tu 1->3 Pixels trong 1 lan    PVOID       pBull[3];       // Con tro toi cac vien dan    int         x,y;            // Toa do LeftTop cua TankE tinh tu goc tren trai trong khung choi}TANK,*PTANK;
    Với TankE nếu bị Tank ta bắn trúng, trường iPower sẽ bị trừ đi 1, nếu trường này là 0, TankE này sẽ bị loại khỏi màn hình.
    Với Tank ta, khi iPower là 0, Tank khởi tạo lại với trường cLife bị trừ 1, nếu trường cLife là 0, Tank ta bị loại hoàn toàn.
    Vì chúng ta phải xác định số lượng đạn của Tank người chơi hiển thị trên màn hình cho yêu cầu bắn liên thanh nên biến pBull sẽ lưu 3 con trỏ của 3 viên đạn nếu có.

    Thêm vào các biến toàn cục mới

    Mã:
    CPtrArray               g_arTankE(FALSE);           // Array TankETANK                    g_TankL;                    // TankLTANK                    g_TankR;                    // TankR
    Sau đây là hàm vẽ các EnemyTank

    Mã:
    BOOL DirectX_DrawTankE(CPtrArray * arTankE,int xFrame,int yFrame){    int         i,cCount;    PENEMYTANK  pTankE;    RECT        r;    HRESULT     hr;     cCount = arTankE->GetSize();    for(i=0;i<cCount;i++)    if(pTankE = (PENEMYTANK)arTankE->GetAt(i))    {        SetRect(&r, pTankE->iFrame*LARGE, (pTankE->iFrend-1)*LARGE, (pTankE->iFrame+1)*LARGE, pTankE->iFrend*LARGE);        hr = g_lpBack->BltFast(xFrame+pTankE->x,yFrame+pTankE->y,g_lpTankE[pTankE->iStyle],&r,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}
    Trở lại các Bitmap Tank đã được vẽ trong ngày 6. Các Bítmap này được vẽ theo ma trận 4x4 khung.
    4 Khung hàng trên cùng được vẽ cho Tank di chuyển xuống dưới FM_DOWN.
    4 Khung hàng thứ hai vẽ cho Tank di chuyển sang trái FM_LEFT.
    4 Khung hàng thứ ba vẽ cho Tank di chuyển lên trên FM_UP.
    4 Khung hàng dưới cùng vẽ cho Tank di chuyển sang trái FM_RIGHT.
    Câu lệnh SetRect xác định chính xác vùng hình ảnh của Bitmap cần hiển thị theo số khung và hướng.
    g_lpTankE[pTankE->iStyle] xác định Bitmap nào được vẽ.

    Tiếp theo là các hàm vẽ TankL và TankR

    Mã:
    BOOL DirectX_DrawTankL(int xFrame,int yFrame){    RECT        r;    HRESULT     hr;     if(g_TankL.bReady)    {        SetRect(&r, g_TankL.iFrame*LARGE, (g_TankL.iFrend-1)*LARGE, (g_TankL.iFrame+1)*LARGE, g_TankL.iFrend*LARGE);        hr = g_lpBack->BltFast(xFrame+g_TankL.x,yFrame+g_TankL.y,g_lpTankL,&r,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawTankR(int xFrame,int yFrame){    RECT        r;    HRESULT     hr;     if(g_TankR.bReady)    {        SetRect(&r, g_TankR.iFrame*LARGE, (g_TankR.iFrend-1)*LARGE, (g_TankR.iFrame+1)*LARGE, g_TankR.iFrend*LARGE);        hr = g_lpBack->BltFast(xFrame+g_TankR.x,yFrame+g_TankR.y,g_lpTankR,&r,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}
    Vẽ Tank của người chơi cũng giống như vẽ TankE ngoại trừ Tank ta không có kiểu.
    Chúng ta sẽ vẽ các thuộc tính cần thiết bên dưới nơi hiển thị lúc đầu của Tank.

    - - - Nội dung đã được cập nhật ngày 12-03-2014 lúc 02:24 PM - - -

    <font color="#0000FF">NGÀY 20 :
    Vẽ Bullet, Card, Fire, Smoke.

    Thêm vào khai báo định nghĩa các cấu trúc sau

    Mã:
    typedef struct{    BOOL        bBullF;         // La dan lua    int         iFrend;         // Huong di chuyen hien hanh    int         iSpeed;         // Toc do di chuyen tu 5->10 Pixels trong 1 lan    int         x,y;            // Toa do LeftTop tinh theo khung choi}BULLETE,*PBULLETE;typedef struct{    BOOL        bBullF;         // La dan lua    int         iFrend;         // Huong di chuyen hien hanh    int         iSpeed;         // Toc do di chuyen tu 5->10 Pixels trong 1 lan    PVOID       pTank;          // TankL hay TankR ban ra    int         x,y;            // Toa do LeftTop tinh theo khung choi}BULLET,*PBULLET;typedef struct{    int         cDelay;         // So lan lam tre truoc khi huy    int         iStyle;         // Kieu card tu 0->10    int         x,y;            // Toa do LeftTop tinh theo khung choi}CARD,*PCARD;typedef struct{    int         iFrame;         // Khung trong bitmap    int         x,y;            // Toa do LeftTop tinh theo khung choi  }FIRE,*PFIRE;typedef struct{    int         cDelay;         // So lan lam tre de ve khung khac    int         iFrame;         // Khung trong bitmap    int         x,y;            // Toa do LeftTop tinh theo khung choi}SMOKE,*PSMOKE;
    Khác nhau giữa đạn của ta và địch là ở chỗ : Đạn địch là chương trình tính toán và tạo ngẫu nhiên, đạn ta là từ nhập liệu của người chơi.
    Vì Tank của ta có thể bắn liên tục với số đạn là trường cBulletJoin trong cấu trúc TANK nên ta phải biết được số đạn mà Tank đã bắn mà
    vẫn còn hiện diện trên màn hình, do đó cấu trúc BULLET (cấu trúc đạn của ta) cần lưu con trỏ tới Tank cha của nó để thông báo ngược
    trở lại khi nó nổ -> Tank cha sẽ đặt lại NULL cho con trỏ của viên đạn mà nó bắn.
    Trường cDelay của CARD chỉ định số lần hiển thị sẽ bị trừ dần, nếu bằng 0 tức là hết thời gian, Card sẽ bị hủy.
    Trường cDelay của SMOKE chỉ là làm chậm lại hình ảnh đám khói mà thôi.

    Tiếp theo khai báo một số biến toàn cục theo dõi đạn, card, fire, smoke.

    Mã:
    CPtrArray               g_arBullE(FALSE);           // BullECPtrArray               g_arBullL(FALSE);           // BullLCPtrArray               g_arBullR(FALSE);           // BullRCPtrArray               g_arCard(FALSE);            // CardCPtrArray               g_arSmoke(FALSE);           // SmokeCPtrArray               g_arFire(FALSE);            // Fire
    Cũng như các biến của Tank, các biến mảng con trỏ này được khởi tạo để lưu con trỏ tới cấu trúc (tham số khởi tạo là FALSE - Tức không phải lớp mà là cấu trúc).

    Sau đây là các hàm vẽ các đối tượng trên

    Mã:
    BOOL DirectX_DrawBulletE(CPtrArray * arBulletE,int xFrame,int yFrame){    int         i,cCount;    PBULLETE    pBulletE;    HRESULT     hr;     cCount = arBulletE->GetSize();    for(i=0;i<cCount;i++)    if(pBulletE = (PBULLETE)arBulletE->GetAt(i))    {        hr = g_lpBack->BltFast(xFrame+pBulletE->x,yFrame+pBulletE->y,pBulletE->bBullF ? g_lpBullF : g_lpBullN,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawBulletL(CPtrArray * arBulletL,int xFrame,int yFrame){    int         i,cCount;    PBULLET     pBulletL;    HRESULT     hr;     cCount = arBulletL->GetSize();    for(i=0;i<cCount;i++)    if(pBulletL = (PBULLET)arBulletL->GetAt(i))    {        hr = g_lpBack->BltFast(xFrame+pBulletL->x,yFrame+pBulletL->y,pBulletL->bBullF ? g_lpBullF : g_lpBullN,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawBulletR(CPtrArray * arBulletR,int xFrame,int yFrame){    int         i,cCount;    PBULLET     pBulletR;    HRESULT     hr;     cCount = arBulletR->GetSize();    for(i=0;i<cCount;i++)    if(pBulletR = (PBULLET)arBulletR->GetAt(i))    {        hr = g_lpBack->BltFast(xFrame+pBulletR->x,yFrame+pBulletR->y,pBulletR->bBullF ? g_lpBullF : g_lpBullN,NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawCards(CPtrArray * arCard,int xFrame,int yFrame){    int         i,cCount;    PCARD       pCard;    HRESULT     hr;     cCount = arCard->GetSize();    for(i=0;i<cCount;i++)    if(pCard = (PCARD)arCard->GetAt(i))    {        hr = g_lpBack->BltFast(xFrame+pCard->x,yFrame+pCard->y,g_lpCard[pCard->iStyle],NULL,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawSmoke(CPtrArray * arSmoke,int xFrame,int yFrame){    int         i,cCount;    PSMOKE      pSmoke;    HRESULT     hr;    RECT        r;     cCount = arSmoke->GetSize();    for(i=0;i<cCount;i++)    if(pSmoke = (PSMOKE)arSmoke->GetAt(i))    {        SetRect(&r,pSmoke->iFrame*88,0,(pSmoke->iFrame+1)*88,96);        hr = g_lpBack->BltFast(xFrame+pSmoke->x,yFrame+pSmoke->y,g_lpSmoke,&r,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}BOOL DirectX_DrawFire(CPtrArray * arFire,int xFrame,int yFrame){    int         i,cCount;    PFIRE       pFire;    HRESULT     hr;    RECT        r;     cCount = arFire->GetSize();    for(i=0;i<cCount;i++)    if(pFire = (PFIRE)arFire->GetAt(i))    {        SetRect(&r,pFire->iFrame*40,0,(pFire->iFrame+1)*40,40);        hr = g_lpBack->BltFast(xFrame+pFire->x,yFrame+pFire->y,g_lpFire,&r,DDBLTFAST_SRCCOLORKEY);        if(hr != DD_OK && hr != DDERR_WASSTILLDRAWING)        {            if(hr != DDERR_SURFACELOST)                return FALSE;            if(!DirectX_RestoreSurfaceAll())                return FALSE;        }    }    return TRUE;}
    Tất cả các hàm này đều được truyền vào một con trỏ tới lớp CPtrArray.
    Công việc chủ yếu là duyệt qua tất cả các phần tử của mảng, tính toán tọa độ đích, rect trong Bitmap, xác định bề mặt rồi truyền khối Bit tới g_lpBack.

    Ngày hôm nay đã hơi nhiều, mỏi tay rồi, nghỉ thôi.
    Tạm biệt.

    [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG] [IMG]images/smilies/17.gif[/IMG]

 

 
Trang 1 của 3 123 CuốiCuối

Quyền viết bài

  • Bạn Không thể gửi Chủ đề mới
  • Bạn Không thể Gửi trả lời
  • Bạn Không thể Gửi file đính kèm
  • Bạn Không thể Sửa bài viết của mình
  •