“坦克大战”项目设计文档

「坦克大战」项目的设计文档

功能设计

游戏部分

​ 在存在 障碍物 的地图上,玩家需要操作一辆绿色的坦克与敌对的灰色坦克作战。 坦克 以及其发射的 炮弹 均会被障碍物所阻挡。玩家用键盘上的 wasd按键 控制坦克移动,用 鼠标 控制炮弹发射方向,按下 空格键 以发射炮弹。而敌方坦克则会自主移动并发射炮弹攻击玩家。炮弹碰到障碍物时会 反弹 ,但是反弹超过一定次数时炮弹就会消失。击毁敌方坦克可以得到一定的分数。每一关均有不同通关条件,分别是一星、二星、三星需要的通关分数。当玩家 用完炮弹操作的坦克被击毁 时,游戏结束。

关卡设计部分

​ 为了让玩家体验更多的快乐,并将快乐分享给其他人 (互相折磨) ,我们开发了 自定义关卡 功能。玩家可以自主设计关卡,将其储存到自己的存档里或生成 分享码 (一个由数字和符号构成的字符串),而其他玩家可以通过输入分享码导入关卡。除了完全自主设计之外,玩家也可以在其他关卡的基础上进行设计 ,让它更加折磨人

控制部分

​ 为了便于保存游戏进度,我们提供了 存档功能 ,将游戏自带文件以及玩家存档都放到游戏目录下的 datas 文件夹中。存档中包含了玩家各个关卡的进度,以及玩家自定义的关卡。

整体架构设计

输出图形时所使用的工具

​ 考虑到 C++ 自带的控制台不能绘制出这一游戏的画面,我们上网查阅了资料,决定使用 Easyx 图形库。使用 Easyx 可以便捷地画出直线、圆、矩形等几何图形,输出文本,导入图片,检测鼠标和键盘的状态。基于这些功能我们就能绘制出游戏中的坦克、炮弹、障碍物,以及控制游戏时用到的按钮、文本框、消息窗口了。

控制部分

widget类有三大功能:初始化, 页面跳转 ,以及绘图。初始化时构造背景数据,并用list容器存储;绘图时迭代器访问并输出;页面跳转:widget类通过 currentindex (记录当前页面的编号)这个变量实现功能的分类。在接受鼠标和键盘信息后,通过 button 类改变 currentindex 的值,再调用不同的函数实现对应的功能。点击“开始游戏”则运行 Level_Editor() 函数,点击“设计关卡”则通过 look_up_pages() 函数查看并选择关卡,选择完毕就进入 Game 类的成员函数 run() 中运行游戏。

游戏的运行

​ 通过 Game 类来实现运行功能。首先数据初始化,以及背景图绘制,接着点击“开始游戏”则开始运行游戏,在每次键盘和鼠标操作后,改变对应数据(坦克,炮弹,背景图中的分数等),并通过双缓冲统一绘制出来,以及改变帧率以此改善画质体验感。最后通过 is_win 这个布尔量判断是否结束循环,展示相应的动画的同时保留并改变该用户相应的数据。

自定义关卡 的实现思路与之类似。

存档部分

​ 通过 File_Manager 类来实现文件操作的功能。游戏一开始,先检测游戏文件是否完整,接着读取所有玩家的用户名和密码以完成登录。当玩家在某一关卡创造了新纪录,或保存正在编辑的关卡时,都要将新的玩家数据存档。我们按照一定规律编排数据,使得玩家信息、关卡信息都可以转化成字符串,同时也可以从字符串中读入这些信息。

数据结构设计

存档部分

​ 使用 Level_Data 类储存一个关卡的数据,包含坦克的初始位置、障碍物的位置、通关条件等信息。使用 Player_Data 类储存一个玩家的数据,包括用户名、密码、得分等。

​ 以下展示这两个类的部分定义:

struct d_wall    //储存障碍物数据的结构体
{
	int stx,sty,edx,edy; // 左上角、右下角的坐标 
};
struct d_player  //储存我方坦克数据的结构体
{
	int dir,x,y;     // 初始坦克位置 
	int bullet_num;  // 初始炮弹数量 
};
struct d_enemy   //储存敌方坦克数据的结构体
{
	int dir,x,y,seed,score;
	// 朝向( 0 表示横向, 1 表示纵向),初始坦克位置,随机种子,分值 
};
class Level_Data
{
public:
	string name;                // 关卡名 
	int score3,score2,score1;   // 通关的分数要求 
	list<d_wall> wall;          // 障碍物
	d_player player;            // 我方坦克
	list<d_enemy> enemy;        // 敌方坦克
}
class Player_Data
{
public:
	int file_id;          // 玩家存档的编号
	string name;          // 用户名
	string password;      // 密码
	list<int> score;      // 所有关卡的得分(先是固定关卡,再是自定义关卡) 
	list<string> c_level; // 自定义关卡的关卡分享码 
}

游戏运行部分

​ 当玩家点进一个关卡后,我们通过 Game 类完成一个关卡的运行。 Game 类中包含若干个成员,其中 optank 类型的数组 Enermy 储存的是 敌方坦克 的数据, tank 类型的变量 my_tank 储存的是 我方坦克 的数据, Bullet 类型的链表 bulletoptank_bullet 分别储存 我方坦克发射的炮弹敌方坦克发射的炮弹 的数据, Wall 类型的链表 wall 储存 障碍物 的数据。

Game 类的定义如下:

class Game{
	private:
		list<Wall> wall;                //障碍物
		list<Bullet> my_bullet;         //我方坦克发射的炮弹 
		list<Bullet> optank_bullet[30]; //敌方坦克发射的炮弹
		optank Enermy[30];              //敌方坦克
		tank my_tank;                   //我方坦克 
		Level_Data lvl;                 //这个关卡的数据
		int num;                        //敌方坦克数量 
}

关键算法设计

多线程的实现

​ 在游戏中,我们需要让敌方坦克、炮弹同时移动,同时能够让玩家随时操作我方坦克移动或攻击。但是 C++ 的程序是单线程运行的,这就需要我们把多线程的任务拆分成更小的子任务,每隔 20 毫秒依次执行各个子任务。我们拆分的子任务依次为:

  1. 检测鼠标左键是否被按下,如果是的话,我方坦克发射一枚炮弹;
  2. 敌方坦克移动;
  3. 我方炮弹移动;
  4. 敌方坦克发射炮弹;
  5. 敌方坦克炮弹移动;
  6. 判断我方坦克是否被敌方炮弹击中;
  7. 判断敌方坦克是否被我方炮弹击中;
  8. 如果分值发生变化,更新屏幕上显示的分值。

​ 在实现 文本框按钮 ,制作 自定义关卡 界面时,也同样采取了这种思路。

图形的绘制

​ 尽管有了 Easyx 这一工具,也要通过拼接各种几何图形完成游戏内容的绘制。

  • 文本框:边框为圆角矩形,内部绘制文本;

  • 按钮:先绘制有内部填充的圆角矩形,再在内部绘制文本;

  • 坦克:先绘制有内部填充的矩形(车身),再绘制由坦克中心指向鼠标的线段,最后绘制有内部填充的圆形(炮塔);

  • 子弹:有内部填充的圆形。

    这些图形的展示图如下:

    在这里插入图片描述
    ​ 后期在对游戏界面进行美化时,我们还制作了启动游戏时的渐变动画。这则是通过每隔40毫秒计算每个像素点的RGB值,并重新输出实现的(后来发现这样对于电脑显卡的要求很高,但是目前也想不到优化的方案)。

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值