目录

简要介绍

游戏基本框架&主循环

如何在控制台实现图形界面

实现游戏不断的更新

消除闪屏

最后对一遍代码~

简要介绍

本文没有繁难的代码,所以很适合想做游戏但不知道如何做游戏的同学食用~

游戏基本框架&主循环

要想做一个游戏,首先要弄清楚游戏的基本框架:

其中,

Model代表游戏模型,存储着游戏的所有信息;

Update每时每刻都在根据Model的自身状态来对Model进行更新;

Handle是根据外界输入来调整Model的信息;

Show是对Model的信息进行翻译,并呈现在玩家眼前;

根据以上的基本理念,我们可以这样写:

int main()
{//先处理用户的输入,并更改Model的数据handle();//在根据Model自身的数据来对Model进行更新update();//最后将Model的内容提取并展示出来show();handle();update();show();handle();update();show();......
}

当然,肯定不能就一直这样重复写,为此,我们需要构造一个main loop来不断读取用户输入,更新Model信息,并将信息翻译展示到玩家面前。

为了实现每秒循环一定次数的效果,我们需要#include <time.h>来构造主循环,具体如下:

#include <time.h>
#define FPS 4int main()
{clock_t t1=0,t2=0;while (1) {t2 = clock();if (t2-t1 > CLOCKS_PER_SEC*1.0/FPS) {t1 = t2;handle();update();show();}}
}

其中,除了三个基本函数handle,update,show外都是main loop的内容,这样便可以实现按一定的帧率(FPS)来进行游戏循环。

但是,一款游戏至少要做到随时响应玩家的输入,而现在的代码每隔1.0/FPS的时间才会响应一次玩家输入,因此我们需要多加这样几行代码:

#include <conio.h>char key;int main()
{clock_t t1=0,t2=0;while (1) {t2 = clock();if (_kbhit()) key = _getch();if (t2-t1 > CLOCKS_PER_SEC*1.0/FPS) {t1 = t2;handle();update();show();}}
}

通过声明一个全局变量char key,同时又用if (_kbhit()) key = _getch()来每时每刻响应用户输入,如有输入便将用户的按键值传给key,这样handle所处理的内容便只是key的值。

OK,现在基本的东西已经搞好了,下面就可以随意发挥了!

如何在控制台实现图形界面

显然,我们并不打算去制作一款3D游戏(而且俺也不会啊(≧﹏ ≦)),而制作一款2D平面游戏的话,我们只需要一张画布即可。

首先,声明一块长宽固定的二维数组,将其当作画布(screen),然后构造一种结构类型作为演员(Actor),这样,游戏的Model就只需要存储每一位演员的样子和位置,再在需要show的时候把这些演员给渲染到screen上,最后把screen展示给玩家看即可。

#define WIDTH 100
#define HEIGHT 40typedef struct _actor{int width;    //演员的宽度int height;    //演员的高度char** image;    //指向演员的图形int x;  //最左侧的位置int y;  //最顶部的位置
} Actor;char screen[HEIGHT][WIDTH+1];Actor boy; //创建一个名为boy的演员int main()
{......
}

现在,我们有一块画布宽为WIDTH+1,高为HEIGHT,之所以宽要加1,是想让每一行的最后一个字符存储的是'\n',这样在show的时候,我们可以先赋值screen[HEIGHT-1][WIDTH] = '\0',再直接把整块画布以'%s'的格式printf出来(是不是很棒的一个小技巧~)。

但是,相信大家已经注意到了,screen,key,boy这些全局变量在声明之后都还没有进行初始化,所以我们有必要统一地在一个函数里面进行全局变量的初始化。

#include <string.h>//由于用到了strlen函数void init(void)
{for (int i = 0; i<HEIGHT; i++) screen[i][WIDTH] = '\n';key = '\0';static char* image_boy[] = //加static是为了保证离开init函数后,boy的图像依旧会被保护{"  ~@~  "," /BOY\\ ","~ ### ~"," _/ \\_ "};boy = (Actor){.x=2,.y=4,.width=strlen(image_boy[0]),.height=4,.image = image_boy};
}int main()
{init();//main loop ...
{

最后运行的效果如下:(当然,现在show()还没有写好,所以你还看不见~( ̄▽ ̄)~*)

然后,为了能尽快看到效果,我们先把show()给写出来。(ヾ(•ω•`)o)

show()应该包含两个部分:render()以及draw()。render即渲染,把演员给誊到画布上,draw即显示,把画布给玩家显示出来。

void show(void);void render(void);void clear(void);void load(Actor* actor);void draw(void);void show(void)
{render();//渲染system("cls");//清空控制台内容draw();//显示
}void render(void)
{clear();//先把画布弄干净load(&boy);//把boy誊上画布
}void clear(void)
{for (int i = 0; i< HEIGHT; i++) {for (int j = 0; j < WIDTH; j ++) {screen[i][j] = ' ';//用' '填充画布}}
}void load(Actor* actor)
//传入actor的指针,而非actor本身,传输速度更快
{for (int i = 0; i<actor->height; i++) {for (int j = 0; j<actor->width; j++) {if (actor->image[i][j] != ' ') { //if条件的添加,使得boy的图片像是png图像一样,有透明像素screen[actor->y+i][actor->x+j] = actor->image[i][j];}}}
}void draw(void)
{screen[HEIGHT-1][WIDTH] = '\0';printf("%s\n",screen);
}

好了!现在你也能看到小boy了,是不是非常奈斯~

最后我们对一下代码,看看是不是一样的:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <conio.h>#define FPS 4
#define WIDTH 100
#define HEIGHT 40typedef struct _actor{int width;int height;char** image;int x;int y;
} Actor;char screen[HEIGHT][WIDTH+1];
char key;
Actor boy;void init(void);void handle(void);void update(void);void show(void);void render(void);void clear(void);void load(Actor* actor);void draw(void);int main()
{init();clock_t t1=0,t2=0;while (1) {t2 = clock();if (_kbhit()) key = _getch();if (t2-t1 > CLOCKS_PER_SEC*1.0/FPS) {t1 = t2;handle();update();show();}}
}void init(void)
{for (int i = 0; i<HEIGHT; i++) screen[i][WIDTH] = '\n';key = '\0';static char* image_boy[] ={"  ~@~  "," /BOY\\ ","~ ### ~"," _/ \\_ "};boy = (Actor){.x=2,.y=4,.width=strlen(image_boy[0]),.height=4,.image = image_boy};
}void handle(void)
{
}void update(void)
{
}void show(void)
{render();system("cls");draw();
}void render(void)
{clear();load(&boy);
}void clear(void)
{for (int i = 0; i< HEIGHT; i++) {for (int j = 0; j < WIDTH; j ++) {screen[i][j] = ' ';}}
}void load(Actor* actor)
{for (int i = 0; i<actor->height; i++) {for (int j = 0; j<actor->width; j++) {if (actor->image[i][j] != ' ') {screen[actor->y+i][actor->x+j] = actor->image[i][j];}}}
}void draw(void)
{screen[HEIGHT-1][WIDTH] = '\0';printf("%s\n",screen);
}

实现游戏不断的更新

现在,我们只剩下handle和update没有写了,之所以最后写,是因为这两个函数是十分灵活的,完全取决于你的游戏内容。

那我就写一个我想的故事吧(。・ω・。)

先介绍一下所有参演角色:

static char* image_boy[] ={"  ~@~  "," /BOY\\ ","~ ### ~"," _/ \\_ "};
boy = (Actor){.x=4,.y=22,.width=strlen(image_boy[0]),.height=4,.image = image_boy};
static char* image_bed[] ={"|                             |","|                             |","+#############################+","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","+#############################+","|                             |","|                             |"};
bed = (Actor){.x=50,.y=10,.width=strlen(image_bed[0]),.height=14,.image=image_bed};
static char* image_guard[] ={"!    @    T","*~~GUARD~~+","   \\###/  |","   /###\\  |","   L   L  |"};
guard = (Actor){.x=65,.y=16,.width=strlen(image_guard[0]),.height=5,.image = image_guard};
static char* image_ground[] ={"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"};
ground_1 = (Actor){.x=0,.y=30,.width=strlen(image_ground[0]),.height=1,.image=image_ground};
ground_2 = (Actor){.x=12,.y=23,.width=strlen(image_ground[0]),.height=1,.image=image_ground};

效果:

对了,还要注意一件事情,就是我们定义的结构Actor还是比较简陋的,因为角色可能有很多状态,比如说,我可以搞一个叫做Monster的结构,在Actor的基础上加入血量,等级,防御力,状态等等内容,然后可以在初始化的时候声明出Monster许多的形态,比如受伤,愉悦等等。

然后就要写handle和update了,,,

好累,,写一整天了教程了,能不能赏一个赞再看(′д` )…卑微新人还没有被赞过呢...感谢~~

✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨

void handle(void)
{switch (key) {case 'w':if (boy.y > 20) {boy.y --;} break;case 'a':if (boy.x > 0) {boy.x --;} break;case 's':if (boy.y < 26) {boy.y ++;} break;case 'd':if (boy.x < 90) {boy.x ++;} break;}key = '\0';
}void update(void)
{if (boy.x >= guard.x-boy.width && boy.x <= guard.x+guard.width) {//boy进入guard的攻击范围guard.y += 2;//冲撞if (boy.y <= guard.y+guard.height) {//boy被撞到了static char* image_boy[] ={"   ~www"," (>_<) "," ~###~ ","  / \\  "};boy.image = image_boy;//更换表情boy.x -= 10;//被撞飞}}
}

当然啦,这个update写的很简单,所以游戏也很简单,如果可以的话,建议你也写一个自己的故事,然后自己设计一下角色图片,比如之前我做的第一个图形小游戏: (这个小游戏剧情也有一百多行呢~)

消除闪屏

之所以会闪屏,原因就在于show()里面的system("cls")会导致整个界面会清空,所以我们可以不采用清空的做法,而是采用覆盖式显示,每次draw之前将光标移到控制台的最开始的位置,然后什么都不管,直接覆盖掉原有的图像。

所以很简单,只用把原来的system("cls")改成移动光标到开始处即可,别忘了#include <windows.h>

system("cls");

————>>>

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { 0,0 };
SetConsoleCursorPosition(hOut, pos);

最后对一遍代码~

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <conio.h>
#include <windows.h>#define FPS 4
#define WIDTH 100
#define HEIGHT 40typedef struct _actor{int width;int height;char** image;int x;int y;
} Actor;char screen[HEIGHT][WIDTH+1];
char key;
Actor boy;
Actor bed;
Actor guard;
Actor ground_1;
Actor ground_2;void init(void);void handle(void);void update(void);void show(void);void render(void);void clear(void);void load(Actor* actor);void draw(void);int main()
{init();clock_t t1=0,t2=0;while (1) {t2 = clock();if (_kbhit()) key = _getch();if (t2-t1 > CLOCKS_PER_SEC*1.0/FPS) {t1 = t2;handle();update();show();}}
}void init(void)
{for (int i = 0; i<HEIGHT; i++) screen[i][WIDTH] = '\n';key = '\0';static char* image_boy[] ={"  ~@~  "," /BOY\\ ","~ ### ~"," _/ \\_ "};boy = (Actor){.x=4,.y=22,.width=strlen(image_boy[0]),.height=4,.image = image_boy};static char* image_bed[] ={"|                             |","|                             |","+#############################+","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","|  |__|                       |","+#############################+","|                             |","|                             |"};bed = (Actor){.x=50,.y=10,.width=strlen(image_bed[0]),.height=14,.image=image_bed};static char* image_guard[] ={"!    @    T","*~~GUARD~~+","   \\###/  |","   /###\\  |","   L   L  |"};guard = (Actor){.x=65,.y=16,.width=strlen(image_guard[0]),.height=5,.image = image_guard};static char* image_ground[] ={"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"};ground_1 = (Actor){.x=0,.y=30,.width=strlen(image_ground[0]),.height=1,.image=image_ground};ground_2 = (Actor){.x=12,.y=23,.width=strlen(image_ground[0]),.height=1,.image=image_ground};
}void handle(void)
{switch (key) {case 'w':if (boy.y > 20) {boy.y --;} break;case 'a':if (boy.x > 0) {boy.x --;} break;case 's':if (boy.y < 26) {boy.y ++;} break;case 'd':if (boy.x < 90) {boy.x ++;} break;}key = '\0';
}void update(void)
{if (boy.x >= guard.x-boy.width && boy.x <= guard.x+guard.width) {guard.y += 2;if (boy.y <= guard.y+guard.height) {static char* image_boy[] ={"   ~www"," (>_<) "," ~###~ ","  / \\  "};boy.image = image_boy;boy.x -= 10;}}
}void show(void)
{render();HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = { 0,0 };SetConsoleCursorPosition(hOut, pos);draw();
}void render(void)
{clear();load(&ground_1);load(&ground_2);load(&bed);load(&guard);load(&boy);
}void clear(void)
{for (int i = 0; i< HEIGHT; i++) {for (int j = 0; j < WIDTH; j ++) {screen[i][j] = ' ';}}
}void load(Actor* actor)
{for (int i = 0; i<actor->height; i++) {for (int j = 0; j<actor->width; j++) {if (actor->image[i][j] != ' ') {screen[actor->y+i][actor->x+j] = actor->image[i][j];}}}
}void draw(void)
{screen[HEIGHT-1][WIDTH] = '\0';printf("%s\n",screen);
}

C语言 一步步教你做一个带有图形界面的冒险小游戏相关推荐

  1. 做一个支持图形界面的操作系统(上)

    分类: OS2006-05-01 20:00 856人阅读 评论(0) 收藏 举报 原文:http://www.binghua.com/Article/Class6/Class7/200409/267 ...

  2. 做一个支持图形界面的操作系统(zz)

    原文:http://www.binghua.com/Article/Class6/Class7/200409/267.html (转载及引用请注明明原作者及出处) (pdf: http://www.b ...

  3. 使用Vue做一个可自动拼图的拼图小游戏(二)

    背景以及最终效果 在上一篇文章中我们已经写完了一个可以正常玩的拼图小游戏,但是这还没有结束,我们还要接着试一下让拼图游戏可以自己完成拼图. 在线体验 本部分是这篇文章的第二部分,主要讲的是实现自动拼图 ...

  4. 利用python做一个小游戏_如何使用python做一个简单的猜数字的小游戏

    1 首先小编先打开IDLE,如下图: 2 然后这里点击菜单栏的'File',然后点击菜单"New File",如下图: 3 然后我们就在idle中新建了一个python文件,如下图 ...

  5. 整活~使用webAI做一个网页AR吃豆人小游戏

    一个好习惯,先给结论 使用网页端深度学习框架识别人脸,做一个AR吃豆人小游戏.吃豆人会随着人脸在镜头内的移动而移动,吃完全部豆子即为获胜. 在线体验地址:点我预览 代码地址:点我github 本文首发 ...

  6. canvas+websocket+vue做一个完整的你画我猜小游戏

    第一次在掘金发文章,瑟瑟发抖. 这个主要是为了学习使用一下canvas和websocket,项目地址.求star- 你画我猜大家应该都玩过,一个人画,其他人猜.目前实现了最基本的功能,慢慢修改. 项目 ...

  7. cocos creator做一个儿童数字答题的微信小游戏(1)

    接了个外包要做个数字答题的微信小游戏 给小学生玩的 准备大概15天完成吧,每天抽点一两个小时做一下 今天第一天先做主界面,先用cocos creator拼一个主界面出来 下面的每个按钮都是一个butt ...

  8. 如何用Cocos Creator做一个胶体(果冻效果)小游戏(四)+测试收尾

    一.游戏测试 测试环节是检测游戏,发现游戏缺陷的一个必不可少的环节.通过进行游戏测试能够不断发现游戏存在的问题以及可能出现的问题,寻找有效可行的解决方案,不断改进,提升游戏的质量,让游戏趋于完善. 一 ...

  9. 使用Vue做一个可自动拼图的拼图小游戏(一)

    背景以及最终效果 随着几个项目的提测,也闲下来了,恰好玩了一把拼图游戏,于是突发奇想打算自己写一个试试. 在线体验 实现的功能有: 普通拼图的功能 自动拼图 本部分是这篇文章的第一部分,主要讲的是实现 ...

  10. 如何用Cocos Creator做一个胶体(果冻效果)小游戏(三)+JavaScript

    游戏实现部分 一.游戏关卡实现 这里就只放两个关卡(其余大部分重复性,还不好看:( ),关卡设计图的草纸找不到了,就不放出来了. 初始情况下,玩家控制的胶体方块角色出现在屏幕左下角的盒子中,上方的白字 ...

最新文章

  1. mormot支持websocket
  2. 浅谈模型压缩之量化、剪枝、权重共享
  3. linux如何get请求urlencode,如何为curl命令urlencode数据?
  4. 【Python】Python语言学习:pip工具使用知识,模型保存pickle,PDF与docx相互转换处理...
  5. 【C语言】控制台窗口图形界面编程(七):鼠标事件
  6. 用树莓派和PC机搭建多节点私人以太坊网络
  7. 使用Speedment 3.0.17及更高版本简化了事务
  8. ffmpeg 0.6.3 代码, 经过我努力,能够在vs 2005 下单步调试代码
  9. (MYSQL) Unknown table 'a' in MULTI DELETE的解决办法
  10. js高级学习笔记(b站尚硅谷)-5-回调函数
  11. java 获取 jsp 内容_JAVA记录-JSP内容
  12. PDF在线阅读开发经验(FlexPaper+SWFTools+SaveAsPDFandXPS)【转】
  13. 小程序和H5 之间的通信
  14. [原创]分布光纤测温DTS产品在市场上两种主流产品架构的区别与对比-未来趋势必然向单板化方向发展
  15. 如何获得一个RAC Oracle数据库(从Github - oracle/docker-images) - 本地版
  16. 音频处理——音频编码原理简介
  17. FPGA项目一:1位闪烁灯设计
  18. c语言写抢QQ红包软件,Android实现QQ抢红包插件
  19. 通过httpclient发送请求的几种方式,发送文件、参数、json对象
  20. exchange2016邮件服务器配置,Exchange Server 2016客户端访问配置

热门文章

  1. 最低成本DIY视频遥控车方案:ESP32-CAM视频遥控车
  2. js-beautify 不换行
  3. word2003流程图变成图片_Microsoft Office 2003(word2003)制作流程图的操作方法介绍
  4. windows系统文件名太长导致无法打开删除移动
  5. Windows 10 文件名太长 无法删除 复制 重命名
  6. 迅捷pdf转换器完美解决如何将pdf转换成word问题
  7. APP崩溃的主要原因
  8. 泰拉瑞亚试图加载不正确的_泰拉瑞亚Switch中文版将在12月19日发售|宝可梦 剑/盾大量细节公布 自动存档可关经验平均分配等...
  9. python requests模块(爬虫)
  10. SV806 QT UI开发