x86汇编游戏——2048

2.1游戏界面绘制

游戏界面绘制窗口程序主要有_WinMain和_ProcWinMain两个函数来构成。

_WinMain是生成窗口的主函数,主要流程为:

(1)得到应用程序句柄

(2)注册窗口类

(3)建立窗口

(4)显示窗口

(5)刷新窗口客户区

(6)维护消息获取和处理的循环

通过GetMessage获取消息

TranslateMessage翻译消息

DispatchMessage分派消息给回调函数处理

通过User32来调用回调函数

_ProcWinMain是用来处理消息的,是窗口的回调函数,也叫窗口过程。消息如WM_PAINT,WM_CHAR等。

2.1.1 界面构成

界面主要由4个部分3个模块构成.

4个部分分别是

 (1)游戏主体部分的方块

 (2)游戏说明部分

 (3)显示得分部分

 (4)游戏名称部分

3个模块分别是

 (1)静态控件'Static'绘制游戏模块

 (2)文本框'edit'绘制得分和游戏说明

 (3)位图装饰界面

2.1.2 绘制静态控件

需要绘制4X4的方块,采用一个2重循环,通过i,j控制绘制的位置和绘制的控件ID

L6:
	;eax=i*100+140,绘制的x坐标,140为起始坐标
	imul eax,i,100
	add eax,140
	;ecx=j*100+100,绘制的y坐标,100为起始坐标
	imul ecx,j,100
	add ecx,100

	;edx=i*4+j,表示第[i][j]个方块
	imul edx,i,4
	add edx,j
	;gameMat[i][j]的值转为字符数组存到Data中,dword:*4
	invoke num2byte,dword ptr gameMat[edx*4]
	;如果为0
	;eax=i*100+140,绘制的x坐标,140为起始坐标
	imul eax,i,100
	add eax,140
	;ecx=j*100+100,绘制的y坐标,100为起始坐标
	imul ecx,j,100
	add ecx,100
	.IF Data[0] =='0'
		;创建静态控件,居中有边框
		invoke CreateWindowEx,NULL,offset static,offset EmptyText,\
		WS_CHILD or WS_VISIBLE or SS_CENTER or WS_BORDER or SS_CENTERIMAGE,ecx,eax,100,100,\  
		hWnd,edx,hInstance,NULL  ;句柄为edx
	.else
		invoke CreateWindowEx,NULL,offset static,offset Data,\
		WS_CHILD or WS_VISIBLE or SS_CENTER or WS_BORDER or SS_CENTERIMAGE,ecx,eax,100,100,\ 
		hWnd,edx,hInstance,NULL  ;句柄为edx
	.endif
	;edx=i*4+j,表示第[i][j]个方块
	imul edx,i,4
	add edx,j
	;存储窗口句柄,句柄返回值在eax中
	mov hGame[edx*4],eax

2.1.2 绘制文本框

绘制文本框同绘制方块一样,通过调用CreateWindowEx函数,指定对应的值即可,由于文本框可以在界面上进行编辑,所以绘制时指定为禁用状态。

L7:
	;绘制游戏说明部分
	;创建文本框,但设为Disabeled防止玩家更改
	invoke CreateWindowEx,NULL,offset edit,offset szText1,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,60,120,15,\
	hWnd,16,hInstance,NULL
	MOV hGame[64],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText2,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,75,400,15,\
	hWnd,17,hInstance,NULL
	mov hGame[68],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText5,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,90,400,15,\
	hWnd,18,hInstance,NULL
	mov hGame[72],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText3,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,105,400,15,\
	hWnd,19,hInstance,NULL
	mov hGame[76],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText4,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,120,400,15,\
	hWnd,20,hInstance,NULL
	mov hGame[80],eax

2.1.3 绘制位图

加载位图主要通过调用gdi32库所支持的API,其主要流程为:

具体实现代码如下:

		;加载位图
		;首先获取窗口DC
		invoke GetDC, hWnd
		mov @hDc,eax
		
		;创建兼容窗口DC的缓存dc
		invoke CreateCompatibleDC,@hDc
		mov hdcIDB_BITMAP1,eax
		
		invoke CreateCompatibleDC,@hDc
		mov hdcIDB_BITMAP2,eax
		
		;创建位图缓存
		invoke CreateCompatibleBitmap, @hDc,150,80
		mov hbmIDB_BITMAP1,eax

		invoke CreateCompatibleBitmap, @hDc,90,60
		mov hbmIDB_BITMAP2,eax

		;将hbm与hdc绑定
		invoke SelectObject,hdcIDB_BITMAP1,hbmIDB_BITMAP1
		
		;载入位图到位图句柄中
		invoke LoadBitmap,hInstance,BITMAP1
		mov @hBm,eax
		;创建以位图为图案的画刷
		invoke CreatePatternBrush,@hBm
		push eax
		;以画刷填充缓存DC
		invoke SelectObject,hdcIDB_BITMAP1,eax
		;按照PATCOPY的方式
		invoke PatBlt,hdcIDB_BITMAP1,0,0,150,80,PATCOPY
		pop eax
		;删除画刷
		invoke DeleteObject,eax
		;在主窗口DC上绘制位图dc
		invoke BitBlt,@hDc,90,0,150,80,hdcIDB_BITMAP1,0,0,SRCCOPY

2.1.4 显示数字和分数

我们通过使用一个数组gameMat来保存游戏的局面,由于存储的是数字,所以需要通过不断除以10,按位转换为数字字符串来显示,之后调用SetWindowText函数显示文字。

L7:
	;绘制游戏说明部分
	;创建文本框,但设为Disabeled防止玩家更改
	invoke CreateWindowEx,NULL,offset edit,offset szText1,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,60,120,15,\
	hWnd,16,hInstance,NULL
	MOV hGame[64],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText2,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,75,400,15,\
	hWnd,17,hInstance,NULL
	mov hGame[68],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText5,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,90,400,15,\
	hWnd,18,hInstance,NULL
	mov hGame[72],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText3,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,105,400,15,\
	hWnd,19,hInstance,NULL
	mov hGame[76],eax
	invoke CreateWindowEx,NULL,offset edit,offset szText4,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,100,120,400,15,\
	hWnd,20,hInstance,NULL
	mov hGame[80],eax

	;绘制分数框
	invoke num2byte,score
	invoke CreateWindowEx,NULL,offset edit,offset Data,\
	WS_CHILD or WS_VISIBLE OR WS_DISABLED,420,38,80,15,\ 
	hWnd,21,hInstance,NULL
	mov hGame[84],eax

	xor eax,eax
	ret

2.1.5 显示游戏胜利和失败消息

通过维护gameIsEnd,gameIsWin和gameContinue三个变量。

游戏失败时,gameIsEnd置1,弹出游戏失败提示消息,点击OK重新开始游戏。

游戏胜利时,gameIsWin置1,弹出游戏胜利提示消息,玩家可选择继续游戏或结束游戏。

若游戏继续,gameContinue置1,则之后不再对游戏是否胜利进行判断。

2.2生成新模块

2.3方块移动及合并

方块的移动分为四个方向,所以首先设想为该模块编写四个函数,分别实现上下左右的移动的实现。通过沟通,我们将输入字符WASD分别代表向上、向左、向下、向右移动。

 

2.3.1移动方向以及起点

上图的四个函数的实现过程,直观上讲有如下效果:

 

通过对16个方块按行或按列进行划分,然后我们对每个方向的行或列进行遍历,之后通过遍历过程中某方块和他周围方块的关系进行比较,如此实现整个遍历过程。例如我们对向右的移动进行分析,我们就会把所有方块按行分组,对每一行的每一个方块进行遍历。

完成分治后,我们还要对遍历起点进行分析。在实现过程中,我采用了Loop的方式进行循环,所以我们的循环起点始终为4,之后不断递减形成循环。此时我们对不同方向的移动起点进行分析,可知我们的起点如图所示:

2.3.2合并实现

确定了循环起点后,我们还要对具体的方块移动方向进行确定。此处我们仍对D方向(向右移动)进行分析。在游戏过程中,我们发现,每个方块都会和其左边的方块进行比较,若某方块的左边出现相同数字或者该相同数字和数字0一同出现,则会进行合并。如此,我们就确定了每个循环中行或列的遍历方向是与移动方向相反的,如此才能更快更好的实现该功能。在确定了循环内遍历方向后,我们自然会向确定移动方向一样,对每个行或列的所有方块抽象为一个状态,并对每个状态进行遍历判断。事实上,在开发过程中我发现整个过程可以抽象为一个递归的判断过程,这样实现就更加简单。其具体实现思想如下:

我们不再对每个方向上的四个方块分别抽象,而是对其状态进行抽象:

1)是否为0

如果我们遍历到该数字为0的话,我们直接跳过,因为我们不需要对0进行任何操作;

2)左右是否为0

如果是在移动过程,在其移动方向上若存在0(存在0就说明有空方块,有位置支持移动),我们就对该方块进行移动,即0和非零数字的交换,然后继续进行判断,若到达边界或没有空方块则无法进行移动,我们对其进行合并功能的判断。

          

3)忽略0后是否有相同数字

我们知道在合并过程中,我们会对移动过程的反方向进行判断,当然判断过程中,我们仍需要对是否有0(即是否有空方方块)进行判断,若有则直接跳过,继续遍历;若遇到相同数字,直接合并,并将遍历到的数字置为0,若没有或者遍历越界,则对循环中的下一个方块进行如上方式的判断,如此就能完成每一行或列的移动和合并。

         

3.完整代码

chris-william0829/x86-2048 (github.com)

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值