在去年学汇编的时候,因为学到了中断处理,就拿int 10h在640x480的全屏模式下写了一个在屏幕上弹来弹去的小方块,因为当时不知道画圆算法。写了一会我就想可以把这个程序做成一个打乒乓球的游戏啊,当时心里还很激动,但是CPU都忙着画小球了,哪有功夫处理拍子的移动呢?问了一下人也没什么结果,就想那可能与多线程有关吧,就想以后再改进。可是到现在还没想法呢,当时的代码如下,写得很憋脚,现在自己都看不懂怎么弹的了,并且里面转来转去的,绝不是好的编程风格。我又不知道怎么在那种方式下截屏,所以也没图:
.model small
.stack
.data
num db 10
.code
.startup
mov ax,0012h
int 10h ;进入图形模式
mov dx,450
mov cx,318 ;初始坐标值
lop0: mov al,09h ;浅蓝色
mov ah,0ch
int 10h
inc cx
cmp cx,328
jne lop0
mov cx,318 ;画完一行,cx还原,接着画下一行
inc dx
cmp dx,460
jne lop0 ;10行10列的小方块
mov dx,460
mov cx,305 ;10行,40列的球拍
lop1: mov al,0eh
mov ah,0ch
int 10h
inc cx
cmp cx,345
jne lop1
mov cx,305
inc dx
cmp dx,470
jne lop1
mov ah,7
int 21h ;按任意键开始移动
mov dx,450
mov cx,318
line1: dec dx ;每次进入该处须有两个参数,亦即方块顶点坐标放在dx,cx中
dec cx ;纵横坐标分别减1,画框实现移动效果
line11: mov al,09h ;浅蓝色
mov ah,0ch
int 10h
inc cx
dec num
cmp num,0
jne line11
mov num,10
line12: mov al,00h ;黑色
mov ah,0ch
int 10h
inc dx
dec num
cmp num,0
jne line12
mov num,10
line13: mov al,00h ;黑色
mov ah,0ch
int 10h
dec cx
dec num
cmp num,0
jne line13
mov num,10
dec dx
line14: mov al,09h ;浅蓝色
mov ah,0ch
int 10h
dec dx
dec num
cmp num,0
jne line14
inc dx
mov num,10
call delay ;延时子程序
cmp cx,0 ;检测cx是否为0,为0了就说明是从下边弹到左边,
je line2 ;反弹
cmp dx,0 ;检测dx是否为0,为0了就说明是从下边弹到上边,
je imline2 ;这两种反弹要区别对待!!!!!!!
jmp line1
line2: mov num,10
line21: mov al,00h
mov ah,0ch
int 10h ;黑色下划,
inc dx
dec num
cmp num,0
jne line21
mov num,10
dec dx
line22: mov al,00h
mov ah,0ch
int 10h ;黑色右划
inc cx
dec num
cmp num,0
jne line22
mov num,10
dec dx
line23: mov al,09h
mov ah,0ch
int 10h ;浅蓝色上划
dec dx
dec num
cmp num,0
jne line23
mov num,10
inc dx
line24: mov al,09h
mov ah,0ch
int 10h ;浅蓝色左划
dec cx
dec num
cmp num,0
jne line24
mov num,10
inc cx
call delay ;延时子程序
cmp dx,0 ;检测dx是否为0了,为0了就说明由左边弹到上边了
je imline1 ;反弹
cmp cx,630
je line1
jmp line2
imline1:mov al,00h
mov ah,0ch
mov num,10
imli11: int 10h ;黑色右划,
inc cx
dec num
cmp num,0
jne imli11
mov num,10
inc dx
imli12: mov al,09h
mov ah,0ch
int 10h ;浅蓝色下划
inc dx
dec num
cmp num,0
jne imli12
mov num,10
dec dx
imli13: int 10h ;浅蓝色左划
dec cx
dec num
cmp num,0
jne imli13
mov num,10
imli14:mov al,00h
mov ah,0ch
int 10h ;黑色上划
dec dx
dec num
cmp num,0
jne imli14
mov num,10
inc dx
inc cx ;使cx,dx的当前值还原到左顶点上去
call delay ;延时子程序
cmp dx,450 ;检测dx是否为180了,为180了就说明由上边弹到下边了
;当前cx值为204
je line2 ;反弹
cmp cx,630 ;检测cx是否为319,为319了就说明由上边弹到右边了
je imline2
jmp imline1
imline2:mov al,00h
mov ah,0ch
mov num,10
imli21: int 10h
inc cx
dec num
cmp num,0
jne imli21
mov num,10
dec cx
;黑色右划
imli22: int 10h
inc dx
dec num
cmp num,0
jne imli22
mov num,10
;黑色下划
dec cx
mov al,09h
mov ah,0ch
imli23: int 10h
dec cx
dec num
cmp num,0
jne imli23
;浅蓝色左划
mov num,10
inc cx
imli24: int 10h
dec dx
dec num
cmp num,0
jne imli24
;浅蓝色上划
mov num,10
inc dx
call delay ;延时子程序
cmp dx,450
je line1
cmp cx,0
je imline1
jmp imline2
.exit 0
delay proc near
push cx
push bx
mov bx,100
del: mov cx,9801h
de: loop de
dec bx
cmp bx,0
jne del ;延时程序
pop bx
pop cx
ret
delay endp
end
前几天学了WIN32下的编程,所以就想把以前的那个弹球程序做成WIN32的,在一个窗口里面弹就可以了,不用切到全屏去,看了半天以前那个弹的算法都没看明白,所以就直接重新考虑了一遍,写成了如下的了,
#include <windows.h>
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Bounce_DrawPixelDirectly") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Bounce_DrawPixelDirectly"),
WS_OVERLAPPED | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
static int cxClient,cyClient,cxStart, cyStart;
PAINTSTRUCT ps ;
static int i,j,delay,direction;
switch (message)
{
case WM_SIZE:
cxClient=LOWORD (lParam);
cyClient=HIWORD (lParam);
cxStart =LOWORD (lParam)/2 ;
cyStart =HIWORD (lParam)-10;
return 0;
case WM_PAINT:
hdc = BeginPaint (hwnd, &ps) ;
direction=1;
while(1)
{
switch(direction)
{
case 1:
for(;cxStart>=0 && cyStart>=0;cxStart--,cyStart--,delay=1000000)
{
while(delay)
{
delay--;
}
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
SetPixel(hdc,cxStart+i,cyStart+j,RGB(0,0,0));
}
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart+9,cyStart+i,RGB(255,255,255));
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart+i,cyStart+9,RGB(255,255,255));
}
}
if(cxStart<=0)
{
direction=2;
}
if(cyStart<=0)
{
direction=4;
}
break;
case 2:
for(;cyStart>=0 && cxStart<=cxClient-10;cxStart++,cyStart--,delay=1000000)
{
while(delay)
{
delay--;
}
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
SetPixel(hdc,cxStart+i,cyStart+j,RGB(0,0,0));
}
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart,cyStart+i,RGB(255,255,255));
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart+i,cyStart+9,RGB(255,255,255));
}
}
if(cyStart<=0)
{
direction=3;
}
if(cxStart>=cxClient-10)
{
direction=1;
}
break;
case 3:
for(;cyStart<=cyClient-10 && cxStart<=cxClient-10;cxStart++,cyStart++,delay=1000000)
{
while(delay)
{
delay--;
}
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
SetPixel(hdc,cxStart+i,cyStart+j,RGB(0,0,0));
}
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart,cyStart+i,RGB(255,255,255));
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart+i,cyStart,RGB(255,255,255));
}
}
if(cyStart>=cyClient-10)
{
direction=2;
}
if(cxStart>=cxClient-10)
{
direction=4;
}
break;
case 4:
for(;cxStart>=0 && cyStart<=cyClient-10;cxStart--,cyStart++,delay=1000000)
{
while(delay)
{
delay--;
}
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
SetPixel(hdc,cxStart+i,cyStart+j,RGB(0,0,0));
}
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart+i,cyStart,RGB(255,255,255));
}
for(i=0;i<10;i++)
{
SetPixel(hdc,cxStart+9,cyStart+i,RGB(255,255,255));
}
}
if(cyStart>=cyClient-10)
{
direction=1;
}
if(cxStart<=0)
{
direction=3;
}
break;
}
}
EndPaint (hwnd, &ps) ;
return 0 ;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
可是问题跟当初汇编写的一样的是在程序运行期间,CPU整个都去绘小球了,别的消息统统无法处理,结束都无法结束,这绝对是不好的,在WINDOWS程序设计一书上的BOUNCE程序是通过计时器处理的,所以我又按计时器的思路重新实现了一遍,这会当然是可以并行无误了,哈哈。
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Bounce_DrawPixelDirectly") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Bounce_DrawPixelDirectly"),
WS_OVERLAPPED | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
static int cxClient,cyClient,cxStart, cyStart,xStart,yStart;
PAINTSTRUCT ps ;
static int i,j,delay,direction=1;
switch (message)
{
case WM_CREATE:
SetTimer (hwnd, ID_TIMER,0, NULL);
return 0;
case WM_SIZE:
cxClient=LOWORD (lParam);
cyClient=HIWORD (lParam);
cxStart =LOWORD (lParam)/2-5;
cyStart =HIWORD (lParam)-10-10;
xStart =cxClient/2-30;
yStart =cyClient-10;
cyClient-=11;
return 0;
case WM_TIMER:
switch(direction)
{
case 1:
if(cxStart>0 && cyStart>=0)
{
cxStart--;
cyStart--;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cxStart==0)
{
direction=2;
}
if(cyStart<=0)
{
direction=4;
}
break;
case 2:
if(cyStart>0 && cxStart<cxClient-10)
{
cxStart++;
cyStart--;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cyStart==0)
{
direction=3;
}
if(cxStart==cxClient-10)
{
direction=1;
}
break;
case 3:
if(cxStart<cxClient-10 && cyStart<cyClient-10)
{
cxStart++;
cyStart++;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cyStart==cyClient-10)
{
direction=2;
}
if(cxStart==cxClient-10)
{
direction=4;
}
break;
case 4:
if(cyStart<cyClient-10 && cxStart>0)
{
cxStart--;
cyStart++;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cyStart==cyClient-10)
{
direction=1;
}
if(cxStart==0)
{
direction=3;
}
break;
}
return 0 ;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
SetPixel(hdc,cxStart+i,cyStart+j,RGB(0,0,0));
}
}
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
KillTimer(hwnd,ID_TIMER);
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
还有就是我突然想到InvalidateRect()后WINDOWS不是会自动擦除整个客户区再重新绘吗?那我还有自己擦除小球边缘并且再添加新线的必要吗?我只需要在新的顶点处重新绘制小球不就可以了,还有那么既然可以并行运行了那么以前想得球拍的控制不是也可以并行处理了吗?在WM_PAINT中加入 :
for(i=0;i<10;i++)
{
for(j=0;j<60;j++)
{
SetPixel(hdc,xStart+j,yStart+i,RGB(0,128,0));
}
}
然后处理WM_KEYDOWN:
case WM_KEYDOWN:
switch(wParam)
{
case VK_LEFT:
if(xStart>=0)
{
xStart-=4;
}
break;
case VK_RIGHT:
if(xStart<=cxClient-60)
{
xStart+=4;
}
break;
}
return 0;
再在小球反弹的控制逻辑中加入没有用球拍接住球的判断,并使程序弹出一个MESSAGEBOX来告诉你失败了,并发送一个WM_DESTROTY消息直接结束程序。最后因为白色的背景不太好看,给换下背景,还有就是球的运动速度有点慢,玩起来太容易了(哈哈,除了我以外真有还想玩的话),可是TIMER的间隔已经设置为最小的1ms了,那可行的途径就是在绘制小球的时候一次移动三个像素单位,我试了一下效果也可以接受,这就是最终成果了,哈哈,我的第一个WIN32游戏:
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("Bounce_DrawPixelDirectly") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ;
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(208,221,238));
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = szAppName ;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
szAppName, MB_ICONERROR) ;
return 0 ;
}
hwnd = CreateWindow (szAppName, TEXT ("Bounce_DrawPixelDirectly"),
WS_OVERLAPPED | WS_SYSMENU,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL) ;
ShowWindow (hwnd, iCmdShow) ;
UpdateWindow (hwnd) ;
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc ;
static int cxClient,cyClient,cxStart, cyStart,xStart,yStart;
PAINTSTRUCT ps ;
static int i,j,delay,direction=1;
// direction=1; //?????????????????????????????????????????????
switch (message)
{
case WM_CREATE:
SetTimer (hwnd, ID_TIMER,0, NULL);
return 0;
case WM_SIZE:
cxClient=LOWORD (lParam);
cyClient=HIWORD (lParam);
cxStart =LOWORD (lParam)/2-5;
cyStart =HIWORD (lParam)-10-10;
xStart =cxClient/2-30;
yStart =cyClient-10;
cyClient-=11;
return 0;
case WM_TIMER:
switch(direction)
{
case 1:
if(cxStart>0 && cyStart>=0)
{
cxStart-=3;
cyStart-=3;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cxStart<=0)
{
direction=2;
}
if(cyStart<=0)
{
direction=4;
}
break;
case 2:
if(cyStart>0 && cxStart<cxClient-10)
{
cxStart+=3;
cyStart-=3;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cyStart<=0)
{
direction=3;
}
if(cxStart>=cxClient-10)
{
direction=1;
}
break;
case 3:
if(cxStart<cxClient-10 && cyStart<cyClient-10)
{
cxStart+=3;
cyStart+=3;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cyStart>=cyClient-10)
{
if(cxStart+10<xStart+60 && cxStart>=xStart)
{
direction=2;
}
else
{
cxStart+=3;
cyStart+=3;
InvalidateRect(hwnd,NULL,TRUE);
if(cyStart>cyClient+10)
{
KillTimer(hwnd,ID_TIMER);
MessageBox(hwnd,"AWHO,You lost!","Fail!",MB_OK);
SendMessage(hwnd,WM_DESTROY,0,0);
}
}
}
if(cxStart>=cxClient-10)
{
direction=4;
}
break;
case 4:
if(cyStart<cyClient-10 && cxStart>0)
{
cxStart-=3;
cyStart+=3;
InvalidateRect(hwnd,NULL,TRUE);
}
if(cyStart>=cyClient-10)
{
if(cxStart+10<xStart+60 && cxStart>=xStart)
{
direction=1;
}
else
{
cxStart-=3;
cyStart+=3;
InvalidateRect(hwnd,NULL,TRUE);
if(cyStart>cyClient+10)
{
KillTimer(hwnd,ID_TIMER);
MessageBox(hwnd,"AWHO,You fail!","Fail!",MB_OK);
SendMessage(hwnd,WM_DESTROY,0,0);
}
}
}
if(cxStart<=0)
{
direction=3;
}
break;
}
return 0 ;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
for(i=0;i<10;i++)
{
for(j=0;j<60;j++)
{
SetPixel(hdc,xStart+j,yStart+i,RGB(0,128,0));
}
}
for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
{
SetPixel(hdc,cxStart+i,cyStart+j,RGB(255,128,0));
}
}
EndPaint(hwnd,&ps);
return 0;
case WM_KEYDOWN:
switch(wParam)
{
case VK_LEFT:
if(xStart>=0)
{
xStart-=5;
}
break;
case VK_RIGHT:
if(xStart<=cxClient-60)
{
xStart+=5;
}
break;
}
return 0;
case WM_DESTROY:
PostQuitMessage (0) ;
return 0 ;
}
return DefWindowProc (hwnd, message, wParam, lParam) ;
}
回头可以把刚学得图形学的画圆算法拿过来,做个更像样点的圆的小球,呵呵,看着应该更爽。