## C与C++游戏项目练习9:接金币游戏简易版
还是只能在devC++里面运行,不要用VS!!!
还是只能在devC++里面运行,不要用VS!!!
还是只能在devC++里面运行,不要用VS!!!
**


宝书镇楼

与快速敲字母和弹跳的小球3.0的主要区别以及改进:
1.把挡板(这里面是叫盘子)类和血量一样写成了全局变量,并且把ShowPlate、ClearPlate、MovePlate等函数归类到盘子类的成员函数,而不是由游戏控制类GameCtroller类控制,因为盘子的绘制、清除以及移动更符合盘子这个类的行为(就好比你能吃饭,老师喊你吃饭,老师应该是发出指令,但是吃饭这个能力属性是你有的),个人认为这样更加规范。

2.弹跳的小球3.0里面挡板能上下左右移动,这里根据惯常玩法只制作了左右移动功能,,但是,我灵机一动——

通过重载MovePlate成员函数改成捕捉鼠标事件使用Point类和按键都可以来控制盘子的左右移动,

void  MovePlate(char input)//根据键盘输入移动盘子(一次一格,好控制) {if (input == 'a'&& GetLeft()  > 0)//接收到a,并且不抵在左边界,盘子左移{x--;ChangeBound();//更改盘子边界}else if (input == 'd'&& GetRight()< STD_WIDTH)//接收到d,并且不抵在右边界,盘子右移{x++;ChangeBound();//更改盘子边界}}void MovePlate(int distance)//重载成员函数,通过鼠标移动距离来移动盘子(一次一段,不好控制) {distance %= STD_WIDTH;//防止距离超出范围,控制在游戏界面内if (GetLeft() > distance  && GetRight() + distance < STD_WIDTH)//在可移动范围内,最左边要至少能移动一个distance的距离,右边同理 {x += distance;ChangeBound();//更改盘子边界}else//避免x卡在边界处无法移动{if (x <= 0){x = 1+ridus;//起点加半径ChangeBound();}else{x = STD_WIDTH - 1-ridus;ChangeBound();}}}

*注意,鼠标不能拖拽太快,否则会造成卡顿以及出现多个盘子
**
顺便提一句,最早先让我知道有捕捉鼠标操作也是这个老师的网课教我的哦~~但是他这里面讲的内容需要配合EasyX实现


经过这个网课的培训我大一下工程实践1做走迷宫游戏(当时还是只学了C语言,代码写了1563行,带EasyX图形界面,可把我得瑟坏了)拿了全班第一,真的很感谢这位老师!没有他我做不出来这么好的项目,也就是这个时候我产生了对编程做游戏的浓厚兴趣~~~~~~~


我想摆脱对EasyX的依赖(因为这个好像(听说的)过时了而且我走迷宫里面已经用得差不多了,想试试学点新的东西)

于是,我又百度了一下:

鼠标消息参考:写的很详细,但没有解决我的问题
https://blog.csdn.net/iteye_11539/article/details/82302654?utm_medium=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase

我又继续搜,emmm··········

再来······
https://blog.csdn.net/qq_31567335/article/details/79674152

代码搬过去了,有点看不懂,于是我搜了一下里面的函数:
https://blog.csdn.net/baidu_38494049/article/details/82930099
这篇真的很赞

3.使用了继承和多态,掉落物类FallThings为父类,三个子类Coin、Boom、Gift分别代表具体的掉落物,各自代表的符号、接到加分扣血情况、名称各不相同

但是,结果————

踩了个大雷:在使用迭代器时创建的是FallThings父类迭代器。但是不能把子类对象往里加


https://www.zhihu.com/question/22585094

下面有人说这位答主的回答有问题,但我觉得是指他示例代码没对(此处略),而不是说我这里截取的理论不对

(此踩雷代码现已加入KFC 上传资源包豪华套餐)早知道我不耍这个小聪明搞什么继承多态了,就普普通通一个FallingThings类多好··········果然还是学艺不精啊

4.构造函数加了默认值,即使在调用构造函数的时候,没有提供实参值,不仅不会出错,而且还确保按照默认的参数值对对象进行初始化

5.新增按ESC退出功能,判断输入字符ASCII码是不是等于27即可

插播一个软件问题,如果遇到Dev-C++调试时提示“项目没有调试信息,您想打开项目调试选项并重新生成吗?”这种报错,可以参考如下解决方案:
https://blog.csdn.net/qq_41112170/article/details/102985428


以下是经过多次修改之后基本健全的代码:

#include<iostream>
#include<time.h>
#include<conio.h>
#include<windows.h>
#include<vector>
HANDLE handle= GetStdHandle(STD_OUTPUT_HANDLE);
COORD crd;
using namespace std;
const short HEALTH = 5;//满血总血量为5
const short STD_WIDTH = 60;//记录宽度,便于输出一整排的边框等
const short GROUND = 27;//地面水平线的纵坐标void gotoxy(int x, int y)//光标移到(x,y)位置处(封装了SetConsoleCursorPosition(handle, crd)函数,减少代码重复)
{crd.X = x;crd.Y = y;SetConsoleCursorPosition(handle, crd);
}
void ShowBound()//绘制边框
{//绘制竖边框for (int i = 0; i < GROUND; i++){gotoxy(STD_WIDTH, i);printf("|\n");}//绘制横边框for (int j = 0; j < STD_WIDTH; j++){gotoxy(j, GROUND);printf("-");}
}class Vitality//血量
{private:int vitality;
public:Vitality() { vitality = HEALTH; }//每次把血量恢复到满血void ShowVitality(){gotoxy(STD_WIDTH * 0.6, 0);SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);//设为黄色字体,高亮cout << "HP: ";//hit points血量for (int i = 0; i < HEALTH; i++){cout<<(i < vitality ? "■" : "□");//如果小于实际血量vitality,输出实心方框,反之输出空心方框}cout<<"Vitality: "<<vitality;SetConsoleTextAttribute(handle, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);//恢复黑底白字}short GetVitality()//获取当前血量{return vitality;}short GetThing(short n = 0)//如果没有传入参数,默认每次不加不减血量{vitality += n;//接到炸弹-1,接到血包+0-3点血 if(vitality>5){vitality=5;}ShowVitality();//更新扣血之后的血量return vitality;}short GetRestore(){vitality = HEALTH;ShowVitality();//更新满血复活之后的血量return vitality;}
};
Vitality vitality;//控制血量的全局变量class FallThings//所有掉落物的基类
{protected://不能写成private,否则子类无法修改int x;int y;
public:bool active;string Name;int grade;int num;//随机生成掉落物用char letter;//符号默认是金币FallThings(int x=rand() % STD_WIDTH - 1,int y=1)//rand() % STD_WIDTH - 1生成0-STD_WIDTH-2的随机数,因为最上面要输出血槽,所以要y=1{this->x = x;this->y = y; active=true; grade = 1;num = rand() % 7;//0-4金币,5炸弹,6礼物,这样金币概率最大if(num==5){Name = "Boom";letter = '@';//炸弹 grade=-1; }else if(num==6)//血包 {Name = "Vitality";letter = '+';grade=rand()%4;}else//金币 {Name = "Coin";letter = 'o';grade=1;}}int GetX()//返回x坐标{return x;}int GetY()//返回y坐标{return y;}// 提供接口框架的纯虚函数void  Fall()//实现下落,沿着垂直方向往下掉就行{y++;}
};class Plate//盘子类,用来接掉落物
{private:int x;int y ;//默认位置是屏幕正中间,离地面一格远int ridus;//半径int left;//左边界int right;//右边界
public:Plate(int x=STD_WIDTH / 2,int y=GROUND - 1,int r=5){this->x = x;this->y = y;this->ridus = r;left = x - ridus;//左边界right = x + ridus;//右边界ChangeBound();//确定左右边界}int GetX()//返回x坐标{return x;}int GetY()//返回y坐标{return y;}int GetLeft() { return left; }int GetRight() { return right; }void ChangeBound()//在圆心坐标改变的时候改变左右边界{left = x - ridus;//左边界right = x + ridus;//右边界}void  MovePlate(char input)//根据键盘输入移动盘子(一次一格,好控制) {if (input == 'a'&& GetLeft()  > 0)//接收到a,并且不抵在左边界,盘子左移{x--;ChangeBound();//更改盘子边界}else if (input == 'd'&& GetRight()< STD_WIDTH)//接收到d,并且不抵在右边界,盘子右移{x++;ChangeBound();//更改盘子边界}}void MovePlate(int distance)//重载成员函数,通过鼠标移动距离来移动盘子(一次一段,不好控制) {distance %= STD_WIDTH;//防止距离超出范围,控制在游戏界面内if (GetLeft() > distance  && GetRight() + distance < STD_WIDTH)//在可移动范围内,最左边要至少能移动一个distance的距离,右边同理 {x += distance;ChangeBound();//更改盘子边界}else//避免x卡在边界处无法移动{if (x <= 0){x = 1+ridus;//起点加半径ChangeBound();}else{x = STD_WIDTH - 1-ridus;ChangeBound();}}}void ShowPlate(){if(GetLeft()>0&&GetY()<STD_WIDTH)//只在规定范围内画盘子 (不然因为鼠标的移动可能盘子会出现在奇奇怪怪的地方哦) {gotoxy(GetLeft(), GetY());//注意横坐标从plate左边界开始,纵坐标是plate的y坐标for (int i = GetLeft(); i != GetRight(); i++)//整个输出的范围是plate从左边界到右边界{printf("*");}}}void ClearPlate()//清除盘子 {gotoxy(GetLeft(), GetY());//原来是盘子的地方输出空格for (int i = GetLeft(); i != GetRight(); i++){printf(" ");}}void RestorePlate(){x = STD_WIDTH / 2;//恢复盘子默认位置y = GROUND - 1;}
};Plate plate;//全局变量盘子
class GameCtroller//游戏管理类,控制游戏运作
{public:vector<FallThings>  fallthings;//用于管理掉落物的动态数组int score;//本轮得分GameCtroller() { score = 0; }short delay;//设置休眠时间void GameRestore()//重复游戏时用,重置游戏初始数据{fallthings.clear();//清空动态数组vitality.GetRestore();//恢复血量score = 0;//清空分数}void GenerateFallingThings()//每隔一段时间调用,自动生成掉落物{gotoxy(rand()%STD_WIDTH ,1);//从最顶端随机列(0-STD_WIDTH-1)生成for (int i = 0; i < rand() % 5; i++)//每次随机生成0-5个掉落物{fallthings.push_back(FallThings());//生成一个匿名的FallThings对象加入vector里}}short Fall(){vector < FallThings>::iterator itr;//迭代器,遍历掉落物数组fallthingsfor (itr = fallthings.begin(); itr != fallthings.end(); itr++){if (itr->active == true)//活跃状态为true才判断执不执行以下代码{gotoxy(itr->GetX(), itr->GetY());//光标移动到当前位置,实施擦除cout << " ";//当前元素被接到了或者掉到了地平线以下,需要消除if (itr->GetY() == plate.GetY() - 1)//y高度=盘子y高度-1,表示被接到了{if (itr->num>=0&&itr->num<=4)//接到金币{score+=itr->grade;//接到一个金币,得分+1}else if (itr->num==5)//接到炸弹{vitality.GetThing(itr->grade);//把炸弹对应的扣血量(-1)作为参数传入Vitality,实现接到炸弹血量-1}else if (itr->num==6)//接到血包{vitality.GetThing(itr->grade);//把礼物对应的加血量(+0-3)作为参数传入Vitality,实现接到血包血量+1}if (vitality.GetVitality() <= 0)//本轮扣血加分处理完了,如果发现没血了,就输出游戏结束{crd.X = 13; crd.Y = GROUND + 2;//GROUND+1是输出的边框,所以提示语可以GROUND+2输出SetConsoleCursorPosition(handle, crd);//SetConsoleTextAttribute(handle, BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED);//白色背景 不注释的话最后输出GameOver就会黑底白字和白底黑子鱼龙混杂,不知道为什么会这样 cout << "Game Over!" << endl;SetConsoleTextAttribute(handle, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);//恢复默认黑色背景return -1;}itr->active = false;//活跃设置为false,不再掉落vitality.ShowVitality();//展示血量ShowScore();//展示分数}else if (itr->GetY() >= GROUND)//当前元素落到地平线以下都没有被接到,直接消除但不扣血(这也是为什么不能和被接到的情况合并处理的原因){itr->active = false;//活跃设置为false,不再掉落}else//当前元素还在下落阶段,就让其继续下落{itr->Fall();//纵坐标继续加1gotoxy(itr->GetX(), itr->GetY());//光标移到该元素的新位置(x横坐标不变,y纵坐标+1)cout << itr->letter;//输出该迭代器对应符号,金币--o,炸弹--@,血包—+//itr++;//因为erase会让迭代器自动移到下一个,这里没有erase,就要手动移动到下一个元素}}}return 0;//正常下落返回0}void SetDelay(int d){delay = d;}void Wait(){Sleep(delay);}void ShowScore(){gotoxy(1, GROUND + 2);cout << " Score: " << score << "   ";//多输出几位空格来,防止上一次得分位数较多没有被完全遮挡}
};int main()
{GameCtroller gamectroller;//实例化游戏控制类char choice = 'n';char input;gamectroller.SetDelay(300);srand((unsigned)time(NULL));//产生随机种子,程序时间作为参数传入do{system("cls");//清屏plate.RestorePlate();//盘子恢复默认位置ShowBound();//绘制边框gamectroller.GameRestore();//重置游戏 vitality.GetRestore();//恢复满血vitality.ShowVitality();//展示血量POINT last_p;//每次开始游戏都重新定义,保证从初始位置开始捕捉鼠标POINT p;do{gamectroller.ShowScore();//输出得分plate.ShowPlate();//更新盘子位置GetCursorPos(&last_p);//获取鼠标坐标last_p.y = GROUND - 1;//防止光标随鼠标移动,影响盘子的位置Sleep(50); //休息0.05s间隔必须短,因为鼠标移动很快,间隔长了会很卡GetCursorPos(&p);//再次获取鼠标坐标p.y = GROUND - 1;if (p.x != last_p.x)//现在x与之前不一样,说明在0.1s内移动了鼠标,就处理移动挡板{//鼠标在范围内移动才行plate.ClearPlate();//清除原来的挡板plate.MovePlate(int(p.x-last_p.x));//根据按键移动挡板,强制转换为int,不然会报错“对重载调用不明确”plate.ShowPlate();//展示类中包含的挡板移动之后}if (_kbhit())//检测到键盘输入,可能挡板移动(ad) {input = _getch();if (input == 'a' || input == 'd')//根据输入的是a或者d来决定挡板的移动{plate.ClearPlate();//清除原来的挡板plate.MovePlate(input);//根据按键移动挡板plate.ShowPlate();//展示类中包含的挡板移动之后}if (input == 27){break;//按ESC退出}}gamectroller.GenerateFallingThings();if (gamectroller.Fall() == -1){break;//-1表示游戏结束,跳出内层循环询问玩家是否开始下一局}gamectroller.Wait();//每300毫秒自动生成下一轮字母} while (true);cout << "      Try Again?  (y or n)";cin >> choice;} while (choice == 'y' || choice == 'Y');return 0;
}

运行效果:
看,鼠标移动盘子的效果还是有缺陷,有时候鼠标移动速度过快,盘子就找不到了,而且盘子的效果也远没有市面上游戏那么灵活,我觉得是我没有用专门的控件(比如EasyX?)导致的。尝试推卸责任ing


费了老大力气,终于把这一节的课后题写完了,一个弹跳的小球居然能联系到接金币、快速敲字母游戏,神奇的相似相通。
看看下一期的主角是什么:

感谢您能看到这里,一起成为更好的自己~~

C与C++游戏项目练习9:接金币游戏简易版相关推荐

  1. 【源码+图片素材+详细教程】Java游戏开发_Java开发经典游戏飞翔的小鸟_飞扬的小鸟_Java游戏项目Flappy Bird像素鸟游戏_Java课程设计项目

    课程目标: 1.通过本课程的学习巩固Java的相关基础知识,例如循环判断,数组和集合的使用,对象的继承,接口的实现,窗口的创建,事件监听,图形绘制. 2.完成小鸟的移动,管道自动生成.碰撞死亡,计分系 ...

  2. C与C++游戏项目练习1:弹跳的小球简易版

    C与C++游戏项目练习1:弹跳的小球简易版 最近在研究知乎上看见的游戏专栏,被童晶老师的<C语言课程设计与游戏开发实践教程>种草了,我本来就喜欢并且专业方向也选的游戏,于是就买了一本,有空 ...

  3. 【教程+源码】Java开发经典游戏飞翔的小鸟_Java游戏项目Flappy Bird像素鸟游戏

    在飞翔的小鸟游戏中,玩家只需通过点击方向键操纵让小鸟避开绿色管道等障碍物即可继续前进.如果小鸟碰到管道,游戏立即结束. 那么如何用Java编写一款属于自己的飞翔的小鸟游戏呢?其过程并不复杂,本课程详细 ...

  4. java一个猿人部落生存游戏,猿人进化模拟器无限金币钻石无敌版

    猿人进化模拟器无限金币钻石无敌版是一款去原始森林探险壮大猿人部落的模拟养成游戏,高品质的画质,玩家将化身为猿,自由的带领的自己的部落进行战斗冒险,收集大量的资源,抵御敌人的进攻,学习更多的生存技巧,在 ...

  5. 【源码+图片素材】Java开发经典游戏飞翔的小鸟_Java游戏项目Flappy Bird像素鸟游戏_Java小游戏_Java初级项目_Java课程设计项目

    开发环境: jdk1.8 开发工具: IDEA JavaEE基础: 变量.数据类型.判断语句.循环结构.数组.集合.简单窗口创建.图形图片绘制.双缓存技术.事件-键盘事件.物体的碰撞检测.File [ ...

  6. 翻金币游戏-大体介绍和资源文件,主场景,选择关卡场景设置

    现在要开始实战做一个翻金币游戏,是这样的环境 一共二十关,金币一面金色,一面银色.翻一个会使周围四个一起翻转,目标是所有金币都是金色. 首先需要添加这些图片资源,方法不多说,主要是资源包括八张金币不同 ...

  7. 一个简单的pygame接金币游戏

    左右键控制小人移动去接空中下来的金币,接住金币得5分,接不住游戏结束,金币速度会随着level的关数 而越来越快import pygame,sys,os,random pygame.init()cla ...

  8. python编程制作接金币游戏_pygame学习笔记(6):完成一个简单的游戏

    学了这么长时间的Pygame,一直想写个游戏实战一下.看起来很简单的游戏,写其来怎么这么难.最初想写个俄罗斯方块,想了很长时间如何实现,想来想去,也没写出来,于是干脆下载别人的代码来读.后来,要想写一 ...

  9. python编程制作接金币游戏,闪电侠接金币的FlashMan类

    python the Flash man catch coin gif animation 闪电侠是美剧,这里是一个小游戏,操作闪电侠接不断冒出来的金币.本模块定义了FlashMan类.这个模块能单独 ...

最新文章

  1. JAVA-数据库之JDBC连接MySQL数据库
  2. java mapper control_java spring boot中怎么编写mapper?怎么编写service和controller?
  3. es6去除重复项_Excel的去除重复项你真的明白原理吗?浅尝去除重复项的函数奥秘...
  4. 解决windows指纹登录不灵问题
  5. Spring Boot 系列(七)Swagger2-生成RESTful接口文档
  6. MyEclipse could not redeploy(不能重新部署)的解决办法
  7. 互联网晚报 | 10月27日 星期三 | 高德车道级导航正式发布;阿里淘菜菜发布“本地菜”计划;特斯拉市值破万亿美元...
  8. 云服务器 搭建虚拟主机,云服务器 搭建虚拟主机
  9. 生成drl文件_drools原生drl规则文件的使用
  10. Visul C++中CList用法
  11. Topcoder口胡记 SRM 562 Div 1 ~ SRM 599 Div 1
  12. ASP.NET Core 3.x 学习笔记(7)——Blazor
  13. 九宫格图案解锁、支付宝解锁、微信钱包解锁
  14. CF外挂界令人发指的垃圾外挂!让你接触不为人知的内幕!
  15. 一个普通的计算机专业专科生,毕业后想拿高薪,大学三年具体应该怎么做?
  16. CSDN高手速成宝典(高手:zjcxc)
  17. CString::CompareNoCase与CString::Compare的区别 .
  18. android自动调用按钮事件,Android Button自动触发点击事件
  19. React 类组件和函数组件
  20. 写出你所知道的测试工具,并写出他们的用途和优缺点

热门文章

  1. classical CNN models : ZF Net 模型结构详解
  2. python3爬取有道云翻译
  3. iOS Swift 5中的键盘处理
  4. 温湿度传感器DHT11介绍
  5. 您的服务器虚拟内存太低,您的服务器虚拟内存太低
  6. Lenovo-ThinkCentre-M730s 电脑 Hackintosh 黑苹果efi引导文件
  7. Unity prefab
  8. hive sql 时间比较、小数点为位
  9. 人车目标检测、交通标志识别…云测数据喊你参加第九届CCF大数据与计算智能大赛啦
  10. 采购谈判技巧:报价谈判,怎么谈你会赢?