项目来源:老九君
项目名称:精忠报国之笑傲江湖
视频课地址:网易云课堂
基本功能/架构:
第二天
今天看得比较快,基本把这个项目结束了吧,明天再仔细看一下有没有遗漏的这项目就应该算是结束了.项目难度的话一般,主要是windows的函数调用以前不知道的话刚看可能会蒙头,同样需要源码的同学私聊或者评论我私发给你,因为这毕竟不是我写的,接下来后天开始应该会开始一个tinyhttp的github项目模仿,欢迎继续关注简易目录
1.COORD windows.h头文件
2.conio.h getch()函数
3. game.c showMap()函数
4.怪物计数与打印
5.随机攻击力 stblib.h
1.COORD windows.h头文件
纵观项目,从player(玩家),monster(怪物),martial(门派)这几个关键结构体的定义和功能地图的切换的到怪物与地图的匹配(哪些怪物在哪些地图)还有地图描述信息的显示,全都使用了COORD结构,有关代码在后面
一开始看到这个地方特别疑惑,因为我在所有文件里都Ctrl F查询了,都没有找到COORD这样一个结构体的声明,最后才发现这是一个windows api里封装好的结构体,可以直接用,只有int x和int y两个成员,作用是表示一个字符在控制台屏幕上的坐标,其定义如下:
typedef struct _COORD {SHORT X; // horizontal coordinate 水平坐标SHORT Y; // vertical coordinate 竖直坐标} COORD;
简单来说,COORD就像是给我们的控制台以左上角为(0,0)点建造了一个坐标系(只不过这个坐标系和平面直角坐标系的第四象限有点相似,但是又不同,Y也是向正方向增大的),例如我们在定义地图时某个地图坐标为(4,3),在后面要显示着地图相关信息的时候只要看怪物所在地图坐标横纵坐标是否与(4 , 3)一致即可.
注意:在项目中COORD并没有用来在屏幕上显示,更多的像是两个参数进行匹配,这不是c语言的标准库,不用太在意,会用就好
结构体的定义
/* 门派 */typedef struct _martial{int id; //门派编号- - -COORD hqCoord; //总部坐标(X-行,Y-列),与正常坐标轴相反- - -}Martial;/* 玩家结构 */typedef struct _player{int id;- - -COORD currCoord;//玩家当前坐标(X-行,Y-列),与正常坐标轴相反Bag bag;}Player;/* 怪物 */typedef struct _monster{int id;- - -COORD mCoord; //怪物坐在地图坐标(X-行,Y-列),与正常坐标轴相反}Monster;
功能的实现
/* 显示怪物:怪物与地图的匹配 */void ShowMonster(){- - - //遍历怪物数组,找到所有跟玩家坐标相同的怪物int monsterCount = sizeof(monsters)/ sizeof(Monster);int monsterIndex[9];//最多9个怪物int currCount = 0;//当前地图的怪物总数for(i = 0; i < monsterCount; i++){if(X == monsters[i].mCoord.X && Y == monsters[i].mCoord.Y && monsters[i].state != 0)//判断怪物所在地图XY坐标与当前地图XY坐标是否相等{//显示怪物- - - }}}/* 地图描述信息的显示 */void ShowMap(){Clear(MARGIN_X, 3);//地图的起始位置固定为MARGIN_X, 3int i = 0, j = 0;char * martialIntro = NULL;//门派介绍for(; i < 8; i++){SetPosition(MARGIN_X, 3 + i);printf("| ");for(j = 0; j < 8; j++){if(i == X && j == Y){SetColor(13, 15);}else{SetColor(10, 0);}printf("%-9s", Maps[i][j]);SetColor(10, 0);}printf(" |\n");}/* 地图的切换 */while(1){- - -printf("请输入选择或四方向移动:");key = GetKey();//等待用户输入if(key == 38){//上X--;}else if(key == 39){//右Y++;}else if(key == 40){//下X++;}else if(key == 37){//左Y--;}- - -//总共只有8行8列 防止越界if(X > 7) X = 0;if(X < 0) X = 7;if(Y > 7) Y = 0;if(Y < 0) Y = 7;ShowMap();}SetColor(15, 0);//InitializeGame();//ShowPlayer();return 0;
2.conio.h getch()函数
getch()函数在项目中,所有功能都是由键盘上的数字键和上下左右方向键来进行选择和控制的,我们固然可以使用getchar()然后转int用ANCII进行判断,但是所有正常的函数都是回显函数,即我们输入的内容在控制台是会打出来的,所以这时需要使用到conio.h中getch()函数getch()是编程中所用的函数,这个函数是一个不回显函数,当用户按下某个字符时,函数自动读取,无需按回车,有的C语言命令行程序会用到此函数做游戏,但是这个函数并非标准函数,要注意移植性!while(1){//死循环等待输入7退出游戏- - -key = GetKey();//等待用户输入if{}- - -}
3. game.c showMap()函数
在项目中有这样一个功能:将所在地图的文字突出显示,如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBvNXVGm-1584329343725)(:/a1ff842a16c746e4b22126b325ba33a5)]
这个功能也是靠windows.h中封装的函数SetConsoleTextAttribute(handle ,int)实现,能控制前景色和后景色,可见在这个项目里使用了大量的windows api,问题是找了半天还没找到官方文档(官网的我就没查到…就查到一个winbase,但winbase只是windows的一部分啊),太难了!
/* 控制颜色 */void SetColor(int ForeColor, int BackGroundColor){HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄SetConsoleTextAttribute(handle, ForeColor + BackGroundColor * 0x10);//设置颜色}/* 打印地图并突出当前地图 */void ShowMap(){Clear(MARGIN_X, 3);//地图的起始位置固定为MARGIN_X, 3int i = 0, j = 0;char * martialIntro = NULL;//门派介绍for(; i < 8; i++){SetPosition(MARGIN_X, 3 + i);//SetPosition是一个控制光标的函数,主要是靠windows.h中封装的函数实现printf("| ");for(j = 0; j < 8; j++){if(i == X && j == Y){SetColor(13, 15);//SetColor是一个改变输出颜色(能控制前景色和后景色)的函数,主要也是靠windows.h中封装的函数实现}else{SetColor(10, 0);}printf("%-9s", Maps[i][j]);SetColor(10, 0);}printf(" |\n");}- - -//地图信息打印}
4.怪物计数与打印
在项目里面,筛选怪物以及打印怪物的方式很值得学习– 1.首先使用for循环遍历所有monsters数组里的monster结构体,判断坐标是否相等,相等则currCount++并将这个monster的索引存入一个int 数组:monsterIndex[currCount] = i 就是这句 很精髓
– 2.然后在每个for循环要结束之前判断一次当前计数器currCount(符合条件的monster)是否==9,等于则退出for循环
– 3.凑足9个怪物后,再使用一个for循环将刚才存怪物索引的int 数组遍历出来,并用取出来的索引去从monsters数组里取出信息打印出来下面是实现,虽然说实话有点花里胡哨和多余,但是思想值得借鉴
//遍历怪物数组,找到所有跟玩家坐标相同的怪物int monsterCount = sizeof(monsters)/ sizeof(Monster);//值得学习的数组长度计算方式-> 总长度/单位长度int monsterIndex[9];//最多9个怪物int currCount = 0;//当前地图的怪物总数for(int i = 0; i < monsterCount; i++){if(X == monsters[i].mCoord.X && Y == monsters[i].mCoord.Y && monsters[i].state != 0)//判断坐标相等{monsterIndex[currCount] = i;//将符合条件的怪物索引放进一个新的数组里currCount++;if(currCount == 9) break;//新的数组里数量达到9时达到页面最多显示的怪物数,退出当前循环}}if(currCount == 0){- - -}//判断无怪物则打印无怪物提示信息//打印怪物信息for (int i = 0; i < currCount; i++){printf("%d.%s(%s)\t\t", i + 1, monsters[i].name, levelNames[monsters[i].level - 1]);if(i % 3 == 0)//换行以及重新定位光标{SetPosition(MARGIN_X, 14 + i / 3);}}
5.随机攻击力 stblib.h
在项目中武器和防具的攻击力和防御力都是在最大值和最小值之间摇摆不定的,因此在每一轮战斗时需要获取中间的一个随机数作为当前攻击力和防御力,结构体定义如下:/* 游戏道具 - 武器、防具、消耗品、卡片 */typedef struct _prop{int id; //道具编号- - -union{//联合 - 多选1int minAttack; //如果是武器,就对应攻击力int minDefence;//如果是防具,就对应防御力int minPower; //如果是血瓶等消耗品,就对应增加的能量值};union{//联合 - 多选1int maxAttack; //如果是武器,就对应攻击力int maxDefence;//如果是防具,就对应防御力int maxPower; //如果是血瓶等消耗品,就对应增加的能量值};char desc[200]; //道具的功能描述}Prop;
产生随机数我们需要使用C语言标准库<stdlib.h>中的rand(void)函数,返回一个0到常量RAND_MAX (不同实现中值可能不同,但至少32767)的整数值假如我们想产生一个0到49的随机数,就该这么写
rand() % 50//对50取模(余数)rand() % (a +1) //a是一个整数,对a+1取模,a+1是长度
假如我们想产生一个19到25的随机数,就该这么写
rand() % 7 + 19//对50取模(余数)rand() % (a - b +1) + b //产生一个a与b之间的整数,b<a ,a - b +1是长度,b是偏移量
所以项目中要产生一个最大最小值之间的攻击力或者防御力也是特别简单了
int playerAttack = rand() % (currPlayer->weapon.maxAttack - currPlayer->weapon.minAttack + 1) + currPlayer->weapon.minAttack;
C语言项目-精忠报国-第二天-COORD windows.h头文件/conio.h getch()函数/game.c showMap()函数 文字突出/怪物计数与打印/随机攻击力 stblib.h