编辑
2022-03-14
课程学习
00
请注意,本文编写于 983 天前,最后修改于 34 天前,其中某些信息可能已经过时。

目录

主要功能
主要类
object类
Tank类
MyTank类
EneTank类
Shell类
Wall类
关键功能
多线程
碰撞检测
计时器
屏幕绘制
按键检测
随机行为
代码
Fighting.h
Fighting.cpp
FightingDlg.h
FightingDlg.cpp
EneTank.h
Object.h
Object.cpp
Tank.h
Tank.cpp
MyTank.h
MyTank.cpp
EneTank.cpp
Shell.h
Shell.cpp
wall.h
wall.cpp

这是大一时写的代码了,有时间了把它放上来,顺便看看自己现在写代码有没有长进。印象中大一写这个坦克大战时真的是被逼得死去活来的,难是真的难,不知道现在这门课还是不是这个坦克大战的作业了,如果要借鉴的话一定注意不要照搬啊!

工程文件请见:面向对象的程序设计


主要功能

坦克大战程序实现了游戏中水池石墙草丛铁墙的基本功能,还创新地添加了木箱。坦克、炮弹、方块之间可以进行碰撞检测。并且实现了游戏的结束判定。使用WSAD移动,space发射子弹,K自杀,P开启“作弊”(铁墙保护和生命增加等)

  • 生命值:被敌人击中生命会减少,并且回到重生点,生命值为0,游戏结束。

  • 水池:子弹能通过,但坦克无法通过。

  • 木箱:子弹不能通过,坦克无法通过,并且木箱在接触子弹或其它方块时被摧毁,可以是唯一可以被坦克推动的方块。

  • 砖墙: 子弹不能通过,坦克无法通过,并且在接触子弹后被摧毁。

  • 草丛:子弹、坦克均可通过,并且会被掩藏。

  • 铁墙:任何物体都不可通过,无法摧毁。

  • 基地:被子弹打中或被敌人入侵游戏即结束,友方坦克可以通过。

主要类

object

object类是所有物体的基类。

属性

  • m_rct:位置
  • m_nDir:方向
  • m_nSpeed:速度
  • m_movable:可否移动

函数

  • 基本的碰撞的函数

Tank

Tank类继承自object类。

属性

  • m_life:生命

函数

  • 虚函数

    • show()
    • fire()
  • 普通函数

    • life_decease():生命减少
    • TisOverlap():坦克碰撞函数
    • TOisOverlap():坦克碰撞函数

MyTank

MyTank类继承自Tank类。

属性

  • m_move:是否处于移动状态,是实现移动开炮同时进行的变量
  • m_respanpos:重生点
  • m_img:静态成员变量、存储贴图

函数

  • 虚函数

    • Show():重写虚函数
  • 普通函数

    • respan():重生
    • gameover():游戏结束
    • show_life():生命显示
    • myMove():移动
    • setDir():转向

EneTank

EneTank类继承自Tank类。

属性

  • ene_img:静态成员变量,存储贴图

  • level:静态成员变量,存储难度

  • total_num:静态成员变量,存储消灭坦克数量

  • m_time:记录开炮时间

  • m_firetime:记录运动的时间

  • m_span:开炮时间间隔

  • m_firespan:开火时间间隔

函数

  • 虚函数

    • Show():重写虚函数
  • 普通函数

    • AutoMove():自动移动
    • ChangeDir():自动转向
    • re_time():立即转向
    • fireClock():开炮计与重置时
    • eme_down():敌人死亡

Shell

Shell类继承自object类。

属性

  • m_inbox:是否在界面内
  • m_time:飞行时间
  • m_iniRct:初始位置
  • s_img:静态成员变量、存储贴图

函数

  • 虚函数Show():重写
  • 其他函数
    • create():创建
    • SSisOverlap():炮弹碰撞
    • UpdatePos():更新位置

Wall

Wall类继承自object类。

属性

  • type:墙的类型
    • ShellPass
    • TankPass
  • destroyable:是否可破坏
  • w_img:静态成员变量、存储贴图

函数

  • 虚函数

    • Show():重写
    • Move():重写
    • disappear()
  • 其他函数

关键功能

此部分内容待更新!

多线程

碰撞检测

计时器

屏幕绘制

按键检测

随机行为


代码

Fighting.h

c++
// Fighting.h: PROJECT_NAME 应用程序的主头文件 // #pragma once #ifndef __AFXWIN_H__ #error "在包含此文件之前包含 'pch.h' 以生成 PCH" #endif #include "resource.h" // 主符号 // CFightingApp: // 有关此类的实现,请参阅 Fighting.cpp // class CFightingApp : public CWinApp { public: CFightingApp(); // 重写 public: virtual BOOL InitInstance(); // 实现 DECLARE_MESSAGE_MAP() }; extern CFightingApp theApp;

Fighting.cpp

c++
// Fighting.cpp: 定义应用程序的类行为。 // #include "pch.h" #include "framework.h" #include "Fighting.h" #include "FightingDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CFightingApp BEGIN_MESSAGE_MAP(CFightingApp, CWinApp) ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() // CFightingApp 构造 CFightingApp::CFightingApp() { // 支持重新启动管理器 m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART; // TODO: 在此处添加构造代码, // 将所有重要的初始化放置在 InitInstance 中 } // 唯一的 CFightingApp 对象 CFightingApp theApp; // CFightingApp 初始化 BOOL CFightingApp::InitInstance() { // 如果一个运行在 Windows XP 上的应用程序清单指定要 // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式, //则需要 InitCommonControlsEx()。 否则,将无法创建窗口。 INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // 将它设置为包括所有要在应用程序中使用的 // 公共控件类。 InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinApp::InitInstance(); AfxEnableControlContainer(); // 创建 shell 管理器,以防对话框包含 // 任何 shell 树视图控件或 shell 列表视图控件。 CShellManager *pShellManager = new CShellManager; // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题 CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows)); // 标准初始化 // 如果未使用这些功能并希望减小 // 最终可执行文件的大小,则应移除下列 // 不需要的特定初始化例程 // 更改用于存储设置的注册表项 // TODO: 应适当修改该字符串, // 例如修改为公司或组织名 SetRegistryKey(_T("应用程序向导生成的本地应用程序")); CFightingDlg dlg; m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: 在此放置处理何时用 // “确定”来关闭对话框的代码 } else if (nResponse == IDCANCEL) { // TODO: 在此放置处理何时用 // “取消”来关闭对话框的代码 } else if (nResponse == -1) { TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n"); TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n"); } // 删除上面创建的 shell 管理器。 if (pShellManager != nullptr) { delete pShellManager; } #if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS) ControlBarCleanUp(); #endif // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序, // 而不是启动应用程序的消息泵。 return FALSE; }

FightingDlg.h

c++
// FightingDlg.h: 头文件 // #pragma once #include "Object.h" #include "Shell.h" #include "wall.h" #include<vector> #include "EneTank.h" #include "MyTank.h" #include <thread> #include <mutex> #include <Windows.h> using namespace std; // CFightingDlg 对话框 class CFightingDlg : public CDialogEx { // 构造 public: CFightingDlg(CWnd* pParent = nullptr); // 标准构造函数 // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_FIGHTING_DIALOG }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 static bool work; static MyTank myTank; static vector<EneTank>eneTank; static vector<Shell>myvecShell; static vector<Shell>enevecShell; static vector<wall>vecWall; static mutex mut; static void Collide1(); static void Collide2(); //static void threadTT(); //static void threadSS(); //static void threadTS(); // 实现 protected: HICON m_hIcon; // 生成的消息映射函数 virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() public: afx_msg void OnTimer(UINT_PTR nIDEvent); afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); // afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnDestroy(); virtual BOOL PreTranslateMessage(MSG* pMsg); afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); };

FightingDlg.cpp

c++
// FightingDlg.cpp: 实现文件 // #include "pch.h" #include "framework.h" #include "Fighting.h" #include "FightingDlg.h" #include "afxdialogex.h" #include "Shell.h" #ifdef _DEBUG #define new DEBUG_NEW #endif CPoint EneTank::birth[3] = { CPoint(100,100),CPoint(300,100),CPoint(500,100) }; MyTank CFightingDlg::myTank; vector<EneTank> CFightingDlg::eneTank; vector<Shell> CFightingDlg::myvecShell; vector<Shell> CFightingDlg::enevecShell; vector<wall> CFightingDlg::vecWall; CString str; int shell_num ; bool CFightingDlg::work = true; mutex CFightingDlg::mut; void CFightingDlg::Collide1() { int i, j; CRect tank; while (work) { if (mut.try_lock()) { //////炮弹碰撞////// for (i = 0; i < myvecShell.size(); i++) { //我方炮弹出界 if (!myvecShell[i].m_inbox) { myvecShell.erase(myvecShell.begin() + i); i--; continue; } //敌我炮弹碰撞 for (j = 0; j < enevecShell.size(); j++) { if (myvecShell[i].SSisOverlap(enevecShell[j])) { myvecShell.erase(myvecShell.begin() + i); enevecShell.erase(enevecShell.begin() + j); i--; j--; break; } } } //敌方炮弹出界 for(i=0;i<enevecShell.size();i++) if (!enevecShell[i].m_inbox) { enevecShell.erase(enevecShell.begin() + i); i--; } //////敌方坦克与我方炮弹////// for (i = 0; i < eneTank.size(); i++) { tank = eneTank[i].GetPos(); for (j = 0; j < myvecShell.size(); j++) { if (myvecShell[j].IsOverLap(tank)) { myvecShell.erase(myvecShell.begin() + j); eneTank.erase(eneTank.begin() + i); EneTank::eme_down(); i--; break; } } } //玩家坦克与敌方炮弹 for (i = 0; i < enevecShell.size(); i++) { tank = myTank.GetPos(); if (enevecShell[i].IsOverLap(tank)) { myTank.lifedecrease(); myTank.respan(); enevecShell.erase(enevecShell.begin() + i); i--; } } //////坦克碰撞////// for (i = 0; i < eneTank.size(); i++) { if (myTank.TOisOverlap(eneTank[i])) { eneTank[i].re_time(); } } myTank.IsOut(); for (i = 0; i < eneTank.size(); i++) { for (j = 0; j < eneTank.size(); j++) { if (i != j) { if (eneTank[i].TOisOverlap(eneTank[j])) { eneTank[i].re_time(); eneTank[j].re_time(); } } } if (eneTank[i].TOisOverlap(myTank) || eneTank[i].IsOut()) { eneTank[i].re_time(); } } mut.unlock(); } } } void CFightingDlg::Collide2() { int i=0, j=0; bool check = false; while (work) { if (mut.try_lock()) { //炮弹与墙 for (i = 0; i < myvecShell.size(); i++) { for (j = 0, check = false; j < vecWall.size(); j++) { if (vecWall[j].IsOverLap(myvecShell[i].GetPos())) { if (vecWall[j].type == BASE) { myTank.gameover(); work = false; } if (vecWall[j].ShellPass) { continue; } else { check = true; if (vecWall[j].destroyable) { vecWall.erase(vecWall.begin() + j); j--; } } } } if (check) { myvecShell.erase(myvecShell.begin() + i); i--; } } for (i = 0; i < enevecShell.size(); i++) { for (j = 0, check = false; j < vecWall.size(); j++) { if (vecWall[j].IsOverLap(enevecShell[i].GetPos())) { if (vecWall[j].type == BASE) { myTank.gameover(); work = false; } if(vecWall[j].ShellPass) { continue; } else { check = true; if (vecWall[j].destroyable) { vecWall.erase(vecWall.begin() + j); j--; } } } } if (check) { enevecShell.erase(enevecShell.begin() + i); i--; } } //坦克与墙 //敌方 for (i = 0; i < eneTank.size(); i++) { for (j = 0; j < vecWall.size(); j++) { if (vecWall[j].type == BASE) { if (vecWall[j].IsOverLap(eneTank[i].GetPos())) { myTank.gameover(); work = false; } } if (vecWall[j].type!=GRASS&&eneTank[i].TOisOverlap(vecWall[j])) { eneTank[i].re_time(); break; } } } //玩家 for (j = 0; j < vecWall.size(); j++) { if (!vecWall[j].TankPass) { if (myTank.TOisOverlap(vecWall[j])) { break; } } } //墙与墙 for (j = 0; j < vecWall.size(); j++) { for (i = 0; i < vecWall.size(); i++) { if (vecWall[j].m_movable && j != i) { if (vecWall[j].IsOverLap(vecWall[i].GetPos())&& vecWall[i].type!=GRASS) { vecWall.erase(vecWall.begin() + j); j--; break; } vecWall[j].IsOut(); } } } mut.unlock(); } } } // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx { public: CAboutDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ABOUTBOX }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CFightingDlg 对话框 CFightingDlg::CFightingDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_FIGHTING_DIALOG, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CFightingDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CFightingDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_TIMER() ON_WM_KEYDOWN() // ON_WM_KEYUP() // ON_WM_ERASEBKGND() ON_WM_ERASEBKGND() ON_WM_DESTROY() ON_WM_KEYUP() END_MESSAGE_MAP() // CFightingDlg 消息处理程序 BOOL CFightingDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != nullptr) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 SetTimer(1, 40, NULL); SetTimer(2, 20000, NULL); SetTimer(3, 20, NULL); srand(time(NULL) * time(NULL)); shell_num = 2;//坦克可发射炮弹数 ////////贴图导入//////// CString strFileName; TCHAR cPath[1024]; GetModuleFileName(NULL, cPath, 1024); strFileName = cPath; CString str; //对炮弹的静态成员变量赋值 str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\shell.png"; Shell::s_img.Load(str); //对敌方坦克的静态成员变量赋值 str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\eneTank0.png"; EneTank::ene_img[0].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\eneTank1.png"; EneTank::ene_img[1].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\eneTank2.png"; EneTank::ene_img[2].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\eneTank3.png"; EneTank::ene_img[3].Load(str); //墙 str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\0.png"; wall::w_img[BRICK].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\1.png"; wall::w_img[METAL].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\2.png"; wall::w_img[GRASS].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\3.png"; wall::w_img[WATER].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\4.png"; wall::w_img[WOOD].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\5.png"; wall::w_img[BASE].Load(str); //对坦克的静态成员变量赋值 str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\mytank0.png"; MyTank::m_img[0].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\mytank1.png"; MyTank::m_img[1].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\mytank2.png"; MyTank::m_img[2].Load(str); str = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"material\\mytank3.png"; MyTank::m_img[3].Load(str); ////////////线程///////////// thread T(Collide1); T.detach(); thread W(Collide2); W.detach(); //////////地图载入/////////// for(int j = 0; j < Y_MAX / 100; j++) for (int i = 0; i < X_MAX / 50; i++) { if((i+j)%4) vecWall.push_back(wall(CPoint(i * 50, j * 100), (i + j)% 5)); } vecWall.push_back(wall(CPoint(250, 450),BASE)); vecWall.push_back(wall(CPoint(200, 400), BRICK)); vecWall.push_back(wall(CPoint(200, 450), WOOD)); vecWall.push_back(wall(CPoint(300, 450), WOOD)); ///////////敌方坦克初始化/////////// for(int i=0;i<3;i++) { EneTank eme(i); eneTank.push_back(eme); } //其他 SetWindowPos(NULL, 0, 0, X_MAX + 200, Y_MAX + 40+10, SWP_NOMOVE); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CFightingDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CFightingDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } //CMemDC memDC(*GetDC(), this); //CDC* pDC = &memDC.GetDC(); int i = 0, j = 0; CRect rc; //CWnd* pWnd = GetDlgItem(IDC_IMAGE_BOX); //pWnd->GetClientRect(); GetClientRect(&rc);// 获取客户区 CDC* pDC = GetDC();//这里通过在CmemDC中绘制,以解决闪烁问题 CMemDC dcMem(*pDC, this); CDC& dc = dcMem.GetDC(); //////敌方坦克////// if (myTank.m_life > 0) { if (eneTank.size() < 3) { EneTank eme; eneTank.push_back(eme); } for (i = 0; i < eneTank.size(); i++) { eneTank[i].AutoMove(); if (eneTank[i].fireClock()) enevecShell.push_back(eneTank[i].fire()); } /////////////////////////绘制部分///////////////////////////// //////绘制背景////// dc.FillSolidRect(&Range, RGB(0, 0, 0)); for (i = 0; i < vecWall.size(); i++) if (vecWall[i].type == WATER || vecWall[i].type == BASE || vecWall[i].type == WOOD) { vecWall[i].Show(&dc); } //////炮弹////// for (i = 0; i < myvecShell.size(); i++) myvecShell[i].Show(&dc); for (i = 0; i < enevecShell.size(); i++) enevecShell[i].Show(&dc); //////敌方坦克////// for (i = 0; i < eneTank.size(); i++) eneTank[i].Show(&dc); //////玩家坦克////// myTank.Show(&dc); for (i = 0; i < vecWall.size(); i++) { if (vecWall[i].type != WATER && vecWall[i].type != BASE && vecWall[i].type!=WOOD) { vecWall[i].Show(&dc); } } //生命显示 CRect rct(CPoint(620, 0), CPoint(620 + 30, 0 + 30)); myTank.show_life(&dc,rct); } else { work = false; str.Format(L"游戏结束!您消灭了%d个敌人", EneTank::total_num); dc.TextOut(250, 270,str); eneTank.clear(); } } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CFightingDlg::OnQueryDragIcon() { return static_cast<HCURSOR>(m_hIcon); } void CFightingDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 switch (nIDEvent) { case 1: InvalidateRect(Range, true); break; case 2: if(EneTank::level<9) EneTank::level++; break; case 3: myTank.myMove(); break; default: break; } CDialogEx::OnTimer(nIDEvent); } void CFightingDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { static bool used = false; CRect rct; CPoint pt; if (myTank.m_life == 0) return; switch (nChar) { case 'w': case 'W': myTank.setDir(0); break; case 's': case 'S': myTank.setDir(1); break; case 'a': case 'A': myTank.setDir(2); break; case 'd': case 'D': myTank.setDir(3); break; case 'p': case 'P': if (myTank.m_life < 30) { myTank.m_life += 5; } if (!used) { vecWall.push_back(wall(CPoint(200, 400), METAL)); vecWall.push_back(wall(CPoint(250, 400), METAL)); vecWall.push_back(wall(CPoint(300, 400), METAL)); vecWall.push_back(wall(CPoint(200, 450), METAL)); vecWall.push_back(wall(CPoint(300, 450), METAL)); shell_num = 1000; myTank.setSpeed(10); used = true; } break; case 'k': case 'K': myTank.gameover(); break; case VK_SPACE: if (myvecShell.size() < shell_num) { myvecShell.push_back(myTank.Fire()); } break; default: break; } CDialogEx::OnKeyDown(nChar, nRepCnt, nFlags); } BOOL CFightingDlg::OnEraseBkgnd(CDC* pDC) { // TODO: 在此添加消息处理程序代码和/或调用默认值 return false; } void CFightingDlg::OnDestroy() { CDialogEx::OnDestroy(); // TODO: 在此处添加消息处理程序代码 work = false; } BOOL CFightingDlg::PreTranslateMessage(MSG* pMsg) { // TODO: 在此添加专用代码和/或调用基类 return CDialogEx::PreTranslateMessage(pMsg); } void CFightingDlg::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (myTank.m_life == 0) return; switch (nChar) { case 'w': case 'W': case 's': case 'S': case 'a': case 'A': case 'd': case 'D': myTank.setDir(5); break; } CDialogEx::OnKeyUp(nChar, nRepCnt, nFlags); }

EneTank.h

c++
#pragma once #include "Tank.h" class EneTank : public Tank { public: EneTank(); EneTank(int pos); void Show(CDC* pDC); void AutoMove(); void EneTank::ChangeDir(); void re_time(); bool fireClock(); static void eme_down(); DWORD m_time; DWORD m_firetime; double m_span; int m_firespan; //消灭坦克的数量,是静态成员变量 static int total_num; static int level; //贴图 static CImage ene_img[4]; protected: //固定的出生点,静态成员变量 static CPoint birth[3]; };

Object.h

c++
#pragma once #define CORRECT 0 //单位格子的大小 //const CSize unitSize(50, 50); /*这一块地方很有问题, 不知道为什么mytank接收不到unitsize 可能是因为构造的时候,unitSize还未初始化*/ //方向对应的数字 const int DIR_UP = 0; const int DIR_DOWN = 1; const int DIR_LEFT = 2; const int DIR_RIGHT = 3; //单元格子的大小 const int size = 50; const int Tank_size = 45; //范围 const int X_MAX = 600; const int Y_MAX = 500; //+20是为了防止出界判断的延迟出现图片残留 //具体解决还可以尝试加快判断速度 const CRect Range(CPoint(0, 0), CPoint(X_MAX + 200, Y_MAX + 40)); class Object { public: virtual void Show(CDC* pDC) {}; void Move(int Dir); void SetPos(CRect rct) { m_rct = rct; m_rct.NormalizeRect(); }; CRect GetPos() { return m_rct; }; void setSpeed(int n) { this->m_nSpeed = n;} bool m_movable = true; bool IsOverLap(CRect& rct);//重叠、碰撞测试基础 bool IsOut(); CRect m_rct; protected: int m_nDir;//0-3:up\down\left\right int m_nSpeed; };

Object.cpp

c++
#include "pch.h" #include "Object.h" void Object::Move(int Dir) { //这里移动是将来的移动,能否实现,要通过碰撞测试 m_nDir = Dir; if (Dir == DIR_UP) { m_rct.MoveToY(m_rct.top - m_nSpeed); } else if (Dir == DIR_DOWN) { m_rct.MoveToY(m_rct.top + m_nSpeed); } else if (Dir == DIR_LEFT) { m_rct.MoveToX(m_rct.left - m_nSpeed); } else if (Dir == DIR_RIGHT) { m_rct.MoveToX(m_rct.left + m_nSpeed); } } bool Object::IsOverLap(CRect& rct) { CRect tmp; rct.NormalizeRect(); return IntersectRect(tmp, rct, m_rct); } bool Object::IsOut() { if (m_rct.bottom > Y_MAX || m_rct.top<0 || m_rct.right>X_MAX || m_rct.left < 0) { if (m_rct.bottom > Y_MAX) m_rct.OffsetRect(0, -(m_rct.bottom - Y_MAX + CORRECT)); if (m_rct.top < 0) m_rct.OffsetRect(0, -m_rct.top + CORRECT); if (m_rct.right > X_MAX) m_rct.OffsetRect(-(m_rct.right - X_MAX + CORRECT), 0); if (m_rct.left < 0) m_rct.OffsetRect(-m_rct.left + CORRECT, 0); return true; } return false; }

Tank.h

c++
#pragma once #include "Object.h" #include "Shell.h" class Tank:public Object { //这里只定义了Show方法,fire方法在子类中定义 public: Tank(); int m_life; virtual void Show(CDC* pDC); virtual Shell fire(); //生命-1 virtual void lifedecrease() { m_life--; } bool TisOverlap(CRect rct); bool TOisOverlap(Object& ob); };

Tank.cpp

c++
#include "pch.h" #include "Tank.h" Tank::Tank() { m_nDir = 3; m_nSpeed = 2; } Shell Tank::fire() { Shell sh; CPoint pt = m_rct.CenterPoint(); sh.Create(pt, m_nDir, m_nSpeed * 50); return sh; } bool Tank::TOisOverlap(Object& ob) { CRect& rct = ob.m_rct; bool check = false; CRect tmp; rct.NormalizeRect(); check = IntersectRect(tmp, rct, m_rct); if (check) { if (tmp.Height() < tmp.Width()) { if (tmp.bottom < rct.bottom) { this->m_rct.OffsetRect(0, -(tmp.Height() - CORRECT)); if (ob.m_movable) { rct.OffsetRect(0, (tmp.Height() - CORRECT)); } } else { this->m_rct.OffsetRect(0, (tmp.Height() - CORRECT)); if (ob.m_movable) { rct.OffsetRect(0, -(tmp.Height() - CORRECT)); } } } else { if (tmp.right < rct.right) { this->m_rct.OffsetRect(-tmp.Width() - CORRECT, 0); if (ob.m_movable) { rct.OffsetRect(tmp.Width() + CORRECT, 0); } } else { this->m_rct.OffsetRect(tmp.Width() - CORRECT, 0); if (ob.m_movable) { rct.OffsetRect(-(tmp.Width() - CORRECT), 0); } } } } return check; } bool Tank::TisOverlap(CRect rct) { bool check = false; CRect tmp; rct.NormalizeRect(); check = IntersectRect(tmp, rct, m_rct); if (check) { if (tmp.Height() < tmp.Width()) { if (tmp.bottom < rct.bottom) { this->m_rct.OffsetRect(0, -(tmp.Height()-CORRECT)); } else { this->m_rct.OffsetRect(0, (tmp.Height()-CORRECT)); } } else { if (tmp.right < rct.right) { this->m_rct.OffsetRect(-tmp.Width()-CORRECT,0); } else { this->m_rct.OffsetRect(tmp.Width()-CORRECT,0); } } } return check; } void Tank::Show(CDC* pDC) { //pDC->Rectangle(m_rct); //pDC->MoveTo(m_rct.CenterPoint()); //if (m_nDir == 0) // pDC->LineTo(m_rct.CenterPoint().x, m_rct.CenterPoint().y - 20); //else if (m_nDir == 1) // pDC->LineTo(m_rct.CenterPoint().x, m_rct.CenterPoint().y + 20); //else if (m_nDir == 2) // pDC->LineTo(m_rct.CenterPoint().x - 20, m_rct.CenterPoint().y); //else if (m_nDir == 3) // pDC->LineTo(m_rct.CenterPoint().x + 20, m_rct.CenterPoint().y); //////////////////贴图的实现///////////////////// /*pDC->Rectangle(m_rct); pDC->MoveTo(m_rct.CenterPoint());*/ }

MyTank.h

c++
#pragma once #include "Tank.h" class MyTank : public Tank { public: Shell Fire(); MyTank(); bool m_move = false; bool respan(); bool gameover(); bool show_life(CDC* pDC,CRect rct); void Show(CDC* pDC); void setDir(int i); void myMove(); static CImage m_img[4]; private: CPoint m_respanpos; };

MyTank.cpp

c++
#include "pch.h" #include "MyTank.h" #include<mmsystem.h> #pragma comment(lib,"winmm.lib") CImage MyTank::m_img[4]; Shell MyTank::Fire() { Shell sh; CPoint pt = m_rct.CenterPoint(); sh.Create(pt, m_nDir, m_nSpeed*40); //播放声音 CString strFileName; TCHAR cPath[1024]; GetModuleFileName(NULL, cPath, 1024); strFileName = cPath; CString strSound = strFileName.Left(strFileName.ReverseFind('\\') + 1) + L"fire.wav"; PlaySound(strSound, 0, SND_FILENAME | SND_ASYNC); return sh; } void MyTank::Show(CDC* pDC) { //显示坦克 if (m_nDir == DIR_UP) m_img[DIR_UP].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); else if (m_nDir == DIR_DOWN) m_img[DIR_DOWN].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); else if (m_nDir == DIR_LEFT) m_img[DIR_LEFT].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); else if (m_nDir == DIR_RIGHT) m_img[DIR_RIGHT].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); } MyTank::MyTank() { m_respanpos = CPoint(150, 450); m_rct = CRect(m_respanpos, CSize(Tank_size,Tank_size)); m_life = 5; m_nSpeed = 4; m_nDir = 0; } bool MyTank::show_life(CDC* pDC,CRect rct) { for (int i = 1; i <= m_life; i++) { if (i % 5) { m_img[DIR_UP].Draw(pDC->GetSafeHdc(), rct, Gdiplus::InterpolationModeHighQuality); rct.OffsetRect(rct.Width() + 2, 0); } else { m_img[DIR_UP].Draw(pDC->GetSafeHdc(), rct, Gdiplus::InterpolationModeHighQuality); rct.OffsetRect(-(rct.Width() + 2)*4, rct.Height() + 2); } } return true; } bool MyTank::respan() { m_rct = CRect(m_respanpos, CSize(Tank_size, Tank_size)); m_nDir = 0; return true; } bool MyTank::gameover() { this->m_life = 0; return true; } void MyTank::setDir(int i) { if (i > 4) { m_move = false; } else { m_nDir = i; m_move = true; } } void MyTank::myMove() { if (m_move) { Move(m_nDir); } }

EneTank.cpp

c++
#include "pch.h" #include "EneTank.h" //静态成员变量初始化 CImage EneTank::ene_img[4]; //消灭坦克数 int EneTank::total_num = 0; //难度 int EneTank::level = 1; EneTank::EneTank() { m_firetime = m_time = GetTickCount(); m_nSpeed = 1+level; m_nDir = 1; m_life = 1; //随机方向和持续时间 unsigned int i; rand_s(&i); m_rct = CRect(birth[total_num % 3],CSize(Tank_size,Tank_size)); rand_s(&i); m_span = 1 + i % 2; m_firespan = 5 + i % 2; } EneTank::EneTank(int pos) { m_firetime = m_time = GetTickCount(); m_nSpeed = 1+level; m_nDir = 1; m_life = 1; //随机运动时间和开火间隔 unsigned int i; rand_s(&i); m_rct = CRect(birth[pos%3], CSize(Tank_size, Tank_size)); rand_s(&i); m_span = 1 + i % 2; m_firespan = 2 + (i % 12) / level; } void EneTank::re_time() { m_span = 0.5; } bool EneTank::fireClock() { //原理和AutoMove相似,随机开炮时间间隔 if ((GetTickCount() - m_firetime) / 1000 < m_firespan) return false; else { unsigned int i; rand_s(&i); m_firetime = GetTickCount(); m_firespan = 1+(i % 6) / (0.5*level); return true; } } void EneTank::Show(CDC* pDC) { if (m_nDir == DIR_UP) ene_img[DIR_UP].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); else if (m_nDir == DIR_DOWN) ene_img[DIR_DOWN].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); else if (m_nDir == DIR_LEFT) ene_img[DIR_LEFT].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); else if (m_nDir == DIR_RIGHT) ene_img[DIR_RIGHT].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); } void EneTank::AutoMove() { ////随机部分(后期实现时可以不用,改为遇墙再随机即可) if((GetTickCount()-m_time)/1000.0<m_span ) Move(m_nDir); else//否则重新随机方向和持续时间 ChangeDir(); } void EneTank::ChangeDir() { unsigned int i; rand_s(&i); m_nDir = (i % 6);//方向,可调节向下运动的权重 if (m_nDir > 3) m_nDir = 1; rand_s(&i); m_nSpeed = 1 + level; m_span = 3 + i % 5;//持续时间 m_time = GetTickCount(); } void EneTank::eme_down() { total_num++; }

Shell.h

c++
#pragma once #include "Object.h" class Shell :public Object { public: Shell(); CImage static s_img; bool m_inbox; public: void Show(CDC* pDC); void Create(CPoint pos, int nDir, int nSpeed); bool SSisOverlap(Shell& sh); protected: void UpdatePos(); protected: DWORD m_time; CRect m_iniRct; };

Shell.cpp

c++
#include "pch.h" #include "Shell.h" CImage Shell::s_img; Shell::Shell() { m_inbox = true; } bool Shell::SSisOverlap(Shell& sh) { if (IsOverLap(sh.GetPos())) { return true; } else return false; } void Shell::Show(CDC* pDC) { UpdatePos(); s_img.Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); } void Shell::UpdatePos() { DWORD tm = GetTickCount(); int nDis = (tm - m_time) * m_nSpeed / 1000; switch (m_nDir) { case DIR_UP: m_rct.MoveToY(m_iniRct.top - nDis); break; case DIR_DOWN: m_rct.MoveToY(m_iniRct.top + nDis); break; case DIR_LEFT: m_rct.MoveToX(m_iniRct.left - nDis); break; case DIR_RIGHT: m_rct.MoveToX(m_iniRct.left + nDis); break; default: break; } if (m_rct.top<0 || m_rct.bottom>Y_MAX || m_rct.left<0 || m_rct.right>X_MAX) m_inbox = false; } void Shell::Create(CPoint pos, int nDir, int nSpeed) { m_nDir = nDir; m_nSpeed = nSpeed; int nShellSize = 4; m_rct = CRect(pos.x - nShellSize, pos.y - nShellSize, pos.x + nShellSize, pos.y + nShellSize); m_iniRct = m_rct; m_time = GetTickCount(); }

wall.h

c++
#pragma once #include "Object.h" #define BRICK 0 #define METAL 1 #define GRASS 2 #define WATER 3 #define WOOD 4 #define BASE 5 class wall :public Object { public: wall(CPoint pt,int type); void Move(int Dir) {}; virtual void disappear() {}; void Show(CDC* pDC); //墙的类型和属性 int type = 0; bool ShellPass = false; bool TankPass = false; bool destroyable = false; //贴图 static CImage w_img[6]; //测试用的构造函数 };

wall.cpp

c++
#include "pch.h" #include "wall.h" CImage wall::w_img[6]; wall::wall(CPoint pt,int mode) { CRect temp(pt, CSize(size, size)); type = mode; m_movable = false; m_rct = temp; switch (type) { case BRICK: destroyable = true; break; case GRASS: ShellPass = true; TankPass = true; break; case WATER: ShellPass = true; break; case WOOD: destroyable = true; m_movable = true; break; case BASE: destroyable = true; TankPass = true; break; } }; void wall::Show(CDC* pDC) { w_img[type].Draw(pDC->GetSafeHdc(), m_rct, Gdiplus::InterpolationModeHighQuality); }

本文作者:Zerol Acqua

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!