做游戏,学编程(C语言) 3 利用函数对飞机游戏进行重构
在进行这个教程的学习前,强烈建议大家先尝试下教程2中的思考题,大概要一百多行代码,是对C语言基本语法、逻辑能力的一个很好的锻炼。
没有学习函数,以上功能都在main中实现,是有点痛苦的。学了函数后,这个教程我们会模块化重构飞机游戏,大家经历过上面的痛苦后才能真正理解函数的好处。如果只是被动地学习语法知识,做些简单的算法题,是很难体会到函数封装的重要性的。要写了足够复杂的代码,才有较深的体会。
还有,随着程序越来越复杂,程序调试的能力也越来越重要。大家都要学会断点单步、变量跟踪、分块调试等常用方法。这方面的教程很多,大家可以网上搜索下。
另外,在实现飞机游戏时,可能会遇到子弹运动时没法控制、键盘控制比较卡、不按键时敌人不会自动移动等问题。为了简化大家的开发难度,这里给一个简化的游戏框架:
// 函数外全局变量定义
void main()
{startup(); // 数据初始化
while(1) // 游戏循环执行
{show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
}
大家的相应功能,都需要放到startup()、show()、updateWithoutInput()、updateWithInput()这四个函数中实现,主函数尽量保持以上的形式,不要修改。
第一步,利用函数和上面的框架对空战游戏进行重构,实现控制飞机的移动。另外,输出部分也改进了,通过直接二重循环,输出所有的空格回车等内容,可以进行更复杂的输出。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// 全局变量
int position_x,position_y; // 飞机位置
int high,width; // 游戏画面尺寸
void startup() // 数据初始化
{high = 20;width = 30;position_x = high/2;position_y = width/2;
}void show() // 显示画面
{system("cls"); // 清屏
int i,j;for (i=0;i<high;i++){for (j=0;j<width;j++){if ((i==position_x) && (j==position_y))printf("*"); // 输出飞机*
elseprintf(" "); // 输出空格
}printf("\n");}
} void updateWithoutInput() // 与用户输入无关的更新
{
}void updateWithInput() // 与用户输入有关的更新
{char input;if(kbhit()) // 判断是否有输入
{input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a') position_y--; // 位置左移
if (input == 'd')position_y++; // 位置右移
if (input == 'w')position_x--; // 位置上移
if (input == 's')position_x++; // 位置下移
}
}void main()
{startup(); // 数据初始化
while (1) // 游戏循环执行
{show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
}
第二步,实现一个正规子弹,子弹发射后自动向上移动。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// 全局变量
int position_x,position_y; // 飞机位置
int bullet_x,bullet_y; // 子弹位置
int high,width; // 游戏画面尺寸
void startup() // 数据初始化
{high = 20;width = 30;position_x = high/2;position_y = width/2;bullet_x = 0;bullet_y = position_y;
}void show() // 显示画面
{system("cls"); // 清屏
int i,j;for (i=0;i<high;i++){for (j=0;j<width;j++){if ((i==position_x) && (j==position_y))printf("*"); // 输出飞机*
else if ((i==bullet_x) && (j==bullet_y))printf("|"); // 输出子弹|
elseprintf(" "); // 输出空格
}printf("\n");}
} void updateWithoutInput() // 与用户输入无关的更新
{ if (bullet_x>-1)bullet_x--;
}void updateWithInput() // 与用户输入有关的更新
{char input;if(kbhit()) // 判断是否有输入
{input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a') position_y--; // 位置左移
if (input == 'd')position_y++; // 位置右移
if (input == 'w')position_x--; // 位置上移
if (input == 's')position_x++; // 位置下移
if (input == ' ') // 发射子弹
{bullet_x = position_x-1; // 发射子弹的初始位置在飞机的正上方
bullet_y = position_y;}}
}void main()
{startup(); // 数据初始化
while (1) // 游戏循环执行
{show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
}
第三步,增加静止的敌机@
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// 全局变量
int position_x,position_y; // 飞机位置
int bullet_x,bullet_y; // 子弹位置
int enemy_x,enemy_y; // 敌机位置
int high,width; // 游戏画面尺寸
void startup() // 数据初始化
{high = 20;width = 30;position_x = high/2;position_y = width/2;bullet_x = -1;bullet_y = position_y;enemy_x = 0;enemy_y = position_y;
}void show() // 显示画面
{system("cls"); // 清屏
int i,j;for (i=0;i<high;i++){for (j=0;j<width;j++){if ((i==position_x) && (j==position_y))printf("*"); // 输出飞机*
else if ((i==enemy_x) && (j==enemy_y))printf("@"); // 输出敌机@
else if ((i==bullet_x) && (j==bullet_y))printf("|"); // 输出子弹|
elseprintf(" "); // 输出空格
}printf("\n");}
} void updateWithoutInput() // 与用户输入无关的更新
{ if (bullet_x>-1)bullet_x--;
}void updateWithInput() // 与用户输入有关的更新
{char input;if(kbhit()) // 判断是否有输入
{input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a') position_y--; // 位置左移
if (input == 'd')position_y++; // 位置右移
if (input == 'w')position_x--; // 位置上移
if (input == 's')position_x++; // 位置下移
if (input == ' ') // 发射子弹
{bullet_x = position_x-1; // 发射子弹的初始位置在飞机的正上方
bullet_y = position_y;}}
}void main()
{startup(); // 数据初始化
while (1) // 游戏循环执行
{show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
}
第四步,让敌机自动向下移动。
注意:为了控制敌机移动的速度,代码中用了一个小技巧,大家可以体会下(可以用局部静态变量,也可以用全局变量来实现)。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// 全局变量
int position_x,position_y; // 飞机位置
int bullet_x,bullet_y; // 子弹位置
int enemy_x,enemy_y; // 敌机位置
int high,width; // 游戏画面尺寸
void startup() // 数据初始化
{high = 20;width = 30;position_x = high/2;position_y = width/2;bullet_x = -1;bullet_y = position_y;enemy_x = 0;enemy_y = position_y;
}void show() // 显示画面
{system("cls"); // 清屏
int i,j;for (i=0;i<high;i++){for (j=0;j<width;j++){if ((i==position_x) && (j==position_y))printf("*"); // 输出飞机*
else if ((i==enemy_x) && (j==enemy_y))printf("@"); // 输出敌机@
else if ((i==bullet_x) && (j==bullet_y))printf("|"); // 输出子弹|
elseprintf(" "); // 输出空格
}printf("\n");}
} void updateWithoutInput() // 与用户输入无关的更新
{ if (bullet_x>-1)bullet_x--; // 用来控制敌机向下移动的速度。每隔几次循环,才移动一次敌机
// 这样修改的话,用户按键交互速度还是保持很快,但我们NPC的移动显示可以降速
static int speed = 0; if (speed<10)speed++;if (speed == 10){enemy_x++; speed = 0;}
}void updateWithInput() // 与用户输入有关的更新
{char input;if(kbhit()) // 判断是否有输入
{input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a') position_y--; // 位置左移
if (input == 'd')position_y++; // 位置右移
if (input == 'w')position_x--; // 位置上移
if (input == 's')position_x++; // 位置下移
if (input == ' ') // 发射子弹
{bullet_x = position_x-1; // 发射子弹的初始位置在飞机的正上方
bullet_y = position_y;}}
}void main()
{startup(); // 数据初始化
while (1) // 游戏循环执行
{show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
}
第五步,增加判断,当子弹和敌机的位置一样时,就是击中敌机。增加变量记录游戏得分。敌机被击中后,会先消失,再重新随机位置出现。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
// 全局变量
int position_x,position_y; // 飞机位置
int bullet_x,bullet_y; // 子弹位置
int enemy_x,enemy_y; // 敌机位置
int high,width; // 游戏画面尺寸
int score; // 得分
void startup() // 数据初始化
{high = 20;width = 30;position_x = high/2;position_y = width/2;bullet_x = -2;bullet_y = position_y;enemy_x = 0;enemy_y = position_y;score = 0;
}void show() // 显示画面
{system("cls"); // 清屏
int i,j;for (i=0;i<high;i++){for (j=0;j<width;j++){if ((i==position_x) && (j==position_y))printf("*"); // 输出飞机*
else if ((i==enemy_x) && (j==enemy_y))printf("@"); // 输出敌机@
else if ((i==bullet_x) && (j==bullet_y))printf("|"); // 输出子弹|
elseprintf(" "); // 输出空格
}printf("\n");}printf("得分:%d\n",score);
} void updateWithoutInput() // 与用户输入无关的更新
{ if (bullet_x>-1)bullet_x--; if ((bullet_x==enemy_x) && (bullet_y==enemy_y)) // 子弹击中敌机
{score++; // 分数加1
enemy_x = -1; // 产生新的飞机
enemy_y = rand()%width;bullet_x = -2; // 子弹无效
}if (enemy_x>high) // 敌机跑出显示屏幕
{enemy_x = -1; // 产生新的飞机
enemy_y = rand()%width;}// 用来控制敌机向下移动的速度。每隔几次循环,才移动一次敌机
// 这样修改的话,用户按键交互速度还是保持很快,但我们NPC的移动显示可以降速
static int speed = 0; if (speed<10)speed++;if (speed == 10){enemy_x++; speed = 0;}
}void updateWithInput() // 与用户输入有关的更新
{char input;if(kbhit()) // 判断是否有输入
{input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a') position_y--; // 位置左移
if (input == 'd')position_y++; // 位置右移
if (input == 'w')position_x--; // 位置上移
if (input == 's')position_x++; // 位置下移
if (input == ' ') // 发射子弹
{bullet_x = position_x-1; // 发射子弹的初始位置在飞机的正上方
bullet_y = position_y;}}
}void main()
{startup(); // 数据初始化
while (1) // 游戏循环执行
{show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
}
有同学给老师反映,这个游戏闪屏,眼睛都快闪瞎了。第六步,利用void gotoxy(int x,int y)函数,实现类似于清屏函数的效果,即光标移动到原点位置进行重画。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
// 全局变量
int position_x,position_y; // 飞机位置
int bullet_x,bullet_y; // 子弹位置
int enemy_x,enemy_y; // 敌机位置
int high,width; // 游戏画面尺寸
int score; // 得分
void gotoxy(int x,int y)//类似于清屏函数,光标移动到原点位置进行重画
{HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos;pos.X = x;pos.Y = y;SetConsoleCursorPosition(handle,pos);
}void startup() // 数据初始化
{high = 20;width = 30;position_x = high/2;position_y = width/2;bullet_x = -2;bullet_y = position_y;enemy_x = 0;enemy_y = position_y;score = 0;
}void show() // 显示画面
{gotoxy(0,0); // 光标移动到原点位置进行重画清屏
int i,j;for (i=0;i<high;i++){for (j=0;j<width;j++){if ((i==position_x) && (j==position_y))printf("*"); // 输出飞机*
else if ((i==enemy_x) && (j==enemy_y))printf("@"); // 输出敌机@
else if ((i==bullet_x) && (j==bullet_y))printf("|"); // 输出子弹|
elseprintf(" "); // 输出空格
}printf("\n");}printf("得分:%d\n",score);
} void updateWithoutInput() // 与用户输入无关的更新
{ if (bullet_x>-1)bullet_x--; if ((bullet_x==enemy_x) && (bullet_y==enemy_y)) // 子弹击中敌机
{score++; // 分数加1
enemy_x = -1; // 产生新的飞机
enemy_y = rand()%width;bullet_x = -2; // 子弹无效
}if (enemy_x>high) // 敌机跑出显示屏幕
{enemy_x = -1; // 产生新的飞机
enemy_y = rand()%width;}// 用来控制敌机向下移动的速度。每隔几次循环,才移动一次敌机
// 这样修改的话,用户按键交互速度还是保持很快,但我们NPC的移动显示可以降速
static int speed = 0; if (speed<20)speed++;if (speed == 20){enemy_x++; speed = 0;}
}void updateWithInput() // 与用户输入有关的更新
{char input;if(kbhit()) // 判断是否有输入
{input = getch(); // 根据用户的不同输入来移动,不必输入回车
if (input == 'a') position_y--; // 位置左移
if (input == 'd')position_y++; // 位置右移
if (input == 'w')position_x--; // 位置上移
if (input == 's')position_x++; // 位置下移
if (input == ' ') // 发射子弹
{bullet_x = position_x-1; // 发射子弹的初始位置在飞机的正上方
bullet_y = position_y;}}
}void main()
{startup(); // 数据初始化
while (1) // 游戏循环执行
{show(); // 显示画面
updateWithoutInput(); // 与用户输入无关的更新
updateWithInput(); // 与用户输入有关的更新
}
}
有同学提到光标闪的问题,有一个隐藏光标显示函数HideCursor,大家可以尝试下。另外,win32的cmd窗口是可以修改窗口大小、字体大小、背景颜色等,大家可以尝试下,让游戏效果更好一些。
这样初步的空战游戏就比较完美了 :)
到思考题的时间了,大家可以海阔天空地去改进这个飞机游戏,比如显示复杂的飞机图形、多台敌机、飞机被撞后重新出现、更好玩的子弹、飞机血量、升级、登录界面、游戏暂停等等......
另外,大家可以利用这个教程中学到的知识,把教程1中的反弹球也重构下,并可以实现反弹球消方块的游戏,效果如下:
下一个教程,我们做一个flappy bird的小游戏:知乎专栏
备注:这个系列的教程主要面向初学者,基础较好的同学就不用看了。另外,我在实际教学中,也会讲经典的算法编程的。开发游戏不会取代算法编程练习,主要是用来培养兴趣,实现大代码量的编程。最后,老师有点懒,大家有问题可以直接在评论中进行交流。。。
做游戏,学编程(C语言) 3 利用函数对飞机游戏进行重构相关推荐
- 教孩子学编程 python语言版_教孩子学编程 PYTHON语言版 PDF_IT教程网
资源名称:教孩子学编程 PYTHON语言版 PDF 资源目录: 第1章Python基础--认识环境1 1.1认识Python3 1.2用Python编写程序5 1.3运行Python程序5 1.4本章 ...
- python教孩子学编程,教孩子学编程(Python语言版)
教孩子学编程(Python语言版) 第1 章 Python 基础:认识环境 #NiceHexSpiral.py #运行后,挺奇特,整个作画过程,完全呈现. import turtle colors=[ ...
- 但凡原神有二十树脂,我也不会来学编程(c语言)
但凡原神有二十树脂,我也不会来学编程(c语言)<第一天> 编程小白一只,从今天开始,将在本专栏记录本人学习编程的过程与心得~来自甘肃,普通双非,由于地域影响,想要在计算机方面得到好的出路, ...
- 用C语言用指针怎么算通用定积分,C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数,...
问题描述: C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数, //我运行的结果和答案对不上,请帮我看看程序对吗? //实现积分 #include #include i ...
- 编写指数函数 c语言,C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数,...
问题描述: C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数, //我运行的结果和答案对不上,请帮我看看程序对吗? //实现积分 #include #include i ...
- 计算机可以编程游戏吗,我的世界真正的大神,在游戏中编程,制作出能玩的单机游戏...
原标题:我的世界真正的大神,在游戏中编程,制作出能玩的单机游戏 都知道,我的世界是一款沙盒建造类游戏,从2009年发行至今,有着不错的热度和源源不断的玩家涌入.虽然游戏中所呈现的并不是华丽精致高清的特 ...
- 少儿编程python线上课程-少儿编程课堂|python – 用游戏学编程
学习编程是很快乐的事情.当我们自己开发出一套时下流行的游戏时,这满满的成就感比玩儿游戏本身高出了不知道会有多少倍. 接下来一段时间我们就python从0开始学习怎么开发 flappy brid 游戏. ...
- python编程少儿游戏编程_少儿编程课堂|python – 用游戏学编程
学习编程是很快乐的事情.当我们自己开发出一套时下流行的游戏时,这满满的成就感比玩儿游戏本身高出了不知道会有多少倍. 接下来一段时间我们就python从0开始学习怎么开发 flappy brid 游戏. ...
- 优的python少儿编程课_少儿编程课堂|python – 用游戏学编程
学习编程是很快乐的事情.当我们自己开发出一套时下流行的游戏时,这满满的成就感比玩儿游戏本身高出了不知道会有多少倍. 接下来一段时间我们就python从0开始学习怎么开发 flappy brid 游戏. ...
最新文章
- 当文员学计算机二级,二本学生毕业后在干什么?多半做3种工作,过来人深有同感...
- 1.4操作系统的变革
- 占用内存的Enum.values()方法
- finalize作用
- Linux和qt电子横幅,【Qt】 自定义组件_滚动横幅弹窗对话框字体图标等
- sql时间转换时分秒_SQL时分秒之间相互转换
- 《精通Spring4.X企业应用开发实战》读后感第六章(国际化)
- yagmail群发邮件
- 此更新不适用于您的计算机 kb4012212,这才是正确的全球勒索软件处置方法(含Windows补丁)...
- SpreadJS 14.1 Crack by Xacker
- js压缩文件或文件夹
- 计算机 去掉快捷方式箭头,去掉桌面快捷方式小箭头方法(无需修改注册表)
- b站视频-尚硅谷jQuery教程张晓飞老师-笔记(二)
- 利用Python自动生成及发送Excel报表
- 西安三日游-梦回长安
- python 蒙特卡罗法求π值
- Nacos -- 集群部署
- 高中计算机听课总结,中学新信息技术老师听课心得体会五篇
- 动手学深度学习番外篇 01.为什么from d2l import torch as d2l
- 如何设置计算机的网络参数,如何为计算机新手简单地设置路由器的基本参数