这是大一时写的代码了,有时间了把它放上来,顺便看看自己现在写代码有没有长进。印象中大一写这个坦克大战时真的是被逼得死去活来的,难是真的难,不知道现在这门课还是不是这个坦克大战的作业了,如果要借鉴的话一定注意不要照搬啊!
工程文件请见:面向对象的程序设计
坦克大战程序实现了游戏中水池、石墙、草丛、铁墙的基本功能,还创新地添加了木箱。坦克、炮弹、方块之间可以进行碰撞检测。并且实现了游戏的结束判定。使用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 许可协议。转载请注明出处!