Windows编程之互动与动画

第五节: 考虑屏幕左侧一台坦克,向水平方向发射一枚炮弹,穿越屏幕。

很自然地,这场景中有坦克和炮弹两个对象,各自有各自坐标,坦克坐标是固定的,而炮弹坐标是变化的。因此有两个结构体类型:Tank和Bullet

在Win_Learn工作区中构建新工程WinStep2,同样选择Win32Application,仍然选择典型的Windows程序。

建好工程以后,在WinStep2.cpp文件开始的地方创建两个结构体类型,以及全局变量。
在这里插入图片描述
程序片段13 数据结构和全局变量

以上代码中,包含了将坦克和子弹绘出的函数的声明。
在这里插入图片描述
程序片段14 绘出坦克和子弹(版本1)

在绘图的消息中添加对坦克和子弹的绘图调用:
在这里插入图片描述
程序片段15 在绘图消息调用对象的绘图

仔细录入程序并运行——嗯?看到子弹的轨迹了,这个不太正常。而且子弹速度太快了,根本没有看到子弹穿越过程。所以第一程序需要一个“擦除上次子弹”的代码,第二程序在连续画子弹时需要有个延迟时间。
代码修订如下:
在这里插入图片描述
程序片段16 擦除子弹轨迹残影
在这里插入图片描述
程序片段17 加入了擦除轨迹和延时的显示代码

注意程序若要正确编译,还需要声明绘制坦克和绘制子弹的地方加入擦除子弹函数的声明。

运行程序——可以看到子弹正确向右发射了,就是似乎慢了点,这个可以通过调整子弹速度和休眠时间来实现。不过有个严重问题——在绘图过程中,整个程序像死机一样。回忆拓展一曾经提到过,任何消息处理的过程必须是快速的,否则程序将停止响应。在上面的例子里,将整个子弹移动过程都放到了绘制消息,若是子弹飞行1分钟,那么这1分钟内程序什么事都做不了。因此需要通过某种方式,将这一分钟的绘图打散为分解为很多时间碎片,每个时间碎片中绘制子弹,然后下一时间碎片中先擦除子弹再在新的位置中画出子弹。这样就需要用到Windows提供的定时器消息。定时器允许最短每50毫秒间隔触发向WndProc发出WM_TIMER消息,在Win7中这个时间间隔被减少到10毫秒,这样可以提供更佳的时间精确度。此外,子弹的射出最好还是由用户控制,比如按下空格键发射子弹。这样程序需要响应WM_KEY,如果是空格键则发出一枚子弹。程序调整如下:(因为代码较长,可以直接贴到VC++)

// WinStep2.cpp : Defines the entry point for the application.
#include "stdafx.h"
#include "resource.h"
#include <math.h>
#define MAX_LOADSTRING 100struct Tank
{int			x,y,v;COLORREF	MarkColor;char		Mark[10];float faceangle;
};
struct Bullet
{int		x,y;int		v;float	direction;BOOL	exist;
};void DrawTank(HDC hdc,struct Tank *pt);
void DrawBullet(HDC hdc,struct Bullet *pb);
void EraseBullet(HDC hdc,struct Bullet *pb);
RECT	rtWindow;	//整个窗口大小,一开始就设置好,省得每次画图都查一遍。
HPEN	TankFrame,TankCannon;
HBRUSH	TankBody;
HFONT	TankMark;
HPEN	BulletFrame;
HBRUSH	BulletBody;
struct	Tank   Tank1;
struct	Bullet b[10];// Global Variables:
HINSTANCE hInst;			// current instance
HWND hWnd;				//主窗口
TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];	// Foward declarations of functions included in this code module:
ATOM				MyRegisterClass(HINSTANCE hInstance);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{int	i=0;MSG msg;HACCEL hAccelTable;LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);LoadString(hInstance, IDC_WINSTEP2, szWindowClass, MAX_LOADSTRING);MyRegisterClass(hInstance);hInst = hInstance;hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_WINSTEP2);GetClientRect(hWnd,&rtWindow);TankFrame=(HPEN)GetStockObject(NULL_PEN);TankCannon=CreatePen(PS_SOLID,5,RGB(0,0,0));TankBody=CreateSolidBrush(RGB(0,255,0));TankMark=CreateFont(18,0,0,0,FW_BOLD,TRUE,FALSE,FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH,"新宋体");BulletFrame=(HPEN)GetStockObject(NULL_PEN);BulletBody=(HBRUSH)GetStockObject(BLACK_BRUSH);Tank1.x=rtWindow.left+20;Tank1.y=(rtWindow.bottom+rtWindow.top)/2;Tank1.faceangle=0;Tank1.v=3;strcpy(Tank1.Mark,"008");Tank1.MarkColor=RGB(227,221,73);for(i=0;i<10;i++)b[i].exist=FALSE;ShowWindow(hWnd, nCmdShow);UpdateWindow(hWnd);SetTimer(hWnd,1,50,NULL);	//设置每50毫秒触发一次WM_TIMER.while (GetMessage(&msg, NULL, 0, 0)) {if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {TranslateMessage(&msg);DispatchMessage(&msg);}}return msg.wParam;
}
ATOM MyRegisterClass(HINSTANCE hInstance)
{WNDCLASSEX wcex;wcex.cbSize = sizeof(WNDCLASSEX); wcex.style			= CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc	= (WNDPROC)WndProc;wcex.cbClsExtra		= 0;wcex.cbWndExtra		= 0;wcex.hInstance		= hInstance;wcex.hIcon			= LoadIcon(hInstance, (LPCTSTR)IDI_WINSTEP2);wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);wcex.lpszMenuName	= (LPCSTR)IDC_WINSTEP2;wcex.lpszClassName	= szWindowClass;wcex.hIconSm		= LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);return RegisterClassEx(&wcex);
}LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{PAINTSTRUCT ps;HDC hdc;int	i;switch (message) {case WM_PAINT:hdc = BeginPaint(hWnd, &ps);DrawTank(hdc,&Tank1);for(i=0;i<10;i++)if(b[i].exist)DrawBullet(hdc,&b[i]);EndPaint(hWnd, &ps);break;case WM_KEYDOWN:	//当用户按下键if(wParam==' ')//如果是空格键{for(i=0;i<10;i++)	//寻找一个可用的子弹if(b[i].exist==FALSE){b[i].exist=TRUE;b[i].direction=Tank1.faceangle;b[i].v=5;b[i].x=Tank1.x+30*cos(b[i].direction);b[i].y=Tank1.y+30*sin(b[i].direction);break;}if(i>=10)	//屏幕子弹过多Beep(500,50);	//发出滴滴声警告}return 0;case WM_TIMER:	//定时器hdc=GetDC(hWnd);for(i=0;i<10;i++)if(b[i].exist){EraseBullet(hdc,&b[i]);b[i].x+=b[i].v*cos(b[i].direction);b[i].y+=b[i].v*sin(b[i].direction);if(b[i].x<rtWindow.left || b[i].x>rtWindow.right || b[i].y<rtWindow.top || b[i].y>rtWindow.bottom)b[i].exist=FALSE;elseDrawBullet(hdc,&b[i]);}ReleaseDC(hWnd,hdc);return 0;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}
void DrawTank(HDC hdc,struct Tank* pt)
{RECT	TankArea;POINT	CannonEnd;HGDIOBJ	OldPen=SelectObject(hdc,TankFrame);HGDIOBJ	OldBrush=SelectObject(hdc,TankBody);HGDIOBJ	OldFont=SelectObject(hdc,TankMark);COLORREF	OldFontColor=SetTextColor(hdc,pt->MarkColor);TankArea.left=pt->x-18,TankArea.right=pt->x+18;TankArea.top=pt->y-18,TankArea.bottom=pt->y+18;RoundRect(hdc,pt->x-20,pt->y-20,pt->x+20,pt->y+20,4,4);		DrawText(hdc,pt->Mark,-1,&TankArea,DT_CENTER | DT_SINGLELINE | DT_VCENTER);SelectObject(hdc,TankCannon);MoveToEx(hdc,pt->x,pt->y,NULL);CannonEnd.x=pt->x+30*cos(pt->faceangle);CannonEnd.y=pt->y+30*sin(pt->faceangle);LineTo(hdc,CannonEnd.x,CannonEnd.y);SetTextColor(hdc,OldFontColor);SelectObject(hdc,OldFont);SelectObject(hdc,OldBrush);SelectObject(hdc,OldPen);
}void DrawBullet(HDC hdc,struct Bullet* pb)
{HGDIOBJ	OldBrush=SelectObject(hdc,BulletBody);HGDIOBJ	OldPen=SelectObject(hdc,BulletFrame);Ellipse(hdc,pb->x-2,pb->y-2,pb->x+2,pb->y+2);SelectObject(hdc,OldBrush);SelectObject(hdc,OldPen);
}void EraseBullet(HDC hdc,struct Bullet* pb)
{HGDIOBJ	oldBrush=SelectObject(hdc,GetStockObject(WHITE_BRUSH));HGDIOBJ	oldPen=SelectObject(hdc,GetStockObject(WHITE_PEN));Rectangle(hdc,pb->x-2,pb->y-2,pb->x+2,pb->y+2);SelectObject(hdc,oldPen);SelectObject(hdc,oldBrush);
}

程序片段18 坦克发射子弹代码

以上程序人机控制较少。还可通过鼠标键来移动坦克的炮口,使用光标键来控制坦克自身的移动。为实现这个功能,需要消息函数中响应鼠标移动事件和响应新的按键事件;另外使用空格键发射子弹不如使用鼠标左键发射子弹来的合理,所以前述程序的WndProc过程可以修改如下:
在这里插入图片描述
程序片段19 修改一:增加EraseTank声明
在这里插入图片描述
程序片段20 修改二:增加按键响应。这里给出上箭头,其他按键自行补上
在这里插入图片描述
程序片段21 修改三:增加鼠标移动消息响应
在这里插入图片描述
程序片段22 修改四:增加左键按下响应
在这里插入图片描述
程序片段23 修改五:增加擦除坦克代码

订正:
在这里插入图片描述
更正为:
在这里插入图片描述
思考:若是在子弹移动过程中碰到物体,比如敌方坦克,或者打到墙上,如何处理?这类问题叫做碰撞检测。需要对系统中每个对象保留exist(是否有效)状态,或者life(生命值)。举例而言,若考虑一个经典的坦克大战游戏,包括:砖墙,敌方坦克,己方坦克,草丛,双方子弹,则需要扩充前文的struct结构体,新增加Brick数组,Grass数组,Bullet数组。然后在WM_TIME中依次对这些可变对象做一对一检测。另外坦克是不能穿越砖墙的,所以,在WM_KEYDOWN中也需要对坦克移动作出限制。只需要简单地堆砌代码即可将程序变得更生动有趣。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/347390.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

创新设计模式:工厂模式

以前&#xff0c;我们对创建模式进行了介绍&#xff0c;并使用抽象工厂模式来创建对象族。 下一个模式是Factory模式 。 当涉及到Java时&#xff0c;工厂模式是最常用的模式之一。 那么&#xff0c;工厂模式到底是什么呢&#xff1f; 工厂模式处理创建对象而未指定确切的类或…

Java福尔摩斯的约会大侦探福尔摩斯接到一张奇怪的字条:“我们约会吧 3485djDkxh4hhGE 2984akDfkkkkggEdsb shgsfdk dHyscvnm”。大侦探很快就明白了

题目描述&#xff1a; 大侦探福尔摩斯接到一张奇怪的字条&#xff1a;“我们约会吧&#xff01; 3485djDkxh4hhGE 2984akDfkkkkggEdsb s&hgsfdk d&Hyscvnm”。大侦探很快就明白了&#xff0c;字条上奇怪的乱码实际上就是约会的时间“星期四 14:04”&#xff0c;因为前面…

Windows编程之网络之邮件槽通讯

第6节 使用邮件槽传输数据 网络通讯的理论很复杂&#xff0c;可是初学者不考虑性能的话&#xff0c;可以使用很简单的模式。一般而言网络通讯有两种模式&#xff0c;点对点模式或者广播模式。点对点模式就像一对一辅导&#xff0c;双方都能听清楚对方说什么&#xff0c;并作出…

java 批量处理 示例_Java异常处理教程(包含示例和最佳实践)

java 批量处理 示例异常是可能在程序执行期间发生的错误事件&#xff0c;它会破坏其正常流程。 Java提供了一种健壮且面向对象的方式来处理异常情况&#xff0c;称为Java异常处理 。 我们将在本教程中研究以下主题。 Java异常处理概述 异常处理关键字 异常层次 有用的异常方…

Java德才论宋代史学家司马光在《资治通鉴》中有一段著名的“德才论”:“是故才德全尽谓之圣人,才德兼亡谓之愚人,德胜才谓之君子,才胜德谓之 小人。凡取人之术,苟不得圣人,君子而与之,与其得小人,不若

题目描述&#xff1a; 宋代史学家司马光在《资治通鉴》中有一段著名的“德才论”&#xff1a;“是故才德全尽谓之圣人&#xff0c;才德兼亡谓之愚人&#xff0c;德胜才谓之君子&#xff0c;才胜德谓之小人。凡取人之术&#xff0c;苟不得圣人&#xff0c;君子而与之&#xff0c…

Windows编程之多媒体

一、语音接口的使用: 第一步&#xff0c;安装MSSpeech SDK。 MSSpeech SDK可从网络课堂安装&#xff0c;也可在微软公司直接下载&#xff0c;安装完毕后具有了语音处理能力。 安装过程全选默认即可。 安装完毕后&#xff0c;在开始菜单中会增加MS Speech SDK 5.1&#xff0…

Java的@Serial批注

JDK可能正在使用JDK 11 &#xff1a; Serial获得另一个标准&#xff08;预定义&#xff09;注释 。 JDK-8202385 [“标记与序列相关的字段和方法的注释”]的目的是添加“某种“ SerialRelated”注释&#xff0c;以促进对序列字段和方法的声明的自动检查。” 这种想法是为了更好…

Java部分A+B正整数A的“DA(为1位整数)部分”定义为由A中所有DA组成的新整数PA。例如:给定A = 3862767,DA = 6,则A的“6部分”PA是66,因为A中有2个6。现给定A、DA

题目描述&#xff1a; 正整数A的“DA&#xff08;为1位整数&#xff09;部分”定义为由A中所有DA组成的新整数PA。例如&#xff1a;给定A 3862767&#xff0c;DA 6&#xff0c;则A的“6部分”PA是66&#xff0c;因为A中有2个6。现给定A、DA、B、DB&#xff0c;请编写程序计算…

Windows编程之调用Matlab

一、选择matlab&#xff1a; 注意。Matlab的安装需要较长时间&#xff0c;建议本拓展在同学们自己的电脑上运行。 Matlab是大多数工科学生必修的科目&#xff0c;是一个口碑极佳的数学计算工具&#xff0c;可以支持立即运算和程序设计两种形式。Matlab较新的版本均提供32位版本…

JavaA除以B本题要求计算A/B,其中A是不超过1000位的正整数,B是1位正整数。你需要输出商数Q和余数R,使得A = B * Q + R成立。

题目描述&#xff1a; 本题要求计算A/B&#xff0c;其中A是不超过1000位的正整数&#xff0c;B是1位正整数。你需要输出商数Q和余数R&#xff0c;使得A B * Q R成立。 输入描述: 输入在1行中依次给出A和B&#xff0c;中间以1空格分隔。 输出描述: 在1行中依次输出Q和R&…

Windows编程之使用外部动态链接库——纸牌

C语言功能强大的一个重要原因是因为具有丰富的库函数&#xff0c;例如字符串处理有string库&#xff0c;数学处理有math库。微软公司对程序库的支持又更进一步&#xff0c;提供了丰富的动态链接库。下面以微软操作系统内嵌的纸牌链接库为例写一个简单的小程序。 由于我们已经较…

netbeans7.4_使用NetBeans 7.4 beta提示进行更好的基于JUnit的单元测试

netbeans7.4在上一篇文章中 &#xff0c;我写了NetBeans 7.4 beta中提供的提示 &#xff0c;这些提示提高了开发人员避免Java异常处理带来的讨厌的运行时问题的能力。 在本文中&#xff0c;我将探讨如何使用NetBeans 7.4 beta提供的另外两个提示使单元测试在执行单元测试期间更…

Java锤子剪刀布大家应该都会玩“锤子剪刀布”的游戏: 现给出两人的交锋记录,请统计双方的胜、平、负次数,并且给出双方分别出什么手势的胜算最大。

题目描述&#xff1a; 大家应该都会玩“锤子剪刀布”的游戏&#xff1a;现给出两人的交锋记录&#xff0c;请统计双方的胜、平、负次数&#xff0c;并且给出双方分别出什么手势的胜算最大。 输入描述: 输入第1行给出正整数N&#xff08;<105&#xff09;&#xff0c;即双方…

【MATLAB函数系列】fskmod函数参数及源代码详解

关注回复【fskmod】可看 在用matlab仿真生成fsk信号时&#xff0c;发现matlab库提供fskmod函数可以直接生成fsk信号&#xff0c;但生成的信号为复信号&#xff0c;于是查看其源码&#xff0c;结果对其原理看了好久都没明白&#xff0c;在网上查询资料也少得可怜&#xff0c;唯一…

Java数字黑洞给定任一个各位数字不完全相同的4位正整数,如果我们先把4个数字按非递增排序,再按非递减排序,然后用第1个数字减第2个数字,将得到 一个新的数字。一直重复这样做,我们很快会停在有“数字

题目描述&#xff1a; 给定任一个各位数字不完全相同的4位正整数&#xff0c;如果我们先把4个数字按非递增排序&#xff0c;再按非递减排序&#xff0c;然后用第1个数字减第2个数字&#xff0c;将得到 一个新的数字。一直重复这样做&#xff0c;我们很快会停在有“数字黑洞”之…

Java Spring Security示例教程中的2种设置LDAP Active Directory身份验证的方法

LDAP身份验证是全球范围内最流行的企业应用程序身份验证机制之一&#xff0c;而Active Directory &#xff08;Microsoft针对Windows的LDAP实现&#xff09;是另一种广泛使用的LDAP服务器。 在许多项目中&#xff0c;我们需要通过登录屏幕中提供的凭据使用LDAP对活动目录进行身…

C语言文件读写操作之换行符处理

getc&#xff0c;fgetc&#xff1a; -1代表结束符&#xff0c;不但能接收键盘输入的内容&#xff0c;也能从文件中读取一个字符&#xff1b; getchar() 等价于 getc(stdin); putc,fputc: putchar© 等价于 putc(c,stdout); 文件操作 t模式 与 b模式&#xff1a; windo…

Java月饼月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需 求量,请你计算可以获得的最大收益是多少。注意:销售时允许取出一

题目描述&#xff1a; 月饼是中国人在中秋佳节时吃的一种传统食品&#xff0c;不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量&#xff0c;请你计算可以获得的最大收益是多少。 注意&#xff1a;销售时允许取出一部分库存。样例给出…

【各种信噪比联系与区别详解】实信号、复信号Es、N0、符号信噪比EsN0、带内信噪比、比特信噪比EbN0、SNR的含义及关系详解

引言 关于EsN0、EbN0与SNR三者关系的描述,在MATLAB帮助文档中搜索词条“AWGN Channel”有比较详细的描述,但有些细节没有讲清楚。 简单翻译解释一下,就是 SNR表示每个采样点的信号噪声比。EbN0表示每个比特能量与噪声功率谱密度的比值。EsN0表示每个符号能量与噪声功率谱密…

非科班Java尝试全国高校计算机能力挑战赛第三届计挑赛

写在前面&#xff1a;①大佬勿在意&#xff0c;小打小闹&#xff01;②若不决参加与否&#xff0c;希望下文可以给你帮助&#xff08;仅限程序设计赛项&#xff09;。 文章目录一、个人真实情况二、比赛最细简介三、最全参赛准备四、比赛时候遇到不会做怎么办&#xff1f;一、个…