在进行这个教程的学习前,强烈建议大家先尝试下教程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 利用函数对飞机游戏进行重构相关推荐

  1. 教孩子学编程 python语言版_教孩子学编程 PYTHON语言版 PDF_IT教程网

    资源名称:教孩子学编程 PYTHON语言版 PDF 资源目录: 第1章Python基础--认识环境1 1.1认识Python3 1.2用Python编写程序5 1.3运行Python程序5 1.4本章 ...

  2. python教孩子学编程,教孩子学编程(Python语言版)

    教孩子学编程(Python语言版) 第1 章 Python 基础:认识环境 #NiceHexSpiral.py #运行后,挺奇特,整个作画过程,完全呈现. import turtle colors=[ ...

  3. 但凡原神有二十树脂,我也不会来学编程(c语言)

    但凡原神有二十树脂,我也不会来学编程(c语言)<第一天> 编程小白一只,从今天开始,将在本专栏记录本人学习编程的过程与心得~来自甘肃,普通双非,由于地域影响,想要在计算机方面得到好的出路, ...

  4. 用C语言用指针怎么算通用定积分,C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数,...

    问题描述: C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数, //我运行的结果和答案对不上,请帮我看看程序对吗? //实现积分 #include #include i ...

  5. 编写指数函数 c语言,C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数,...

    问题描述: C语言:利用函数指针编写一个用矩形法求定积分的通用函数,包括正弦,余弦和指数函数, //我运行的结果和答案对不上,请帮我看看程序对吗? //实现积分 #include #include i ...

  6. 计算机可以编程游戏吗,我的世界真正的大神,在游戏中编程,制作出能玩的单机游戏...

    原标题:我的世界真正的大神,在游戏中编程,制作出能玩的单机游戏 都知道,我的世界是一款沙盒建造类游戏,从2009年发行至今,有着不错的热度和源源不断的玩家涌入.虽然游戏中所呈现的并不是华丽精致高清的特 ...

  7. 少儿编程python线上课程-少儿编程课堂|python – 用游戏学编程

    学习编程是很快乐的事情.当我们自己开发出一套时下流行的游戏时,这满满的成就感比玩儿游戏本身高出了不知道会有多少倍. 接下来一段时间我们就python从0开始学习怎么开发 flappy brid 游戏. ...

  8. python编程少儿游戏编程_少儿编程课堂|python – 用游戏学编程

    学习编程是很快乐的事情.当我们自己开发出一套时下流行的游戏时,这满满的成就感比玩儿游戏本身高出了不知道会有多少倍. 接下来一段时间我们就python从0开始学习怎么开发 flappy brid 游戏. ...

  9. 优的python少儿编程课_少儿编程课堂|python – 用游戏学编程

    学习编程是很快乐的事情.当我们自己开发出一套时下流行的游戏时,这满满的成就感比玩儿游戏本身高出了不知道会有多少倍. 接下来一段时间我们就python从0开始学习怎么开发 flappy brid 游戏. ...

最新文章

  1. 当文员学计算机二级,二本学生毕业后在干什么?多半做3种工作,过来人深有同感...
  2. 1.4操作系统的变革
  3. 占用内存的Enum.values()方法
  4. finalize作用
  5. Linux和qt电子横幅,【Qt】 自定义组件_滚动横幅弹窗对话框字体图标等
  6. sql时间转换时分秒_SQL时分秒之间相互转换
  7. 《精通Spring4.X企业应用开发实战》读后感第六章(国际化)
  8. yagmail群发邮件
  9. 此更新不适用于您的计算机 kb4012212,这才是正确的全球勒索软件处置方法(含Windows补丁)...
  10. SpreadJS 14.1 Crack by Xacker
  11. js压缩文件或文件夹
  12. 计算机 去掉快捷方式箭头,去掉桌面快捷方式小箭头方法(无需修改注册表)
  13. b站视频-尚硅谷jQuery教程张晓飞老师-笔记(二)
  14. 利用Python自动生成及发送Excel报表
  15. 西安三日游-梦回长安
  16. python 蒙特卡罗法求π值
  17. Nacos -- 集群部署
  18. 高中计算机听课总结,中学新信息技术老师听课心得体会五篇
  19. 动手学深度学习番外篇 01.为什么from d2l import torch as d2l
  20. 如何设置计算机的网络参数,如何为计算机新手简单地设置路由器的基本参数

热门文章

  1. Youki的笔记本重装系统的方法~
  2. Github上删除文件后与本地同步
  3. 【亲测有效】装了双系统后在ubuntu下耳机没有声音的解决方法
  4. 常用类 (四) ----- Random随机数类
  5. asp.net中Roles和User的异常处理机制的思考
  6. 敏捷开发般若敏捷系列之九:敏捷开发与本能反应
  7. springcloud -zuul(2-执行流程及源码)
  8. Python并发编程系列之多进程(multiprocessing)
  9. 你真的搞懂ES6模块的导入导出规则了吗
  10. Java学习:抽象类与接口