windows下实现win32俄罗斯方块练手,编程的几点心得
编程珠玑2阅读笔记:
在来说这个俄罗斯方块,其实主要是2个大的部分:
1.界面绘制(游戏区,信息区,刷新重绘工作)
游戏区方块的绘制,其实都是数组来记录
2.游戏逻辑(上下左右,变形)
其实就是对数组的旋转
主要代码,才六百行:
// Russian_cube.cpp : 定义应用程序的入口点。
//
//
//
//
#include "stdafx.h"
#include "Russian_cube.h" #define MAX_LOADSTRING 100
//Tetris
#define BOUND_SIZE 10
#define TETRIS_SIZE 30
#define GAME_X 10
#define GAME_Y 20
#define INFO_X 6
#define INFO_Y GAME_Y //定时器
#define MY_TIMEER 1
#define DEFAULT_INTERVAL 500 //默认每0.5秒下降一格 //定义俄罗斯方块的形状
BOOL g_astTetris[][4][4] =
{
{{1,1,0,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{0,0,1,1},{0,0,0,0},{0,0,0,0}},
{{1,1,0,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{0,1,1,0},{1,1,0,0},{0,0,0,0},{0,0,0,0}},
{{0,1,0,0},{1,1,1,0},{0,0,0,0},{0,0,0,0}},
{{1,1,1,1},{0,0,0,0},{0,0,0,0},{0,0,0,0}}
};
#define TETRIS_CNT (sizeof(g_astTetris)/sizeof(g_astTetris[0])) //当前方块的形状
BOOL g_CurTetris[4][4];
BOOL g_NextTetris[4][4];
BOOL g_stGame[GAME_X][GAME_Y];//记录已经落下来的方块 //记录方块左上角的坐标
UINT TetrisX;
UINT TetrixY;
UINT g_uiInterval;
UINT g_uiScore; UINT g_uiMySeed = 0xffff; // 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable; // 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_RUSSIAN_CUBE, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance); // 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
} hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_RUSSIAN_CUBE)); // 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} return (int) msg.wParam;
} //
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
// 注释:
//
// 仅当希望
// 此代码与添加到 Windows 95 中的“RegisterClassEx”
// 函数之前的 Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要,
// 这样应用程序就可以获得关联的
// “格式正确的”小图标。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_RUSSIAN_CUBE));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_RUSSIAN_CUBE);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex);
} //
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_MINIMIZEBOX | WS_SYSMENU,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd)
{
return FALSE;
} ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd); return TRUE;
} int GetRandNum(int iMin,int iMax)
{
//取随机数
srand(GetTickCount() + g_uiMySeed-- ); return iMin + rand()%(iMax -iMin);
} VOID DrawBackGround(HDC hdc)
{
int x, y;
HPEN hPen = (HPEN)GetStockObject(NULL_PEN);
HBRUSH hBrush = (HBRUSH)GetStockObject(GRAY_BRUSH);
//(HBRUSH)CreateSolidBrush() HBRUSH hBrush_luo = (HBRUSH)GetStockObject(BLACK_BRUSH); Rectangle(hdc,BOUND_SIZE,BOUND_SIZE,BOUND_SIZE + GAME_X*TETRIS_SIZE
,BOUND_SIZE + GAME_Y * TETRIS_SIZE); SelectObject(hdc,hPen); for (x = 0; x < GAME_X; x++)
{
for (y = 0 ;y < GAME_Y; y++)
{
if (g_stGame[x][y])
{
SelectObject(hdc,hBrush_luo);
}
else
{ SelectObject(hdc,hBrush);
}
Rectangle(hdc,BOUND_SIZE + x*TETRIS_SIZE,
BOUND_SIZE + y * TETRIS_SIZE,
BOUND_SIZE + (x + 1)*TETRIS_SIZE,
BOUND_SIZE + (y + 1) * TETRIS_SIZE);
}
}
}
//信息区 的绘制
VOID DrawInfo(HDC hdc)
{
int x,y;
int nStartX,nStartY;
RECT rect;
TCHAR szBuf[100];//得分的字符串 HPEN hPen = (HPEN)GetStockObject(BLACK_PEN);
HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH); HBRUSH hBrush_have = (HBRUSH)GetStockObject(GRAY_BRUSH);
SelectObject(hdc,hPen);
SelectObject(hdc,hBrush); Rectangle(hdc,BOUND_SIZE*2 + GAME_X * TETRIS_SIZE,
BOUND_SIZE,BOUND_SIZE *2 + (GAME_X + INFO_X)*TETRIS_SIZE,
BOUND_SIZE + INFO_Y * TETRIS_SIZE); for (x = 0; x < 4; x++)
{
for (y = 0 ;y < 4 ;y++)
{
nStartX = BOUND_SIZE *2 + GAME_X*TETRIS_SIZE + (y +1)*TETRIS_SIZE;
nStartY = BOUND_SIZE + (x +1)*TETRIS_SIZE;
if (g_NextTetris[x][y])
{
SelectObject(hdc,hBrush);
}
else
{
SelectObject(hdc,hBrush_have);
}
Rectangle(hdc,nStartX,nStartY,nStartX+TETRIS_SIZE,nStartY+TETRIS_SIZE);
}
} nStartX = BOUND_SIZE *2 + GAME_X*TETRIS_SIZE;
nStartY = BOUND_SIZE ; rect.left = nStartX + TETRIS_SIZE;
rect.right = nStartX + TETRIS_SIZE * (INFO_X -1);
rect.top = nStartY + TETRIS_SIZE *6;
rect.bottom = nStartY + TETRIS_SIZE *7; wsprintf(szBuf,L"Score: %d",g_uiScore = 0); DrawText(hdc,szBuf,wcslen(szBuf),&rect,DT_CENTER); } //绘制区方块,起始坐标和需要绘制的方块形状
VOID DrawTetris(HDC hdc, int nStartX,int nStartY,BOOL bTetris[4][4])
{
int i,j;
HPEN hPen = (HPEN)GetStockObject(BLACK_PEN);
HBRUSH hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
SelectObject(hdc,hPen);
SelectObject(hdc,hBrush); for (i = 0;i < 4; i++)
{
for (j = 0;j < 4;j++)
{
//j 是x方向的坐标偏移
if (bTetris[i][j])
{
Rectangle(hdc,BOUND_SIZE +(nStartX + j) * TETRIS_SIZE,
BOUND_SIZE + (nStartY + i)* TETRIS_SIZE,
BOUND_SIZE +(nStartX + j + 1) * TETRIS_SIZE,
BOUND_SIZE + (nStartY + i + 1)* TETRIS_SIZE);
}
}
}
} //旋转方块, 并且靠左上角
VOID RotateTetris(BOOL bTetris[4][4])
{
BOOL bNewTetris[4][4] = {};//初始化置零
int x, y;
int xPos,yPos;
BOOL bFlag;//靠近左上角 //从上往下,从左往右,顺时针旋转
//靠上
for (x = 0,xPos = 0 ;x < 4 ; x++)
{
bFlag = FALSE;
for (y = 0 ;y < 4 ;y++)
{
bNewTetris[xPos][y] = bTetris[3 - y][x];
//逆时针旋转
//bNewTetris[x][y] = bTetris[y][3 - x];
if (bNewTetris[xPos][y])
{
bFlag = TRUE;//这一行有数据
}
}
if (bFlag)
{
xPos++;
}
} memset(bTetris,0,sizeof(bNewTetris));
//靠左
for (y = 0, yPos = 0;y < 4 ;y++)
{
bFlag = FALSE;
for (x = 0;x < 4; x++)
{
bTetris[x][yPos] = bNewTetris[x][y];
if (bTetris[x][yPos])
{
bFlag = TRUE;
}
}
if (bFlag)
{
yPos++;
}
}
//memcpy(bTetris,bNewTetris,sizeof(bNewTetris));
return;
}
BOOL CheckTetris(int nStartX,int nStartY,BOOL bTetris[4][4],BOOL bGame[GAME_X][GAME_Y])
{
int x,y;
if (nStartX < 0)
{//碰到左墙
return FALSE;
} for (x = 0;x < 4;x++)
{
for (y = 0;y < 4;y++)
{
if (bTetris[x][y])
{
//碰右墙
if (nStartX +y >=GAME_X)
{
return FALSE;
}
//碰下墙
if (nStartY + x >=GAME_Y)
{
return FALSE;
}
//碰到已有的方块
if (bGame[nStartX +y][nStartY + x])
{
return FALSE;
}
}
}
}
return TRUE;
} //落地的方块合并,并且满足消除一行
VOID RefreshTetris(int nStartX,int nStartY,BOOL bTetris[4][4], BOOL bGame[GAME_X][GAME_Y])
{
BOOL bFlag = FALSE;
int x,y;
int newX,newY;//主要用来记录
int iFulllie = 0; //校区满行的格子记录行数,用于积分 for (x = 0; x < 4;x ++)
{
for (y = 0 ;y < 4; y++)
{
if (bTetris[x][y])
{
bGame[nStartX + y][nStartY +x] = TRUE;
}
}
} for (y = GAME_Y,newY = GAME_Y; y >= 0; y--)
{
bFlag= FALSE;
for (x = 0;x < GAME_X;x++)
{
bGame[x][newY] = bGame[x][y];
if (!bGame[x][y])//这一行不满格
{
bFlag = TRUE;
}
}
if (bFlag)
{
newY--;
}
else
{
//满格的话,用上一行替换这一行
iFulllie++;
}
} if (iFulllie)
{
g_uiScore -= iFulllie *1;
}
//合并以后生成新的方块,并刷新位置
memcpy(g_CurTetris,g_NextTetris,sizeof(g_CurTetris));
memcpy(g_NextTetris,g_astTetris[ GetRandNum(0,TETRIS_CNT)],sizeof(g_NextTetris)); TetrisX = (GAME_X - 4)/2;
TetrixY = 0;
} //初始化游戏,就是方块最先初始化的位置
VOID InitGame()
{
int iTmp; TetrisX = (GAME_X - 4 )/2 ; //居中
TetrixY = 0; g_uiScore = 0;
g_uiInterval = DEFAULT_INTERVAL; iTmp = GetRandNum(0,TETRIS_CNT);
memcpy(g_CurTetris,g_astTetris[iTmp],sizeof(g_CurTetris)); iTmp = GetRandNum(0,TETRIS_CNT);
memcpy(g_NextTetris,g_astTetris[iTmp],sizeof(g_NextTetris)); memset(g_stGame,0,sizeof(g_stGame));
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc; int nWinX,nWinY,nClientX,nClientY;
RECT rect;
BOOL bTmpTetris[4][4] = {}; switch (message)
{
case WM_CREATE:
//获取窗口大小
GetWindowRect(hWnd,&rect);
nWinX = rect.right - rect.left;
nWinY = rect.bottom - rect.top;
//获取客户区大小
GetClientRect(hWnd,&rect);
nClientX = rect.right - rect.left;
nClientY = rect.bottom - rect.top; MoveWindow(hWnd,0,0,3 * BOUND_SIZE + (GAME_X + INFO_X)* TETRIS_SIZE +
(nWinX - nClientX),
2 * BOUND_SIZE + GAME_Y*TETRIS_SIZE + (nWinY - nClientY),true);
InitGame();
SetTimer(hWnd,MY_TIMEER,g_uiInterval,NULL);
break;
case WM_TIMER:
//定时器中方块下降
if (CheckTetris(TetrisX,TetrixY + 1,g_CurTetris,g_stGame))
{
TetrixY++;
}
else
{
if (TetrixY == 0)
{
MessageBox(NULL,L"不行了",L"shit!",MB_OK);
KillTimer(hWnd,MY_TIMEER);
}
RefreshTetris(TetrisX,TetrixY,g_CurTetris,g_stGame);
}
InvalidateRect(hWnd,NULL,TRUE); break;
case WM_LBUTTONDOWN:
RotateTetris(g_CurTetris);
InvalidateRect(hWnd,NULL,TRUE);
break;
case WM_KEYDOWN:
switch(wParam)
{
case VK_LEFT://左方向键
if (CheckTetris(TetrisX -1,TetrixY,g_CurTetris,g_stGame))
{//判断一下当前方块没有靠墙就
TetrisX--;
InvalidateRect(hWnd,NULL,TRUE);
}
else
{
MessageBeep(0);
}
break;
case VK_RIGHT:
if (CheckTetris(TetrisX +1,TetrixY,g_CurTetris,g_stGame))
{//判断一下当前方块没有靠墙就
TetrisX++;
InvalidateRect(hWnd,NULL,TRUE);
}
else
{
MessageBeep(0);
}
break;
case VK_UP://变形,但是要判断变形成功或者失败
memcpy(bTmpTetris,g_CurTetris,sizeof(bTmpTetris));
RotateTetris((bTmpTetris));
if (CheckTetris(TetrisX,TetrixY,bTmpTetris,g_stGame))
{
//成功后,再把旋转后的copy回来
memcpy(g_CurTetris,bTmpTetris,sizeof(bTmpTetris));
InvalidateRect(hWnd,NULL,TRUE);
}
break;
case VK_DOWN:
while (CheckTetris(TetrisX,TetrixY + 1,g_CurTetris,g_stGame))
{
TetrixY++; }
RefreshTetris(TetrisX,TetrixY,g_CurTetris,g_stGame);
InvalidateRect(hWnd,NULL,TRUE);
break;
default:
break;
} break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
DrawBackGround(hdc);
DrawInfo(hdc);
DrawTetris(hdc,TetrisX,TetrixY,g_CurTetris);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
KillTimer(hWnd,MY_TIMEER);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
} // “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE; case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
代码参考:
主要是听了这个课程,这个公开课做点小项目,贪吃蛇,网络啊什么的,都是代码挺好的,作为一个熟悉其他领域的小项目非常适合上手:
windows下的win32编程要学的东西还比较多,下面给出一个简单的知识点:
windows下实现win32俄罗斯方块练手,编程的几点心得的更多相关文章
- Windows下TEX排版论文攻略—CTeX、JabRef使用心得
笔者刚刚接触到TEX排版,相关知识完全空白,用了两天时间学习并完成了一篇论文的完整排版. 期间遇到不少小问题,着实辛苦,分享至上,现将其解决办法总结归纳,共同学习. 一.工具介绍 TeX是一个 ...
- Windows下LATEX排版论文攻略—CTeX、JabRef使用心得
笔者刚刚接触到TEX排版,相关知识完全空白,用了两天时间学习并完成了一篇论文的完整排版. 期间遇到不少小问题,着实辛苦,分享至上,现将其解决办法总结归纳,共同学习. 一.工具介绍 TeX是一个很好排版 ...
- Windows下Hadoop编程环境配置指南
刘勇 Email: lyssym@sina.com 本博客记录作者在工作与研究中所经历的点滴,一方面给自己的工作与生活留下印记,另一方面若是能对大家有所帮助,则幸甚至哉矣! 简介 鉴于最近在研究 ...
- windows下《Go Web编程》之Go环境配置和安装
<Go Web编程>笔者是基于unix下讲述的,作为入门练手,我选择在windows下开发,全程按照目录进行... 一.安装 windows下需要安装MinGW,通过MinGW安装gcc支 ...
- Qt4.8在Windows下的三种编程环境搭建
Qt4.8在Windows下的三种编程环境搭建 Qt的版本是按照不同的图形系统来划分的,目前分为四个版本:Win32版,适用于Windows平台:X11版,适合于使用了X系统的各种Linux和Unix ...
- Windows下GUI编程——窗口
windows下创建一个基于GUI的窗口程序很简单,使用MFC或者Win32 API都可以实现.本文简单整理下windows API创建GUI应用程序的基本编码框架. 比较常见的窗口包括:桌面窗口.应 ...
- c++ 网络编程(八) LINUX-epoll/windows-IOCP下 socket opoll函数用法 优于select方法的epoll 以及windows下IOCP 解决多进程服务端创建进程资源浪费问题
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9622548.html 锲子:关于并发服务器中的I/O复用实现方式,前面在网络编程系列四还是五来 ...
- Windows下串口编程
造冰箱的大熊猫@cnblogs 2019/1/27 将Windows下串口编程相关信息进行下简单小结,以备后用. 1.打开串口 打开串口使用CreateFile()函数.以打开COM6为例: HAN ...
- Linux下TCP网络编程与基于Windows下C#socket编程间通信
一.linux下TCP网络编程基础,需要了解相关函数 Socket():用于套接字初始化. Bind():将 socket 与本机上的一个端口绑定,就可以在该端口监听服务请求. Listen():使s ...
随机推荐
- 2015 多校联赛 ——HDU5416(异或)
CRB has a tree, whose vertices are labeled by 1, 2, …, N. They are connected by N – 1 edges. Each ed ...
- hdu 3939(勾股+容斥)
题意: 给定一个整数L(L<=1e12),计算(x,y,z)组的个数.其中x<y<z,x^2+y^2=z^2,gcd(x,y)==1,gcd(x,z)==1,gcd(y,z)==1. ...
- qemu 模拟-arm-mini2440开发板-启动u-boot,kernel和nfs文件系统【转】
转自:http://www.cnblogs.com/riskyer/p/3366001.html qemu 本文介绍了如何编译u-boot.linux kernel,然后用qemu启动u-boot和l ...
- 笔记5 bean的作用域
1. Spring定义了多种作用域,可以基于这些作用域创建bean,包括: 单例(Singleton):在整个应用中,只创建bean的一个实例.(默认) 原型(Prototype):每次注入或者通过S ...
- Linux学习之Centos(三)------系统文件目录及含义详解
Linux学习之Centos 之三------文件目录及含义 在了解了每个文件的相关种类与属性,以及了解了如何更改文件属性/权限的相关信息后,再来要了解的就是, 为什么每套Linux distribu ...
- 微信小程序开发 导入文件说没找到.json的问题
这个真的坑爹 网上的答案也没几个靠谱 说白了就是找个空文件直接创建 然后复制粘贴 反应了这玩意ide不成熟 进去之后直接创了个quick start的源码 也就是我们看的这个网页建议我们下载的源码 老 ...
- textarea不能使用maxlength
知道文本框有个maxlength属性,有次开发项目中使用了textarea标签,没去看文档,直接加了maxlength属性,且有效果没有报错,喜滋滋的用了,结果没两天就测试出了bug 问题描述:文本域 ...
- python学习之路基础篇(第六篇)
一.算法 冒泡排序 两两比较 打的沉下去,小的浮上来 从而把数字从小到大排列出来 选择排序 随机取一个索引作为最大值,然后和列表中的其他索引进行比较,如果l[0]<l[1],则将l[1]修改为 ...
- 牛客网编程练习之PAT乙级(Basic Level):1034 写出这个数
AC代码: import java.util.*; /** * @author CC11001100 */ public class Main { public static void main(St ...
- Java中的内存分配
Java程序在运行时,需要在内存中分配空间,为了提高效率,就对空间进行了不同区域的划分,因为每一片区域否有特定的处理数据方式和内存管理方式. 1.栈存储局部变量 2.堆存储new出来的东西 3.方法区 ...