前言

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

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


主要功能

坦克大战程序实现了游戏中水池石墙草丛铁墙的基本功能,还创新地添加了木箱。坦克、炮弹、方块之间可以进行碰撞检测。并且实现了游戏的结束判定。使用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


// 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


// 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


// 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


// 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

#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

#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

#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

#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

#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

#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

#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

#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

#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

#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

#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

#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);
}