之前的贪吃蛇都是在cmd下实现,每次都要调用cls刷新屏幕,简直是闪瞎了我的狗眼。

  度娘得知有一种方法可以避免闪烁,即:双缓冲。原理是先在内存中作图,然后将做好的图复制到前台,同时禁止背景刷新。

  主要使用函数:

    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

    int WINAPI WinMain(HINSTANCE, HINSTANCE,PSTR, int);

  涉及主要方法:

WNDCLASS wndclass;
wndclass.style=CS_HREDRAW|CS_VREDRAW;//位置改变时重绘
wndclass.lpfnWndProc=(WNDPROC)WndProc;//消息处理函数
wndclass.hInstance=;//当前实例句柄
wndclass.hbrBackground=(HBRUSH)COLOR_WINDOWFRAME;//背景色
wndclass.lpszClassName=szWindowClass;//参窗口类名
wndclass.hIcon=;//图标
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);//光标
wndclass.lpszMenuName=;//菜单名称
wndclass.hIconSm=;//最小化图标
RegisterClassEx(&wndclass);//注册窗口类

  CreateWindow:

HWND CreateWindow(
LPCTSTR lpClassName, // 如果lpClassName是一个字符串,它指定了窗口的类名
LPCTSTR lpWindowName, // 指向一个指定窗口名的空结束的字符串指针,可使用lpWindowName来指定控制文本
DWORD dwStyle, // 指定创建窗口的风格, WS_CAPTION:创建一个有标题框的窗口
// WS_SYSMENU:创建一个在标题条上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格
int x, // 指定窗口的初始水平位置
// 如果该参数被设为CW_USEDEFAULT则系统为窗口选择缺省的左上角坐标并忽略Y参数。
// CW_USEDEFAULT只对层叠窗口有效,如果为弹出式窗口或子窗口设定,则X和y参数被设为零
int y, //
int nWidth, // 以设备单元指明窗口的宽度
// 若nWidth是CW_USEDEFAULT,则系统为窗口选择一个缺省的高度和宽度:
// 缺省宽度为从初始X坐标开始到屏幕的右边界,缺省高度为从初始Y坐标开始到目标区域的顶部。
// CW_USEDEFAULT只对层叠窗口有效;如果为弹出式窗口和子窗口设定CW_USEDEFAULT标志则nWidth和nHeight被设为零。
int nHeight, //
HWND hWndParent, // 指向被创建窗口的父窗口或所有者窗口的句柄
HMENU hMenu, // 菜单句柄,或依据窗口风格指明一个子窗口标识
HANDLE hlnstance, // 与窗口相关联的模块实例的句柄
LPVOID lpParam // 指向一个值的指针,该值传递给窗口WM_CREATE消息
)
// 返回值:如果函数成功,返回值为新窗口的句柄:如果函数失败,返回值为NULL。

  将字符贪吃蛇迁移过来,gcc 编译时需加 -mwindows 参数,如:

  gcc snake.c -mwindows

  代码如下:  

#include <windows.h>
#define ID_TIMER 1
#define WIDTH 12 // 宽
#define HEIGHT 8 // 高
#define DEBUG 0
const char FENCE ='*'; // 栅栏
const char HEAD ='@'; // 蛇头
const char BODY ='#'; // 蛇身
const char FOOD ='O'; // 食物
const char BLANK =' '; // 空白
char arr[HEIGHT][WIDTH];
struct Snake{
int y,x;
}snake[WIDTH*HEIGHT]; // 结构体,保存坐标点
int len=,food=,key=,score=,alive=,direct=,speed=,full=(WIDTH-)*(HEIGHT-); // 长度、食物、按键、总分、存活、方向、速度
int uldr[][]={{},{-,},{,-},{,},{,}}; // 上、左、下、右 void init(){ // 初始化游戏地图
int y=,x=,start_pos=HEIGHT/;
for(y=;y<HEIGHT;y++)
for(x=;x<WIDTH;x++)
if( y == || y == HEIGHT- || x == || x == WIDTH- ){
arr[y][x] = FENCE;
}
else{
arr[y][x] = BLANK;
}
snake[].y=start_pos;
snake[].x=;
len++;
snake[].y=start_pos;
snake[].x=;
len++;
arr[ snake[].y ][ snake[].x ] = HEAD ;
arr[ snake[].y ][ snake[].x ] = BODY ;
} void make_food(){
int rx,ry;
while(!food){
rx = rand()%WIDTH;
ry = rand()%HEIGHT;
if( arr[ry][rx] == BLANK ){
arr[ry][rx]=FOOD;
food=;
break;
}
}
} void move(){
int cnt=;
len++; // 准备将当前位置放在snake数组首部
if(DEBUG)printf("len:%d\n",len);
for(cnt=len-;cnt>;cnt--){
snake[cnt].x=snake[cnt-].x;
snake[cnt].y=snake[cnt-].y; // 1234 变为 51234
}
snake[].y+=uldr[direct][];
snake[].x+=uldr[direct][]; // 移动蛇头
} void check_head(){
int y=snake[].y;
int x=snake[].x;
int i=;
int cnt=;
if(y < || y > HEIGHT- || x < || x > WIDTH-){ // 是否越界
alive=;
}
if( arr[y][x] == BODY ){ // 是否吃到自己
alive=;
}
if( arr[y][x] == BLANK){
arr[y][x] = HEAD;
}
if( arr[y][x] == FOOD ){ // 吃到食物
arr[y][x] = HEAD;
score++;
len++;
food=;
snake[len-].y=snake[len-].y; // 蛇尾增加一节蛇身
snake[len-].x=snake[len-].x;
make_food();
}
if(DEBUG)printf("len:%d\n",len);
if(DEBUG){
for(;i<len;i++){
printf("y,x:(%d,%d)\n",snake[i].y,snake[i].x);
}
}
arr[ snake[len-].y ][ snake[len-].x ]=BLANK; // 先清除蛇尾显示
len--;
for(cnt=;cnt<=len-;cnt++){
arr[ snake[cnt].y ][ snake[cnt].x ]=BODY; // 蛇身显示
}
} void handle(){
init();
make_food();
while(alive && len<full){
move();
check_head();
speed=(speed > )?:(score/+);
if(alive){
Sleep(-speed*); // 多久刷新一次
}
}
if( len == full){
printf("congratulations!\n");
}
else{
printf("you lose\n");
}
} LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("snake") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ; if(!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ;
return ;
} hwnd = CreateWindow (szAppName, TEXT("snake"),
WS_CAPTION | WS_SYSMENU , //| WS_THICKFRAME,
, ,
, ,
NULL, NULL, hInstance,
NULL) ; ShowWindow (hwnd, SW_SHOW) ; //显示
UpdateWindow (hwnd) ;
//ShowCursor(FALSE); //隐藏鼠标光标 while (GetMessage (&msg, NULL, , ))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
ShowCursor(TRUE); //显示鼠标光标
return msg.wParam ;
} LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
static HDC hdcMem;
HFONT hFont;
static HBITMAP hBitmap;
static int cxScreen, cyScreen; //屏幕的宽度 高度.
static int iFontWidth=, iFontHeight=; //字体的宽度 高度 switch (message)
{
case WM_CREATE:
cxScreen = GetSystemMetrics(SM_CXSCREEN) ; //屏幕宽度
cyScreen = GetSystemMetrics(SM_CYSCREEN) ; hdc = GetDC(hwnd);
hdcMem = CreateCompatibleDC(hdc);
hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
SelectObject(hdcMem, hBitmap);
ReleaseDC(hwnd, hdc);
//创建字体
hFont = CreateFont(iFontHeight, iFontWidth-, , , FW_NORMAL, , , ,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DRAFT_QUALITY, FIXED_PITCH | FF_SWISS, TEXT("Fixedsys"));
SelectObject(hdcMem, hFont);
DeleteObject (hFont) ;
SetBkMode(hdcMem, TRANSPARENT); //设置背景模式为 透明
init();
make_food(); case WM_TIMER:
hdc = GetDC(hwnd);
PatBlt (hdcMem, , , cxScreen, cyScreen, BLACKNESS) ; //将内存设备映像刷成黑色
int y,x,size_s,size_p,size_l,size_w;
TCHAR sText[],pText[],lText[],wText[]; move();
check_head();
speed=(speed > )?:(score/+);
SetTimer (hwnd, ID_TIMER, (-speed*), NULL) ;
size_s = wsprintf( sText,TEXT("your score: %d"),score); // 计算字符长度
size_p = wsprintf( pText,TEXT("current speed: %d"),speed);
size_l = wsprintf( lText,TEXT("you lose"));
size_w = wsprintf( wText,TEXT("you win"));
SetTextColor(hdcMem, RGB(, , )); // 字体颜色 TextOut(hdcMem, , , sText, size_s); // hdc、x坐标、y坐标、字符地址、字符长度
TextOut(hdcMem, , , pText, size_p);
if(alive && len<full){
for(y= ; y<HEIGHT ; y++ ){
for(x= ; x<WIDTH ; x++ ){
TextOut(hdcMem, x*, (y+)*, &arr[y][x], );
}
}
}
else{
for(y= ; y<HEIGHT ; y++ )
for(x= ; x<WIDTH ; x++ )
if( y== || y==HEIGHT- || x== || x==WIDTH-){
TextOut(hdcMem, x*, (y+)*, &arr[y][x], );
}
if(alive == ){
TextOut(hdcMem, , (+HEIGHT/)*, lText, size_l);
}else{
TextOut(hdcMem, , (+HEIGHT/)*, wText, size_w);
}
BitBlt(hdc, , , cxScreen, cyScreen, hdcMem, , , SRCCOPY);
KillTimer (hwnd, ID_TIMER) ;
}
BitBlt(hdc, , , cxScreen, cyScreen, hdcMem, , , SRCCOPY);
ReleaseDC(hwnd, hdc);
return ; case WM_RBUTTONDOWN:
KillTimer (hwnd, ID_TIMER) ;
return ; case WM_RBUTTONUP:
SetTimer (hwnd, ID_TIMER, , NULL) ;
return ; // 按下 w a s d 时
case WM_CHAR:
switch (wParam){
case 'w' : direct=(direct == )?:;break;
case 'a' : direct=(direct == )?:;break;
case 's' : direct=(direct == )?:;break;
case 'd' : direct=(direct == )?:;break;
default : direct;
}
return ;
//处理善后工作
case WM_DESTROY:
KillTimer (hwnd, ID_TIMER) ;
DeleteObject(hBitmap);
DeleteDC(hdcMem);
PostQuitMessage () ;
return ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}

C图形化第一步的更多相关文章

  1. scratch少儿编程第一季——01、初识图形化界面编程的神器

    各位小伙伴大家好: 说到2018年互联教育的热门事件,那就不得不提Scratch. 相信各位不关注信息技术领域的各位家长也都听说过这个东西. 对于小学阶段想要接触编程或信息技术学生来说,Scratch ...

  2. MFC入门(一)-- 第一个简单的windows图形化界面小程序(打开计算器,记事本,查IP)

    ////////////////////////////////序//////////////////////////////// 大约三年前,学过一些简单的编程语言之后其实一直挺苦恼于所写的程序总是 ...

  3. [EntLib]微软企业库5.0 学习之路——第一步、基本入门

    话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白). 直到去年在 ...

  4. Samba在CentOS下的图形化界面的安装

    第一步:构建yum仓库(在此用的是北交大的yum仓库) 打开目录/etc/yum.repos.d下的CentOS-Base.repo文件,此处是我自己建的yum仓库,修改里面的链接地址为北交大的镜像的 ...

  5. 安装oracle11g不能启动图形化界面

    问题:安装oracle11g时出现xhost:  unable to open display "192.168.2.12:0.0".打不开图形化界面等. 终极解决方法:1.使用X ...

  6. Sourcetree使用 - git图形化工具(三)

    前面两个章节总结了Sourcetree的安装与配置Sourcetree密钥,这个章节主要讲如何使用Sourcetree.以前呢,都是使用git Bash进行命令行方式进行操作git,感觉部分时间浪费在 ...

  7. linux下c图形化编程之gtk+2.0简单学习

    在linux下想做一个图形化的界面,然后自己选择使用gtk+2.0来进行编辑,我的电脑已经安装过gtk+2.0了,所以就在网上找了一个安装方法,结果未测试,大家有安装问题可以说下,一起探讨下. 1.安 ...

  8. IDEA中使用Docker: 图形化 or 命令行 ,你更稀罕那个??

    Docker简介: Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化. 容器是完全使用沙箱机 ...

  9. 2018.4.21 如何正确快速安装/卸载云服务器Centos7安装GUI图形化界面GNOME桌面环境

    为云服务哦Centos安装图形化界面GNOME .KDE 1.开始前先验证一下能不能上网 ping www.baidu.com 2.接下来开始安装X(X Window System),命令为 yum ...

随机推荐

  1. 利用.bat脚本使得可运行jar开机自动运行

    1.利用Elipse到处可运行的jar包 2.写.bat脚本[点此下载],相应目录自己根据需要修改即可 3.将此脚本放在"启动"文件夹中

  2. IIS-This configuration section cannot be used at this path.

    Q:在IIS上部署web后,游览器打开报以下异常: This configuration section cannot be used at this path. This happens when ...

  3. Linux命令——dmesg

    参考:Linux kernel buffer ring Linux dmesg Command Tutorial for Beginners (5 Examples) 7 ‘dmesg’ Comman ...

  4. Kubernetes系统基础

    Kubernetes系统基础 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.容器编排系统概述 1>.容器编排系统生态圈 Docker通过“镜像”机制极富创造性地解决了应用 ...

  5. Redis锁机制的几种实现方式

    1. redis加锁分类 redis能用的的加锁命令分表是INCR.SETNX.SET 2. 第一种锁命令INCR 这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执 ...

  6. 关于类型为numpy,TensorFlow.tensor,torch.tensor的shape变化以及相互转化

    https://blog.csdn.net/zz2230633069/article/details/82669546 2018年09月12日 22:56:50 一只tobey 阅读数:727   1 ...

  7. 通过visual studio制作类库的文档

    java的集成开发工具,可以导出jar的文档. visual studio 也可以生成类库的文档,邮件项目属性,生成,输出下,选择XML文档文件.然后生成项目,就会再bin下面生成一个xml文件. 将 ...

  8. Java8 Stream 流使用场景和常用操作

    JAVA8内置的函数式编程接口应用场景和方式 pojo类对象和默认创建list的方法 import lombok.AllArgsConstructor; import lombok.Data; imp ...

  9. JDK源码那些事儿之LinkedBlockingDeque

    阻塞队列中目前还剩下一个比较特殊的队列实现,相比较前面讲解过的队列,本文中要讲的LinkedBlockingDeque比较容易理解了,但是与之前讲解过的阻塞队列又有些不同,从命名上你应该能看出一些端倪 ...

  10. python get/post接口使用

    背景: 使用python调用get post接口,入参.出参都需要转换,在使用时经常会忘记其中的一步,本文用来记录,后面再使用时直接参考使用 代码如下 post: headers = {'Conten ...