Windows下的C和C++编程(免费)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Windows下的C/C++编程
对于任何一种编程语言,都会以一个最基本的程序入门,就是大家都很熟悉的Hello World。
那么,我们也从这里开始吧。
最简单的Windows程序
1)TC中的Hello World
相信对于下面的Turbo C 程序大家都不会陌生,这就是最简单的TC经典入门程序:
#include < stdio.h >
main()
{
prinft(“ Hello World !”);
}
这个程序将在DOS模式中输出“Hello World”。
2)最简单的Windows下的Hello World
其实,下面的程序并不是标准的Windows应用程序,它不产生传统意义上的窗口,而只是跳出一个对话框。
让我们详细的操作一下:
首先从开始菜单中选择Microsoft Visual C++ 6.0(其他版本也行);(见图1-1)
图1-1 Microsoft Visual C++ 6.0
然后通过File菜单中的new选项新建一个工程。
在Projects选项中选择Win32 Application;然后指定工程名,这里是:Hello World;再指定存储路径,这里是
D:\DirectX8.0游戏编程\;最后点击OK。
(见图1-2)
图1-2 Projects选项Win32 Application
下面出现以下窗口,选择An empty project;点击OK。
(见图1-3)
图1-3 An empty project
这样,就创建了一个空的工程,工程名为Hello World,存储路径为D:\DirectX8.0游戏编程\下的Hello World文件夹。
下一步,在空的工程中加入C语言的源程序文件。
在Visual C++ 6.0的File 菜单中选择new选项;在弹出的File菜单中选择C++ Source File;选中Add to project 选框;选中加入到那个工程中,这里是Hello World,就是刚才创建的工程;填写文件,这里是Hello World,下面是工程的存放路径;最后,选择OK。
(见图1-4)
图1-4工程的存放路径
这样,就在刚才的空工程中加入了一个文件,可以在Vc的FileView框中看到它,右边是它的编辑框,在那里编写源程序。
(见图1-5)
图1-5Vc的编辑框
那么,加入如下代码:
#include<windows.h>
int WINAPI WinMain ( HINSTANCE hInstance ,
HINSTANCE hPrevInstance ,
LPSTR lpcmdLine ,
int nCmdShow)
{
MessageBox ( NULL , TEXT("Hello World!")
TEXT("My 1st Program") , MB_OK) ;
return 0 ;
}
然后,选择执行,或者直接按F5键。
就会得到如下的执行结果:一个弹出的对话框(见图1-6)。
图1-6 Hello World弹出框
3)程序分析
和TC程序比较,这个Windows程序基本相同。
包含了头文件,有一个主函数,在主函数中调用了一个系统函数,主函数返回一个整数值。
每个语句以分号结束,复合语句用大括号包含。
下面对它进行详细的分析。
4)程序的参数
下面,对上面的程序的参数从上至下进行分析。
◆#include<windows.h>
程序包含了一个头文件windows.h ,基本上所有的Windows程序都会包含这个文件。
它定义了Windows的基本数据类型、数据结构、系统函数等。
◆int WINAPI WinMain
Tc中的程序是以main函数为程序入口的,而Windows程序是以WinMain函数为入口的,这个函数的定义如下:
函数原型
int WINAPI WinMain (
HINSTANCE hInstance ,
HINSTANCE hPrevInstance ,
LPSTR lpcmdLine ,
int nCmdShow);
第一个参数hInstance是当前实例句柄,为一个整数,用于标识窗口、程序等。
第二个参数hPrevInstance 被置为NULL。
第三个参数lpcmdLine是运行程序的命令行。
第四个参数nCmdShow用于指定程序窗口最初的显示模式,可以在初始是指定是正常还是最大化等。
WinMain函数返回一个整数。
WINAPI用于指定调用约定,具体在WINDEH.H中定义。
◆MessageBox
MessageBox是一个Windows API函数,用于弹出一个对话框,显示一些简短的信息。
定义如下:
函数原型
MessageBox ( HWND hWnd ,
LPCTSTR lpText ,
LPCTSTR lpCaption ,
UINT uType ) ;
第一个参数hWnd是一个窗口句柄,表明对话框所属的窗口。
第二个参数lpText是一个字符串,表明需要显示的信息。
TEXT是一个宏。
第三个参数lpCaption是一个字符串,表明对话框的标题。
第四个参数uType用于指定对话框中的按钮和图标。
(见表1-1)各种按钮和图标类型可以同时使用,用“ | ”连接,如“MB_OK | MB_ERROR”。
类型按钮图标
MB_OK 确定-----
MB_OKCANCEL 确定和取消-----
MB_YESNO 是和否-----
MB_INFORMATION ----- 提示 MB_WARNING ----- 警告 MB_ERROR
-----
出错
表1-1按钮和图标
Windows 应用程序
1)真正的Hello World
上一节中,实现了Windows 弹出一个对话框,显示了一个简短的信息“Hello World ”。
但是,这和平时看到的大多数Windows 应用程序不太一样,没有窗口,没有最大化、最小化按钮等。
虽然如此,它还是相当重要的,特别在显示出错或提示信息时尤为简便。
下面,来看一看一个真正的Windows 窗口程序。
同样,创建一个新的工程,这里是Hello World Windows (见图1-7):
图1-7Hello World Windows 工程
试一试
试试让系统弹出不同的对话框
然后加入一个新的C++源文件(其实可以看成是C源文件,只是换了后缀名),文件名为Win Hello World。
下面是程序的代码。
注意
要利用空格和回车,还要注意程序代码的缩进,这样才能保证代码的可读性,同时也会方便修改:
#include<windows.h>
LRESULT CALLBACK WndProc (HWND , UINT ,
WPARAM , LPARAM ) ;
int WINAPI WinMain ( HINSTANCE hInstance ,
HINSTANCE hPrevInstance,
LPSTR lpcmdLine , int nCmdShow)
{
static TCHAR szAppName[ ] = TEXT( " HelloWorld " ) ;
static TCHAR szClassName[] = TEXT( " HelloWorldClass " );
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 = szClassName;
if ( ! RegisterClass ( & wndclass ) )
{
MessageBox ( NULL , TEXT ( " This program
requires Windows NT !") ,
szAppName , MB_ICONERROR ) ;
return 0 ;
}
hwnd = CreateWindow ( szClassName ,
TEXT("My Hello World to Windows Program") ,
WS_OVERLAPPEDWINDOW ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT ,CW_USEDEFAULT ,
NULL ,NULL ,
hInstance ,
NULL ) ;
ShowWindow (hwnd , nCmdShow ) ;
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 ;
PAINTSTRUCT ps ;
RECT rect ;
switch ( message )
{
case WM_CREATE :MessageBox( NULL , " Hello
World!" , "Hello World " , MB_OK) ;
MessageBeep ( MB_OK ) ;
return 0 ;
case WM_PAINT:hdc = BeginPaint ( hwnd , &ps ) ;
GetClientRect ( hwnd , &rect ) ;
DrawText ( hdc , TEXT ( "Hello , Windows !") ,
-1 , &rect ,
DT_SINGLELINE | DT_CENTER
| DT_VCENTER) ;
EndPaint ( hwnd , &ps ) ;
return 0 ;
case WM_DESTROY :
PostQuitMessage ( 0 ) ;
return 0 ;
}
return DefWindowProc ( hwnd , message , wParam , lParam ) ;}
运行这个程序,将得到如下结果:
首先弹出一个对话框,并且伴随一个很熟悉的“当”的一声(见图1-8):
图1-8 Hello World弹出框2
点击确定以后,关闭这个对话框,打开了一个Windows窗口(见图1-9):
图1-9 Windows窗口
这个窗口大家因该很熟悉了吧。
一个标准的Windows窗口,可以最大化、最小化、拖动等。
好,接下来仔细分析一下整个程序的代码。
当然,没有必要去记住这些所有的代码,基本上也没有人记得住,以后在使用的时候,可以直接复制过去,然后作相应的修改就行了。
2) Windows类
在程序分析之前,先说明一些基本的新的东西。
◆数据类型
在这里,出现了一些在TC中没有见过的数据类型。
这些是Windows程序的一些常见数据类型。
基本上,它们的命名与相应的英文含义相同,或是在前面加上表示类型的字母,如32位的类型加上L,不过部分因为Windows版本的变迁而失去了原来的意义,只是沿用以前的名字。
详细的说明以后会慢慢熟悉,下面列举了一些常用的前缀和表达的意义(见表1-2):
前缀表示的数据类型
c char
by BYTE (无符号字符)
n short
x、y int (表示x坐标和y坐标)
cx、cy int (表示x方向长度,y方向长度)
w WORD
l LONG
p pointer (指针)
sz string ended with zero (以0结尾的字符串)
s string
dw DWORD
fn Function (函数)
b BOOL (int)
表1-2常用的前缀
◆函数
这里,除了MessageBox之外,还有一些Windows API函数,下面列表说明(见表1-3):
API函数作用
LoadIcon 加载程序图标
LoadCursor 加载鼠标
GetStockObject 获取图形设备对象
RegisterClass 注册窗口类
CreateWindow 创建窗口
ShowWindows 显示窗口
UpdateWindow 刷新窗口
GetMessage 从消息队列中获取消息
TranslateMessage 转换消息
DispatchMessage 发送消息
MessageBeep 发出系统声音
BeginPaint 开始在用户区绘制
GetClientRect 取得用户区
DrawText 显示文本
EndPaint 结束绘制
PostQuitMessage 插入推出消息
DefWindowProc 执行缺省的消息处理
表1-3 Windows API函数
好,现在来看看Windows类。
Windows窗口类规定了窗口的一些基本特征,如显示方式、名称、风格等等。
创建的窗口是属于窗口类的一个具体的实例,由窗口类可以创建多个窗口。
就像房子的窗户一样,窗户类定义了窗户是用来采光、通风的、有一个外框、可以用一些材料封闭等;而窗户就是一个个窗户类的实例,当然,窗户有很多种,但是它们的基本特征就是上面所说得。
就像在房子上开一个窗户要准备一些材料、工具一样,在创建一个Windows
窗口之前有一系列准备工作,要为窗口注册一个窗口类,告诉Windows希望创建一个什么样的窗口。
RegisterClass函数用于注册窗口类,它的定义如下:
函数原型
ATOM RegisterClass(const WNDCLASS *lpWndClass);
如果调用成功,返回一个非零值,ATOM数据类型保证其返回的值在系统中是唯一的;调用失败,返回零。
RegisterClass函数的参数只有一个,是一个指向WNDCLASS数据类型的指针,WNDCLASS数据类型是一个结构,Windows有一个通用的定义:typedef struct _WNDCLASS
{
UNIT style ;
WNDPROC lpfnWndProc ;
Int cbClsExtra ;
Int cbWndExtra ;
HINSTANCE hInstance ;
HICON hIcon ;
HCURSOR hCursor ;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName ;
} WNDCLASS ;
看到这里大家因该想到些什么了吧。
对了!在创建窗口之前要注册窗口,在注册窗口之前,就是要填充上面这个结构。
下面,一项项看:
◆首先,定义了一个WNDCLASS结构:
WNDCLASS wndclass ;
下面就填充这个结构
◆wndclass. style = CS_HREDRAW | CS_VREDRAW ;
表示当窗口的水平方向或垂直方向的大小发生变化时,完全刷新窗口,
这使得Hello , Windows !一直位于窗口的正中心。
◆wndclass. lpfnWndProc = WndProc ;
设置窗口函数为WndProc。
这个函数处理窗口里的所有消息。
◆wndclass. cbClsExtra = 0 ;
目前不使用,设为0 。
◆wndclass. cbWndExtra = 0 ;
目前不使用,设为0 。
◆wndclass. hInstance = hInstance ;
程序的句柄,也就是标识。
◆wndclass. hIcon = LoadIcon ( NULL , IDI_APPLICATION) ;
设置图标,就是程序的图标,比如Windows记事本的图标就是一个小的翻开的记事本。
◆wndclass. hCursor = LoadCursor ( NULL , IDC_ARROW) ;
设置鼠标,比如说游戏中的鼠标往往是不同形状的。
◆wndclass.hbrBackground =
( HBRUSH ) GetStockObject ( WHITE_BRUSH ) ;
指定窗口背景色。
◆wndclass. LpszMenuName = NULL ;
指定窗口菜单,置为NULL表示不需要菜单。
◆ wndclass. lpszClassName = szClassName;
指定一个类名。
填充完这个结构后,就要注册窗口,而且要对返回值进行检验:
if ( ! RegisterClass ( & wndclass ) )
{
MessageBox ( NULL , TEXT ( " This program
requires Windows NT !") ,
szAppName , MB_ICONERROR ) ;
return 0 ;
}
注册成功,就可以创建窗口了。
3)创建窗口
好了,既然决定了窗户的样子,就可以照着作了。
当然,还要决定窗户的位置等信息,这只要调用CreateWindows函数就可以了:
hwnd = CreateWindow ( szClassName ,
TEXT("My Hello World to Windows Program") ,
WS_OVERLAPPEDWINDOW ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
NULL ,
NULL ,
hInstance ,
NULL ) ;
这里,CreateWindow函数又出现了11个参数,来看看这些参数有些什么作用(见表1-4),表中的参数从上至下的对应上面CreateWindow函数的参数顺序:
参数作用
szClassName 字符串,表示窗口类名称
TEXT("My Hello World to Windows
字符串,用于显示窗口标题栏中的字Program")
WS_OVERLAPPEDWINDOW 显示风格
CW_USEDEFAULT 窗口左上x坐标,这里用默认值
CW_USEDEFAULT 窗口左上y坐标,这里用默认值
CW_USEDEFAULT 窗口右下x坐标,这里用默认值
CW_USEDEFAULT 窗口右下y坐标,这里用默认值
NULL 父窗口句柄,NULL表示最高级
NULL 菜单句柄,NULL表示没有
hInstance 应用程序实例句柄
NULL 创建参数,这里置为NULL
表1-4 CreateWindow函数参数
这里说明一下,WS_OVERLAPPEDWINDOW是标准窗口风格,包括一个标题栏、窗口图标、窗口标题、最小化按钮、最大化按钮、关闭按钮、可调边框。
下面是WS_OVERLAPPEDWINDOW的定义。
#define WS_OVERLAPPEDWINDOW(WS_OPVERLAPPED |
WS_CAPTION |
WS_SYSMENU |
WS_THICKFRAME |
WS_MINIMIZEBOX |
WS_MAXIMIZEBOX )试一试
试试替换成其中的几项,看看有什么效果。
CreateWindows函数创建个窗口,系统为这个窗口分配一定的资源,下一步就是在屏幕下显示了,也就是调用ShowWindow 函数:
ShowWindow (hwnd , nCmdShow ) ;
函数的第一个参数hwnd表示窗口的句柄,也就是这个窗口在系统中的唯一标识。
第二个参数nCmdShow是显示参数,用以指定窗口初始化时的状态,决定是最小化、最大化还是常规显示。
如果要自己设置初始状态,可以参照下面的表来进行(见表1-5):
参数作用
SW_SHOWNORMAL 按常规显示窗口并激活
SW_SHOWMAXIMIZED 最大化显示窗口并激活
SW_ SHOWMINIMIZED 最小化显示窗口并激活
SW_SHOWMININOACTIVE 最小化显示窗口,不激活
表1-5 nCmdShow显示参数
接下来,调用UpdateWindow函数,通过窗口函数WndProc进行窗口的绘制:
UpdateWindow ( hwnd ) ;
4)主循环
在UpdateWindow ( hwnd ) 语句之后,有一段循环语句,这就是Windows程序的消息循环。
在这里,指定了Windows程序从消息队列中取数据的方式和事件与消息的转换方式。
所谓的消息循环可以这样理解:比如说,有时当连续按键盘时,屏幕上并不是同步显示,而是有一段时间延迟。
消息循环就是当触发一定的事件时(比如按键盘),主循环将它转换成消息,也就是Windows可以识别的信息,放入消息队列,等待系统以一定的方式进行读取。
下面的代码就是负责接受消息,并进行转换和发送,真正处理消息的函数稍后说明。
while ( GetMessage ( & msg , NULL , 0 , 0 ) )
{
TranslateMessage ( &msg ) ;
DispatchMessage ( &msg) ;
}
GetMessage有4个参数,用一个表格来说明它们的作用(见表1-6):
参数说明
msg MSG类型,就是Windows消息结构
NULL 窗口句柄,NULL表示接受自己创建窗口的所用消息
0 提取消息的最小索引值,常与下一个参数同时置0
0 提取消息的最大索引值,置0表示提取所用消息
表1-6 GetMessage参数
GetMessage ( & msg , NULL , 0 , 0 )语句有一个返回值,当不为WM_QUIT (退出)时返回一个非零值,这时while 循环为1,执行内部的语句;为WM_QUIT时返回一个零值,于是跳出while 循环,执行推出程序。
当GetMessage返回一个非零值时,执行while 循环内的语句,将消息传给Windows,进行转换:
TranslateMessage ( &msg ) ;
并由Windows发送到窗口函数,在这个程序中是WndProc函数:
DispatchMessage ( &msg) ;
所有的流程可以通过下面的图来表示(见图1-10):
图1-10 Windows程序流程
5)事件处理
好,现在清楚了WinMain函数的工作流程,那就进入到窗口函数中,看看WndProc函数。
从上面的流程图可以看出,这里,窗口函数处理所用的Windows消息,也就是说,这将是程序的核心,而WinMain函数仅仅是搭建了一个基本的框架。
看看窗口函数的定义:
函数原型
LRESULT CALLBACK WndProc ( HWND hwnd ,
UINT message ,
WPARAM wParam ,
LPARAM lParam );
这里有四个参数。
第一个是指窗口句柄,我们因该很熟悉了。
第二个是消息标识符,每个消息对应一个唯一的标识符。
第三、第四个参数为32位的消息参数,其值根据消息的不同而不同。
消息处理语句一般是由switch语句实现:
switch ( message )
{
case WM_CREATE :……………;
return 0 ;
case WM_XXXXX:………………;
GetClientRect ( hwnd , &rect ) ;
return 0 ;
…………………………………….
}
return DefWindowProc ( hwnd , message , wParam , lParam ) ;
所有的消息处理之后,返回0;如果不经由switch分支语句处理的消息,交由DefWindowProc函数处理,这是Windows默认的消息处理函数。
这里,switch语句中出现了三个分支,分别处理三类消息。
◆WM_CREATE
初始消息,表示窗口显示前进行的处理。
在这里,我们让它弹出了一个对话框,并发出一个系统声音。
◆WM_PAINT
WM_PAINT消息以BeginPaint函数开始,以EndPaint 函数结束。
这个消息处理部分使得系统可以根据变化及时更新用户区。
比如,如果将另一个窗口激活,可能会覆盖这个窗口,原窗口中得Hello Windows 就不可见了,当我们从新激活窗口是,这部分负责重新绘制窗口,使得窗口有效。
下面看看里面得语句:
GetClientRect ( hwnd , &rect ) ;
这个语句将获取用户区域的大小、位置等信息。
&rect是一个指向RECT 结构的指针,这个结构由四个Long类型数据组成,分别表示一个矩形区域的左上、右下点的坐标。
DrawText ( hdc , TEXT ( "Hello , Windows !") ,
-1 , &rect ,
DT_SINGLELINE | DT_CENTER
| DT_VCENTER) ;
这个语句就是在窗口正中间显示Hello Windows的语句。
参数描述见下表(见表1-7):
参数说明
hdc 由BeginPaint函数返回的显示设备描述
句柄
TEXT ( "Hello , Windows !") 显示的字符串
-1 输出的字符数,-1表示字符串以0结尾
&rect 实际用户区
DT_SINGLELINE | DT_CENTER |
DT_VCENTER
显示方式,对应为:同一行,水平居中,垂直居中
表1-7 DrawText参数
◆WM_DESTROY
指示Windows清除窗口,比如点击了关闭按钮。
PostQuitMessage ( 0 );
这个语句就是在消息队列中插入一个WM_QUIT消息,使得while循环的值为0,跳出主循环。
执行
return msg. wParam ;
并中止程序运行。
到这里,已经把一个最基本的Windows程序讲解完了。
其中的关键就是理解Windows消息循环的处理机制。
建议大家多思考一下,这对后面的游戏编程的理解有很大的帮助。
控制图标和光标
上面就是Windows程序的基本框架,而且也详细分析了每一个语句。
应为是基本框架,只要了解就可以了,所以在程序中没有加入注释。
以后的程序都是对这个基本框架的增减和修改,这就有必要加上注释了,这不仅保证了程序的可读性,让其他的人员可以有效地明白程序的作用,同时也方便自己,谁也不能保证在编写了几千行代码之后还清楚的记得前面的代码的作用,但是如果有注释的话,大多数人会马上想起来,大脑真是奇妙。
1)控制图标
图标就是在Windows中看见的各种各样的用以表示不同文件类型的图案。
基本上Windows为每种文件类型都指定一个图标,但是,大多数程序会改变这个设置,把图标换成自己个性化的图案。
在上一节中,完成了Hello World Windows程序,系统自动的对它进行编译,生成的 .EXE 文件就存放在Debug 子目录下。
它的图标就是Windows系统默认的图标(见图1-11)。
图1-11系统默认的图标
现在,来改变这个文件的图标。
首先,新建一个工程,命名为Hello World Icon,其他步骤就像上一节一样,不过可以把WM_CREATE 消息中的MessageBox和MessageBeep函数删了,它对我们这一节的程序没什么用处,但是return语句要保留。
下面,通过File菜单的New选项,创建一个资源文件,就是在弹出的对话框中选中Resource Script,选中Add to project 选框,这里将这个Resource Script 文件命名为Resource,点击OK按钮(见图1-12):
图1-12 Resource选项
这样,在工程中加入了一个后缀名为 .rc的文件,系统同时为他创建了一个 .h 文件,我们可以暂时不去理会它,一切交与系统处理。
下一步,点中在左边的窗口中Resource.rc ,右边将出现一个文件夹样的图标,点中它,然后点鼠标右键,就会弹出一个选项窗口,选取Insert选项(见图1-13):
图1-13Insert选项
然后,又会弹出一个窗口,选择Icon,也就是图标文件,再点击New按钮,新建一个图标文件(见图1-14):
图1-14新建图标文件
这时,就可以利用Microsoft Visual C++ 6.0来绘制想要的图标了,这里简单的画了一个Hello的字样(见图1-15):
图1-15绘制图标文件
当然,也可以用其他的绘图软件进行图标的编辑,网上有专门的图标编辑软件,可以下载。
在后面的游戏编程中,还会使用比较有个性化的图标。
下一步,就是改变 .EXE 文件的图标了。
打开Hello World Icon.cpp 文件,对它进行一些修改:
首先,要加入Resource的头文件,注意加上注解。
还有,加上回车会比较容易找到:
#include<windows.h>
//包含资源文件
#include "resource.h"
LRESULT CALLBACK WndProc (HWND , UINT ,
WPARAM , LPARAM ) ;
int WINAPI WinMain ( HINSTANCE hInstance ,
HINSTANCE hPrevInstance,
LPSTR lpcmdLine , int nCmdShow)
{
………………………………………….
找到前面提到的有关图标的Windows API 函数,就是:
………………………………….
wndclass. hInstance = hInstance ;
wndclass. hIcon = LoadIcon ( NULL , IDI_APPLICATION) ;
wndclass. hCursor = LoadCursor ( NULL , IDC_ARROW) ;
………………………………….
把它修改为如下代码:
……………………………………
wndclass.hInstance = hInstance ;
//指定图标
wndclass.hIcon =
LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1)) ;
wndclass. hCursor = LoadCursor ( NULL , IDC_ARROW) ;
……………………………………….
其中IDI_ICON1是创建图标时Vc为它分配的一个标识;MAKEINTRESOURCE是一个宏,不用去了解太详细,毕竟这与本书的重点--游戏编程没有太大的关系,只要会用就可以了。
好,下面按F5键,编译运行程序,创建了一个窗口(见图1-16):
图1-16创建窗口
看出区别没有?没有!再仔细一点,在窗口的左上角。
出现了新的图标。
当然也可以不要,但是这里就不说了,这与本书的游戏无关。
所要实现的是下面这个效果,就是可执行文件(.EXE)文件的图标是自己设计的(见图1-17):
图1-17图标
试一试
好了,现在可以改变程序的图标了,那么,也就可以开始为自己的游戏设计一个自己喜欢的图标了,现在就可以动手了。
2)控制鼠标
这里说所得控制鼠标仅仅是指控制鼠标在窗口中的形状,更高级的控制在后面
的章节会讲到。
在Windows中,可以改变鼠标的样式,Windows提供了很多样式以供选择。
但是,用户可能会想在自己的程序中需要一个个性化的形状。
下面,就一步步的来吧:同样,它也属于一种资源,所以把它加入资源文件中。
选中左边的Resource 选项,然后点击根目录,点击鼠标右键,选取Insert选项(见图1-18):
图1-18鼠标右键Insert选项
然后在弹出的对话框中选取Cursor选项,点击New按钮(见图1-19):
图1-19Cursor选项
下一步,就同上一节一样,绘制需要的图案,这里画了一个十字架加个圈,就
当作是瞄准点吧(见图1-20)。
游戏中的可没有这么粗糙哦,这个以后再说。
图1-20绘制Cursor
好了,接下来就是改变代码了。
Resource头文件已经被包括在源文件中了,找到下面代码:
……………………………………
wndclass.hInstance = hInstance ;
//指定图标
wndclass.hIcon =
LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1)) ;
wndclass. hCursor = LoadCursor ( NULL , IDC_ARROW) ;
……………………………………….
把它修改为如下:
……………………………………
wndclass.hInstance = hInstance ;
//指定图标
wndclass.hIcon =
LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1)) ;
//指定光标
wndclass.hCursor =
LoadCursor(hInstance,MAKEINTRESOURCE(IDC_CURSOR1)) ;
其中IDC_CURSOR1是创建时系统指定的标识。
好了,按F5键吧,因该得到如下窗口(见图1-21):
图1-21窗口
只要鼠标移动到窗口中,就会变成这样的形状了。
如果不想要鼠标呢?在后面的游戏中也会有这个情况,那到时再说吧。
下面,来谈谈Windows下的图形编程,为以后的游戏编程作个铺垫。
Windows GDI 编程
GDI(Graphics Device Interface)就是图形设备接口,属于Windows系统的一个子系统,负责在显示设备上显示图形,如显示器、打印机、绘图仪等。
1)色彩基础
以前的显示器都是单色的,也就是只有黑、白两种颜色。
随着硬件、软件的发展,显示器的色彩已经有了极大的飞跃。
就目前而言,常用的色彩设置为增强色和真彩色。
所谓的单色,就是对应于屏幕的一个象素,只用一位来表示色彩。
0表示黑,1表示白。
这是由于早期电脑的各种资源决定的。
如果用4为表示一个象素点的色彩,就可以有24,也就是16种颜色。
这种色彩设置目前在家用电脑上已经很少见了。
256色,这因该是游戏爱好者很熟悉的了,也就是每象素用8位表示。
很多游
戏都是用256色表示的。
其中的原因就是256色对系统硬件的要求不高,同时也便于计算机软件处理。
详细的情况,在后面的章节会具体说明。
增强色,就是用16位表示一个象素点,大约可以表示65000多种色彩。
其中用于表示色彩的宏称为RGB,就是红、绿、蓝三种原色。
16位会被分为5位、6位、5位,分别用于表示RGB的三个分量(也就是表示三种颜色的不同值)。
为什么绿色分配了6位呢,这与人眼对绿色的敏感度与其他颜色不同有关。
同时,也存在为每种原色分配5位的设置,留下的最高位用于表示alpha值。
真彩色分为两种,24位和32位。
24位就是为每个原色分配8位,这样就可以表示大约1600万种色彩。
而32为,就是用8为表示alpha值,余下的24位分配给每个原色8位。
实际上,肉眼是无法区分16位色和24位色的具体区别。
16位色中,给出两个在计算机上相差不大的色彩,人的眼睛恐怕就难以区别了。
但是,我们可以从总体上看出区别。
同一幅图,用16位色和24位色分别表示,我们就可以看出,24位色的图效果从总体上说要好,至于具体怎么个好法,就难以描述了。
所以,在进行游戏编程的同时,也要视情况来进行色彩设置。
一味的抬高色彩设置,不但消耗硬件资源,还会加长程序处理色彩的时间,这样色彩效果相差不大,却造成了不必要的麻烦。
2)WM_PANIT消息
前面提到过WM_PANIT消息,这是一个相当重要的消息。
一般说来,当程序初始化或者窗口发生变化(大小、位置等)时,都会产生WM_PANIT消息,通知系统更新显示用户区。
需要更新显示的用户区可以是整个用户窗口,也可以是窗口的一部分,举个例子。
打开前面的Win Hello World 工程,找到里面的WM_PANIT消息:
……………………………………..
switch(message)
{
case WM_CREATE :
MessageBox(NULL,"Hello World!","Hello World ",MB_OK) ;
MessageBeep(MB_OK) ;
return 0 ;
case WM_PAINT :
hdc=BeginPaint(hwnd,&ps) ;
GetClientRect(hwnd,&rect) ;
DrawText(hdc,TEXT("Hello,Windows !"),-1,&rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
EndPaint(hwnd,&ps) ;
return 0 ;
case WM_DESTROY:
……………………………………………….
把它改成:
………………………………………………..
switch(message)
{
case WM_CREATE :
return 0 ;
case WM_PAINT :
hdc=BeginPaint(hwnd,&ps) ;
GetClientRect(hwnd,&rect) ;
DrawText(hdc,TEXT("Hello,Windows !"),-1,&rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
MessageBox(NULL,"Hello World!","Hello World ",MB_OK) ;
MessageBeep(MB_OK) ;
EndPaint(hwnd,&ps) ;
return 0 ;
case WM_DESTROY:
…………………………………………….
然后,重新编译运行,将得到如下结果(见图1-22)。
图1-22Windows窗口
在窗口上,弹出一个对话框,覆盖了窗口的一部分。
点击确定,对话框消失,显示完整的窗口。
这时,系统就不需要重新绘制整个窗口,只要重新绘制被对话框覆盖的窗口的一部分就可以了。
如果,你要改变窗口的状态(如大小、位置等),就会产生WM_PANIT消息,于是就会又弹出另外一个对话框(见图1-23)。
图1-23多对话框Windows窗口
当然,你关闭这些对话框时,如果改变了窗口的状态,又会产生新的对话框。
不过,你可以关闭整个窗口,这样所有的对话框都会消失。
注意,别试图弹出太多的对话框,虽然很有意思,但是如果弹出上百个对话框的话很可能会因为系统资源枯竭而死机,一些病毒正是这么干的。
3)文本输出
确切的说,文本输出不该归到GDI编程中,所以,这里不作详细的介绍,就以一个例子来讲解。
新建一个TXT文本文件(命名为Temp.TXT),至于里面要写些什么,那随个人喜好,这里就写HELLO WORLD。
新建一个空的工程(或者直接在原来的 Win Hello World 上修改),加入一个C++的源文件,将原来的 Win Hello World 中的代码拷贝过来。
如果愿意,可以改改WinMain函数的一些参数,这里就不作修改了。
然后,将WndProc函数修改成如下代码:
……………………………………………………….
LRESULT CALLBACK WndProc (HWND hwnd ,UINT message ,
WPARAM wParam ,
LPARAM lParam )
{
HDC hdc ;
PAINTSTRUCT ps ;
TCHAR szTemp[256] ; //字符缓冲区
FILE *fp ; //文件指针
static LOGFONT lgfont ; //自定义字体
static HFONT tempfont ;//自定义字体存储变量
switch(message)
{
case WM_CREATE :
//自定义字体
lgfont.lfHeight = 20 ;
lgfont.lfWidth = 20 ;
lgfont.lfEscapement = 900 ;
lgfont.lfOrientation = 600 ;
lgfont.lfWeight = FW_ULTRABOLD ;
lgfont.lfItalic = 1 ;
lgfont.lfUnderline = 1 ;
tempfont = CreateFontIndirect(&lgfont) ;
return 0 ;
case WM_PAINT :
hdc=BeginPaint(hwnd,&ps) ;
//打开文件
if((fp = fopen("Temp.txt","r")) != NULL)
{
int I ; //循环变量
int j=0 ; //字符控制变量
char ch ; //字符临时存放
//读取字符
while((ch = fgetc(fp)) != '\n' && ch != EOF) if(ch == '\t')
for(i=0 ; i<4 ; i++)。