NGÀY 37: Thêm vào chức năng tính điểm cho người chơi, hiển thị 20 người chơi có số điểm cao nhất, cài đặt cho F11
Trong trò chơi, chúng ta tìm cách xếp hạng theo thứ tự cho 20 người có kết quả cao nhất, nhưng lấy gì làm thước đo đây ?
Nếu lấy vòng chơi thì không ổn rồi, vì phím PageUp và PageDown đã cho phép người chơi tùy nghi thay đổi vòng chơi rồi.
Nghĩ tới nghĩ lui, mình quyết định lấy số điểm khi bắn TankE làm đơn vị tính điểm.
Chúng ta sẽ quy định, người chơi bắn 1 viên đạn trúng TankE sẽ được cộng thêm 1 điểm, bắn cháy TankE sẽ được cộng thêm 2 điểm.
Rồi, thống nhất như vậy và bắt tay vào làm thôi.
Thêm 2 cấu trúc
Mã:
typedef struct{ CHAR szName[MAX_PATH]; // Tên người chơi CHAR szDate[MAX_PATH]; // Chuỗi ngày tháng DWORD dwScore; // Điểm số}SCORE,*PSCORE;typedef struct{ int cCount; // So người được lưu tên hiện tại - Cao nhất là 20 SCORE Score[20]; // Cấu trúc Score của từng người}SCOREALL,*PSCOREALL;
Thêm 2 biến toàn cục
Mã:
SCORE g_Score; // ScoreSCOREALL g_ScoreAll; // ScoreAll
Trong hàm Bullet_HitTankE() thay đổi đoạn mã sau
Mã:
// mã cũ if((--pTankE->iPower) < 1) { if(pSmoke = (PSMOKE)malloc(sizeof(SMOKE))) // Kich thuoc 1 khung la 88x96 { pSmoke->cDelay = 5; pSmoke->iFrame = 0; pSmoke->x = pTankE->x - (88-LARGE)/2; pSmoke->y = pTankE->y - (96-LARGE)/2; if(!g_arSmoke.Add(pSmoke)) free(pSmoke); } arTankE->RemoveAt(i); } return TRUE;
thành
Mã:
// Mã mới if((--pTankE->iPower) < 1) { g_Score.dwScore += 1; if(pSmoke = (PSMOKE)malloc(sizeof(SMOKE))) // Kich thuoc 1 khung la 88x96 { pSmoke->cDelay = 5; pSmoke->iFrame = 0; pSmoke->x = pTankE->x - (88-LARGE)/2; pSmoke->y = pTankE->y - (96-LARGE)/2; if(!g_arSmoke.Add(pSmoke)) free(pSmoke); } arTankE->RemoveAt(i); } g_Score.dwScore += 1; return TRUE;
Trong WndProc() thêm dòng mã sau:
Mã:
// g_Score.dwScore = 0; // Thêm 1 dòng này vào mã đã có sẵn if(!Level_LoadData(&g_DataLevel,g_iLevel)) // Mã cũ { Cells_CreateValueRandom(g_DataLevel.iCells); for(i=0;i<5;i++) g_DataLevel.iStyleCard[i] = rand()%11; for(i=0;i<30;i++) g_DataLevel.iStyleTank[i] = rand()%4; Level_SaveData(&g_DataLevel,g_iLevel); }
Trong WinMain() thêm các dòng như sau
Mã:
// Mã thêm vào if(!Score_LoadData(&g_ScoreAll)) { g_ScoreAll.cCount = 0; Score_SaveData(&g_ScoreAll); } // Mã cũ g_ParamBezier.cMax = (rand()%20)*5+5; g_ParamBezier.xMax = rand()%10+1; g_ParamBezier.yMax = rand()%10+1;
Tạo mới 2 hàm Nạp và Lưu Score, nên đặt gần các hàm nạp lưu các đối tượng khác
Mã:
BOOL Score_LoadData(PSCOREALL pScoreAll){ CHAR szText[MAX_PATH]; HANDLE hFile; SCOREALL Data; DWORD dw; BOOL bFlag; GetModuleFileName(NULL,szText,MAX_PATH); PathRemoveFileSpec(szText); lstrcat(szText,"\\Tank2D"); if(!PathIsDirectory(szText)) return FALSE; lstrcat(szText,"\\Score.sco"); 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(SCOREALL)) { CloseHandle(hFile); return FALSE; } bFlag = ReadFile(hFile,&Data,sizeof(SCOREALL),&dw,NULL); CloseHandle(hFile); if(!bFlag || dw != sizeof(SCOREALL)) return FALSE; CopyMemory(pScoreAll,&Data,sizeof(SCOREALL)); return TRUE;}BOOL Score_SaveData(PSCOREALL pScoreAll){ 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,"\\Score.sco"); 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,pScoreAll,sizeof(SCOREALL),&dw,NULL); CloseHandle(hFile); if(!bFlag || dw != sizeof(SCOREALL)) { DeleteFile(szText); return FALSE; } return TRUE;}
Trong WndProc(), thêm mã cho phím F11 như sau
Mã:
// case IDC_F11: if(g_lpDD && g_iStatus == SP_LEVEL) { g_lpDD->FlipToGDISurface(); g_bMoused = TRUE; SendMessage(hwnd,WM_SETCURSOR,0,0); DialogBox(g_hInst,MAKEINTRESOURCE(IDD_DIALOG_SCORE),hwnd,DlgProc_Score); g_bMoused = FALSE; SendMessage(hwnd,WM_SETCURSOR,0,0); } return 0;
Cũng trong WndProc(), thêm xử lý thông điệp WM_CLOSE như sau
Mã:
// Mã mới case WM_CLOSE: if(g_lpDD) { g_lpDD->FlipToGDISurface(); g_bMoused = TRUE; SendMessage(hwnd,WM_SETCURSOR,0,0); DialogBox(g_hInst,MAKEINTRESOURCE(IDD_DIALOG_SCORE),hwnd,DlgProc_Score); g_bMoused = FALSE; SendMessage(hwnd,WM_SETCURSOR,0,0); } DestroyWindow(hwnd); return 0; // Mã cũ case WM_DESTROY: KillTimer(hwnd,1); if(g_hwndMCI) MCIWndDestroy(g_hwndMCI); g_arWave.RemoveAll(); g_arTankE.RemoveAll(); g_arBullE.RemoveAll(); g_arBullL.RemoveAll(); g_arBullR.RemoveAll(); g_arCard.RemoveAll(); g_arSmoke.RemoveAll(); g_arFire.RemoveAll(); DirectX_DeleteDD(); DirectX_DeleteDS(); DirectX_DeleteDI(); PostQuitMessage(0); return 0;
Tạo hộp thoại mới với id = IDD_DIALOG_SCORE, đổi Title thành "Score".
Thêm Listview có id = IDC_LIST1, đổi kiểu xem thành "Report", đặt thuộc tính "Client edge" và "Modal frame".
Thêm Edit id = IDC_EDIT1, có kiểu canh Text là "Centered".
Thêm Edit id = IDC_EDIT2, có kiểu canh Text là "Centered", check thuộc tính "Read-Only".
Thay text của button OK thành "Import", thay text của button Cancel thành "Close".
Canh chỉnh hộp thoại, ListView có chiều cao chứa khoảng trên 20 dòng. Chỉnh tạm thời thôi, khi chạy bạn chỉnh lại cho hoàn chỉnh.
Sau đây là hàm xử lý thông điệp cho hộp thoại này ( Đặt trước WndProc())
Mã:
BOOL CALLBACK DlgProc_Score(HWND hDlg,UINT message,WPARAM wparam,LPARAM lparam){ static HBRUSH hbrBack; static HWND hList,hEdit1,hEdit2; LVCOLUMN lvc; LVITEM lvi; CHAR szText[MAX_PATH]; SYSTEMTIME st; int i,cCount; switch(message) { case WM_INITDIALOG: SetClassLong(hDlg,GCL_HICON,(LONG)LoadIcon(g_hInst,MAKEINTRESOURCE(IDI_TANK2D))); hbrBack = CreateSolidBrush(g_DataSetup.crScreen); hList = GetDlgItem(hDlg,IDC_LIST1); hEdit1 = GetDlgItem(hDlg,IDC_EDIT1); hEdit2 = GetDlgItem(hDlg,IDC_EDIT2); lvc.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM; lvc.fmt = LVCFMT_CENTER; lvc.cx = 50; lvc.iSubItem = 0; lvc.pszText = "Index"; ListView_InsertColumn(hList,0,&lvc); // Cột đầu tiên là số thứ tự lvc.cx = 150; lvc.iSubItem = 1; lvc.pszText = "Name"; ListView_InsertColumn(hList,1,&lvc); // Cột thứ 2 là tên người chơi lvc.cx = 100; lvc.iSubItem = 2; lvc.pszText = "Score"; ListView_InsertColumn(hList,2,&lvc); // Cột thứ 3 là Điểm củ người chơi lvc.cx = 200; lvc.iSubItem = 3; lvc.pszText = "Date"; ListView_InsertColumn(hList,3,&lvc); // Cột cuối cùng là mốc thời gian của người chơi ListView_SetTextBkColor(hList,RGB(0,255,255)); ListView_SetBkColor(hList,RGB(0,255,255)); ListView_SetTextColor(hList,RGB(255,0,0)); ListView_SetExtendedListViewStyle(hList,LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT); // Đổ dữ liệu vào ListView lấy nguồn từ biến toàn cục g_ScoreAll for(i=0;i<g_ScoreAll.cCount;i++) { lvi.mask = LVIF_TEXT; lvi.iItem = i; lvi.iSubItem = 0; lvi.pszText = szText; wsprintf(szText,"%i",i+1); ListView_InsertItem(hList,&lvi); ListView_SetItemText(hList,i,1,g_ScoreAll.Score[i].szName); wsprintf(szText,"%d",g_ScoreAll.Score[i].dwScore); ListView_SetItemText(hList,i,2,szText); ListView_SetItemText(hList,i,3,g_ScoreAll.Score[i].szDate); } // Nếu chưa có tên, đặt trước NoName cho người chơi SetWindowText(hEdit1,(g_Score.szName[0] != '\0') ? g_Score.szName:"< No Name >"); wsprintf(szText,"%i",g_Score.dwScore); SetWindowText(hEdit2,szText); return TRUE; case WM_COMMAND: switch(LOWORD(wparam)) { case IDOK: GetWindowText(hEdit1,szText,GetWindowTextLength(hEdit1)+1); if(szText[0] == '\0') wsprintf(g_Score.szName,"< No Name >"); else wsprintf(g_Score.szName,szText); GetLocalTime(&st); // Nhận thời gian hệ thống wsprintf(g_Score.szDate,"%02d:%02d:%02d %02d/%02d/%d",st.wHour,st.wMinute,st.wSecond,st.wDay,st.wMonth,st.wYear); // Tìm nơi chèn dữ liệu mới trong ListView cCount = ListView_GetItemCount(hList); for(i=0;i<cCount;i++) { ListView_GetItemText(hList,i,2,szText,MAX_PATH); if(atoi(szText) <= (int)g_Score.dwScore) break; } lvi.mask = LVIF_TEXT; lvi.iItem = i; lvi.iSubItem = 0; lvi.pszText = szText; wsprintf(szText,"%i",i+1); ListView_InsertItem(hList,&lvi); ListView_SetItemText(hList,i,1,g_Score.szName); wsprintf(szText,"%d",g_Score.dwScore); ListView_SetItemText(hList,i,2,szText); ListView_SetItemText(hList,i,3,g_Score.szDate); i++; cCount ++; while(i<cCount) { wsprintf(szText,"%i",i+1); ListView_SetItemText(hList,i,0,szText); i++; } if(cCount > 20) // Nếu số dòng trong ListView vượt quá 20 thì xóa nó { ListView_DeleteItem(hList,cCount-1); cCount --; } // Lưu lại thay đổi vào biến toàn cục g_ScoreAll.cCount = cCount; for(i=0;i<cCount;i++) { ListView_GetItemText(hList,i,1,g_ScoreAll.Score[i].szName,MAX_PATH); ListView_GetItemText(hList,i,3,g_ScoreAll.Score[i].szDate,MAX_PATH/2); ListView_GetItemText(hList,i,2,szText,MAX_PATH); g_ScoreAll.Score[i].dwScore = atoi(szText); } Score_SaveData(&g_ScoreAll); // Lưu ra tập tin EnableWindow(GetDlgItem(hDlg,IDOK),FALSE); // Không cho người chơi lưu tiếp return TRUE; case IDCANCEL: SendMessage(hDlg,WM_CLOSE,0,0); return TRUE; } break; case WM_CTLCOLORDLG: return (int)hbrBack; case WM_CLOSE: DeleteObject(hbrBack); EndDialog(hDlg,0); return TRUE; } return FALSE;}
Hộp thoại này có thể xuất hiện bằng 2 cách, người chơi nhấn F11 hoặc thoát khỏi trò chơi.
Mã tuy dài nhưng cũng không khó hiểu lắm, chủ yếu là khi người chơi nhấn <Import> thì mã hơi khó nhưng có các chỉ dẫn rồi, hy vọng các bạn hiểu.
Khi thực thi, hộp thoại sẽ trông như sau
Ngày nay tới đây thôi, muộn rồi, tạm biệt. [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-04-2014 lúc 11:30 AM - - -</font>
<font color="#0000FF">NGÀY 38: Những xử lý cuối cùng
Ngày hôm nay, chúng ta sẽ thêm mã hiển thị các chức năng phím tắt ở phần trên cùng màn hình.
Các phím chúng ta xử lý là: F1, F2, F3, F4, F5, F6, F11, F12, PageUp, PageDown. Như vậy là dư ra : F7, F8, F9, F10, Insert, Delete, Home, End. Chuỗi hiển thị sẽ như sau:
F1: About F2: Design F3: Color F4: Image F5: Sound F6: Tank F11: Score F12: Bezier PageUp: Foward PageDown: Next
Hàm cho công việc này như sau ( nên đặt trên hàm DirectX_DrawTitle())
Mã:
void DirectX_DrawTextTop(){ DDSURFACEDESC ddsd; HDC hdc; HFONT hFont; RECT r; ddsd.dwSize = sizeof(ddsd); ddsd.dwFlags = DDSD_HEIGHT|DDSD_WIDTH; if(g_lpBack->GetSurfaceDesc(&ddsd) == DD_OK) if(g_lpBack->GetDC(&hdc) == DD_OK) { SetBkMode(hdc,TRANSPARENT); SetTextColor(hdc, (~(g_DataSetup.crScreen << 8)) >> 8); SetRect(&r,0,0,ddsd.dwWidth-1,ddsd.dwHeight-1); hFont = CreateFont(8,0,0,0,FW_NORMAL,0,0,0,0,0,0,0,0,"MS Sans Serif"); SelectObject(hdc,hFont); DrawText(hdc,"F1: About F2: Design F3: Color F4: Image F5: Sound F6: Tank F11: Score F12: Bezier PageUp: Foward PageDown: Next",-1,&r,DT_SINGLELINE|DT_CENTER|DT_TOP); DeleteObject(hFont); g_lpBack->ReleaseDC(hdc); }}
Trong hàm App_Update(), thêm vào 1 dòng mã gọi DirectX_DrawTextTop() như sau
Mã:
// Mã đã hiện hữu DirectX_DrawAndUpdateBezier(g_lpBack,&g_ParamBezier,xFrame,yFrame); switch(g_iStatus) { case SP_LEVEL: DirectX_DrawTextTop(); // Chỉ thêm vào duy nhất 1 dòng này DirectX_DrawTitle(iLevel,xFrame,yFrame); DirectX_DrawFrame(xFrame,yFrame); DirectX_DrawRightSide(xFrame,yFrame);
Khi thực thi chương trình, text sẽ hiển thị như hình dưới
Ngày hôm nay là kết thúc phần phát triển của trò chơi. Cũng là tạm chia tay với các bạn trong đề tài này.
Có lẽ bạn sẽ hỏi : Chương trình đã hoàn thành chưa ? Có cần phát triển gì thêm không ?
Với mình : Không bao giờ cảm thấy hoàn thiện, vẫn còn muốn viết thêm nhiều nữa, nhưng phải dừng lại ở đâu đó.
Thực ra lúc bắt đầu mình nghĩ sẽ mất khoảng nửa tháng cho trò chơi này, nhưng như bạn thấy thời gian đã gấp 3 lần so với dự tính.
Mục tiêu cho thời gian tới :
_ Dò tìm các lỗi đang tiềm ẩn nguy cơ gây bể vỡ chương trình.
_ Rút gọn mã nguồn xuống còn phân nửa hiện tại ( còn khoảng 3000 so với khoảng 6000 dòng).
_ Rút gọn kích thước tập tin thực thi Tank2D.exe xuống phân nửa hiện tại ( 532KB xuống còn khoảng 300KB - chế độ Release).
Chương trình viết trên Microsoft VC++6.0, Hệ điều hành Windows SP#3, CPU : Intel(R) Celeron(R) CPU G550 @ 2.60GHz 1.88 GB of RAM.
Chạy thử nghiệm chưa phát hiện ra lỗi nào, mong chờ các bạn chạy trên các máy khác nhau góp thêm ý kiến.
Hãy cùng " Học hỏi và chia sẻ" với mình nhé. Byebye !!!
Pallet nhựa Long An đã trở thành một trong những lựa chọn phổ biến cho nhu cầu vận chuyển và lưu trữ hàng hóa trong nhiều ngành công nghiệp. Với đặc tính nhẹ nhàng, chắc chắn và dễ vận chuyển, các...
Thanh lý pallet nhựa Long An giá rẻ