网站:点击打开链接是助 C++语言初学者上手图形编程的网站。
在VS2013中没有图形库:
从点击打开链接下载对应编辑器的EasyX库,EasyX 是针对 C++ 的图形库,可以帮助 C++语言初学者快速上手图形和游戏编程。
安装EasyX :点击打开链接。
从网站:点击打开链接可以学坦克大战的游戏设计。我主要加深对C++面向对象编程的理解。
面向对象的三大特性:封装、继承、多态。
设计步骤:
一、主坦克设计及在固定坐标界面中测试
1、画布类:实现创建和销毁画布的功能,还能够让其他代码随时通过这个类拿到画布的尺寸。
创建Graphic.h和Graphic.cpp文件。
在Graphic.h文件中主要是对游戏界面设计的封装。
方法:界面的创建static void Create();、销毁static void Destroy();,
获得界面的高度static int GetScreenHeight();,宽度static int GetScreenWidth();。
属性:界面的高度static int m_screen_height;和宽度static int m_screen_width;。
在Graphic.cpp文件中是对Graphic.h方法的实现和属性初始化。
其中类使用的是静态成员变量和静态成员函数,注意静态成员变量的初始化时在类外进行的。
2、坦克抽象类:为后面多态的使用做准备。
新建文件Tank.h。
在文件中先枚举坦克移动的4个方向enum Dir { UP, DOWN, LEFT, RIGHT };
建立显示virtual void Display() = 0;和移动的虚函数virtual void Move() = 0;。
建立受保护的属性(供继承中子类使用),颜色,移动坐标,步数,方向。
主战坦克设计:所谓主战坦克就是玩家控制的坦克,所有的坦克中,只有这个一个是可以控制的。
创建文件MainTank.h,MainTank类公有继承Tank,构造函数初始化Tank中所有成员变量。 style);
设置行驶方向:void SetDir(Dir dir); ,重写显示和移动方法。
绘制坦克主体void DrawTankBody(int style);
创建文件MainTank.cpp
对MainTank类中方法实现。
SetDir()修改成员变量的值。通过这个函数能够改变坦克的行驶方向。
DrawTankBody(int style);style有1和0分别表示上下和左右情况,通过参数负责绘制不同的形状。
这个函数负责画坦克的主体部分,一个正方形的坦克身和两个矩形的履带。
m_x,m_y这个坐标是坦克的中心点。
FillRectangle是填充一个Graphics,然后显示Rectangle指定的部分。
“FillRectangle就是用指定的图象填充坐标在(x, y)处,宽width,高height的矩形。
void fillrectangle (int left, int top, int right, int bottom);// 画填充矩形(有边框)
通过该函数可以确定中心点坐标((right+left)/2,(top+bottom)/2)。
以中心点向左移动left长度,向右移动right长度,向上移动top长度,向下移动bottom长度,形成一个矩形。
简单来说fillrectangle函数中前两个参数是要填充矩形的左上角点坐标,后两个参数是矩形的右下角点坐标。
//中部正方形的坦克身(8x8)
fillrectangle(m_x - 4, m_y - 4, m_x + 4, m_y + 4);
//左边矩形履带(2x12)
fillrectangle(m_x - 8, m_y - 6, m_x - 6, m_y + 6);
//右边矩形履带(2x12)
fillrectangle(m_x +6, m_y - 6, m_x +8, m_y + 6);
判断坦克的行驶方向,之后调用DrawTankBody绘制出坦克身
根据行驶方向画上炮管
setfillcolor(m_color);给坦克身体填充颜色。
DrawTankBody(1);上下移动
炮管长度为10.
上部炮管:line(m_x, m_y, m_x, m_y - 10);
下部炮管:line(m_x, m_y, m_x, m_y +10);
DrawTankBody(0);左右移动
左部炮管:line(m_x, m_y, m_x-10, m_y );
右部炮管:line(m_x, m_y, m_x+10, m_y);
Move()函数每执行一次,坦克向前移动m_step长度。当超出屏幕边沿时,从另一侧重新出现,行驶方向不变。
整个界面是坐标象限的第四象限,原点在左上角点。
上下左右出现越界。
1)、上部分越界即m_y < 0
重新给纵坐标赋值m_y = Graphic::GetScreenHight() - 1;即从下端出来
2)、下部分越界即m_y >m_y >Graphic::GetScreenHight()(纵坐标长度)
重新给纵坐标赋值m_y = 1;即从上端出来
3)、左部分越界即m_x < 0
重新给纵坐标赋值m_x = Graphic::GetScreenWidth() - 1;即从右端出来
4)、右部分越界即m_x >GetScreenWidth()(横坐标长度)
重新给纵坐标赋值m_x = 1;即从左端出来
3、主函数测试
通过kbhit()捕捉键盘动作
通过getch()得到按下键的码值
方向键
UP: 72
DOWN:80
LEFT: 75
RIGHT:77
Esc,程序退出。
ESC : 27
Space: 32
Enter程序暂停,再按一下程序继续
Enter: 13
创建mainTank对象
调用mainTank.SetDir()。
二、位置信息数据结构设计
游戏设计过程中,需要记录大量的位置信息,如果仅仅使用(x,y)坐标很容易出错,先定义两个简单的数据结构用来保存点和矩形的信息。
1、点Point类
创建Point.h文件
Point类中私有属性为m_x,m_y用来记录一个点的横、纵坐标。
通过构造函数初始化。
将“=”功能进行重载,方便用一个Point对象给另一个Point对象赋值,同时也能够将Point作为参数进行传递。
属性为私有,用Set()方法设置坐标,SetX()与SetY()单独设置横纵坐标。
方法GetX()和GetY()分别获得点横纵坐标值。
创建Point.cpp文件对Point类中方法实现。
2、矩阵Rect类
创建Rect.h文件,私有方法Check()创建对象时两个点顺序反了,Check()函数会自动把它们调整过来。
私有属性 有左上角点m_startPoint;和右下角点m_endPoint;。
建立两个构造函数,Rect(int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0)和Rect(const Point p1, const Point p2)
设置矩阵参数方法:Set(const Point pStart, const Point pEnd);和Set(int x1, int y1, int x2, int y2);。
设置左上右下角点方法:SetStartPoint(const Point p);,SetEndPoint(const Point p);
获得左上右下角点方法:GetStartPoint() const; GetEndPoint() const;两个函数都通过const修饰,表示返回值不能被修改。
获得高度和宽度的方法:GetWidth(); GetHeight();
创建Rect.cpp文件对Rect类中方法实现。
3、主战坦克升级
对Tank类进行修改,新增一部分功能。
计算出矩形位置virtual void CalculateSphere() = 0;
将原有的坐标点直接用Point类创建的对象m_pos(中心点)代替。
矩阵类创建对象m_rectSphere用来记录坦克的形状范围。
MainTank.h
修改原来的绘制坦克主体 void DrawTankBody();
对坦克中心点设置。
在MainTank.cpp中修改坐标及方法
坦克身:(12*12)
m.pos值是(300,298),StartPoint(287,288),EndPoint(313,308)
fillrectangle(m_pos.GetX() - 6, m_pos.GetY() - 6, m_pos.GetX() + 6, m_pos.GetY() + 6);
要填充的矩阵左上角点坐标是(294,292),右下角点坐标(306,304)
上下移动,左右两边履带
fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetStartPoint().GetY(),
m_rectSphere.GetStartPoint().GetX() + 4, m_rectSphere.GetEndPoint().GetY());
左:要填充的矩阵左上角点坐标是(287,288),右下角点坐标(291,308)
fillrectangle(m_rectSphere.GetEndPoint().GetX() - 4, m_rectSphere.GetStartPoint().GetY(),
m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetEndPoint().GetY());
右:要填充的矩阵左上角点坐标是(309,288),右下角点坐标(313,308)
左右移动,上下两边履带
由于程序首先执行上下运动,通过按下左右键,才能左右移动,此时的中心点的坐标已经改变。
选取左右位置的某一点
m.pos值是(298,288),StartPoint(288,275),EndPoint(308,301)
fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetStartPoint().GetY(),
m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetStartPoint().GetY() + 4);
上:要填充的矩阵左上角点坐标是(288,275),右下角点坐标(308,379)
fillrectangle(m_rectSphere.GetStartPoint().GetX(), m_rectSphere.GetEndPoint().GetY() - 4,
m_rectSphere.GetEndPoint().GetX(), m_rectSphere.GetEndPoint().GetY());
下:要填充的矩阵左上角点坐标是(288,297),右下角点坐标(313,308)
颜色及炮管、边界都要相应的修改。
GetBattleGround()给坦克划定一个运行区域。
CalculateSphere()计算出左上角和右下角的Point位置。
每移动一次都需要调用CalculateSphere()方法重新计算坦克区域。
4、敌人坦克
新建文件EnemyTank.h和EnemyTank.cpp。
有了Tank这个抽象类,所有的坦克都从它继承。除了抽象类中继承的函数之外,加了一个RandomTank()用来随机地在战场区域生成一个坦克。
5、修改main()函数演示
与之前相比:
新建了一个宏MAX_TANKS用来设置坦克的数量
用一个指针数组保存每个坦克的指针
循环创建坦克,这里用了new的方法把坦克对象创建在堆空间中
每次擦屏之后,遍历指针数组,绘制出每个坦克。调用的是Move()和Display()方法。
退出程序前,释放每一个坦克所占的堆空间。
源码:https://github.com/breakerthb/TankWar/tree/day2