VC绘图/游戏简易教程--16:设备上下文句柄(Windows 编程入门2)

教程总目录:https://www.codeabc.cn/bestans/post/concise-lesson-contents(里面包括VC下的graphics.h的配置方法)

注:学习本节前,请自备 MSDN,以便查阅 Windows GDI 函数。

EasyX的绘图函数最初是模仿的BGI的函数命名。为了让大家借此学习 Windows GDI 绘图,EasyX 增加了获取 HDC 句柄的功能。

对于 Windows GDI 中的绘图函数,很多都需要一个 HDC 句柄。我们用 GetImageHDC() 函数获取该句柄,然后就可以使用 Windows GDI 了。先看看例子吧:

#include <graphics.h>
#include <conio.h>

int main()
{

...

为什么没有 setviewport() 函数?用什么替代?

由于 windows 下的 viewport 概念和过去有了很大的区别,为了避免大家混淆,EasyX 从 2011-2-24 起取消了这个函数。

下面解释一下怎样用其他函数替代 setviewport 的功能。

先说说过去的 setviewport() 的功能:

  • 设置矩形区域
  • 将矩形区域左上角设置为原点坐标
  • 可以选择是否裁剪

对于设置裁剪,可以先定义一个区域,然后将该区域设置为裁剪区。区域的概念不仅局限于矩形,在 Windows GDI 函数中有很多定义区域的语句。下面代码定义一个矩形的区域,并设置为裁剪区:

 HRGN rgn = CreateRectRgn(100, 100, 200, 200);	// 定义矩形区域
 setcliprgn(rgn);			// 设置区域 rgn 为裁剪区
 DeleteObject(rgn);			// 不再使用 rgn,清理 rg
...

通过直接操作显存实现高速绘图和特殊效果(如逐渐变亮)

[开场白]

EasyX 从 2011-2-24 开始,支持了直接操作显存的功能。当然,这个显存并不是通常所说的显卡上的显存,而是用来显示窗口的内存,以及用来保存 IMAGE 对象的内存。为了有别于普通的内存,暂且叫这部分为显存吧。

传统的显存概念这里就不介绍了,这里只说一下在 EasyX 中怎么利用这个“显存”。

比如画点,传统的画点函数要考虑很多因素,比如坐标系、缩放、裁剪区等等,所以性能要差。要实现高速绘图,就需要一些更直接的方法,这就是 EasyX 实现直接操作显存功能的目的。

[理论知识]

在 EasyX 中,每个点用 4 个字节表示。对于 640 x 480 的绘图窗口,显存的大小为 640 x 480 x 4 = 307,200 x 4 = 1,228,800 字节。
一个 DWORD 类型正好四个字节,如果是 DWORD 类型的指针指向显存,那么用数组的形式,数组的下标范围是 0~307,199。

关于颜色有点需要注意的地方。平时我们用

...

同时检测多个按键和平滑按键处理

getch() 函数,用于返回用户输入的字符。当连续按键时,该函数返回第一个字符和第二个字符之间,默认有 0.5 秒的延时,并且之后的连续字符,默认是每秒钟 15 次输入。这两个数值可以在控制面板中设置。

如果需要平滑的按键输入,或者同时按下多个按键,就不能用 getch() 了,需要使用另一个 Windows API 函数:GetAsyncKeyState()。该函数原型如下:

SHORT GetAsyncKeyState(
	int vKey		// virtual-key code
);

vKey 是要检测的按键的虚拟键码,常用的如 VK_UP、VK_DOWN 等,分别表示方向键的上、下等。需要注意:对于 26 个字母的键码,可以直接写 'A'、'B'……,而不要写 VK_A、VK_B。数字键也是,请直接写 '0'、'1'……。全部的 256 种虚拟键码,请参考 MSDN

...

使用 getch() 获得方向键和更多的功能键

通常来说,getch() 可以返回用户键入的字符。对于一些小游戏,希望用方向键控制的时候,怎么做呢?

先看看 MSDN 的解释:

When reading a function key or an arrow key, _getch and _getche must be called twice; the first call returns 0 or 0xE0, and the second call returns the actual key code.

简单来说,当 getch() 返回 0 或 0xE0 时,就表示用户按了功能键,这时候需要再调用一次 getch()。第二次 getch() 返回的值表示功能键,比如:

72 表示 上
80 表示 下
75 表示 左
77 表示 右

更多的功能键码,可以自己试验得出。

具体到程序中的应用,请参考“俄罗斯方块”的源代码:http://www.easyx.cn/samples/

...

实现简单的询问对话框

游戏结束的时候,通常会简单的弹出一个对话框询问用户是否要重新来一句,如下图:

这篇文章就简单讲解一下这个功能如何实现。

首先,我们需要使用一个 Windows API 函数:MessageBox。该函数原型如下:

int MessageBox(
	HWND hWnd,          // handle to owner window
	LPCTSTR lpText,     // text in message box
	LPCTSTR lpCaption,  // message box title
	UINT uType          // message box style
);

第一个参数 hWnd 是指向父窗口的句柄,可以通过 EasyX 的函数 GetHWnd() 得到绘图窗口的句柄;

第二个参数 lpText 是要显示的字符串;

...

在游戏中播放音乐

游戏没有声音多单调。

这里做一个简单的范例,用 mciSendString 函数播放 MP3 格式的音乐,先看看代码吧:

// 编译该范例前,请把 music.mp3 放在项目文件夹中
// 发布时,请把 music.mp3 和编译的 exe 放在一起
// 编译环境:VC6~VC2017 + EasyX_20190314(beta)
//
#include <graphics.h>
#include <conio.h>
// 引用 Windows Multimedia API
#pragma comment(lib,"Winmm.lib")

void main()
{
	initgraph(640, 480);

	// 打开音乐
	mciSendString(_T("open music.mp3 alias mymusic"), NULL, 0, NULL);

	outtextxy(0, 0, _T("按任意键开始播放"));
...

程序的编写风格(代码格式)可以随心所欲

很多文章都阐述了编写程序时“代码格式”的重要性,甚至还有些硬性的规定。当然,这篇文章并不是否定格式的重要性,初学者甚至应该十分注重格式才对。

但是另一方面,编写程序还应该注重整体,通过代码格式展现出整体的结构。这里举一个例子,EasyX 网站有一个变幻线的源代码:
http://www.easyx.cn/samples/View.aspx?id=42

其中有几行代码是很长的,并且违反了很多条“代码格式”的规定,这里转过来这几行:

	// 判断顶点是否越界
	if		(m_head->pos.x < 0)			{ m_head->pos.x = -m_head->pos.x;				m_step.x =  rand() % MAXSTEP + 1;	}
	else if (m_head->pos.x >= WIDTH)	{ m_head->pos.x -= m_head-&g

...

学习编写一个完整的 Windows 应用程序

从 C 语言,到常见的 setup.exe 安装程序,究竟有多远?怎样实现双击 .xls 文件会自动启动 excel 并加载?这篇文章就解释这个问题。

请跟随以下步骤:

1. 编写应用程序。为了叙述方便,暂且叫 test.exe 吧。通常 test.exe 中会用若干 scanf 或 cin 来读取用户输入的参数,并根据参数来执行程序,这是大家在学习 C 语言的过程中见到的。

2. 要规定程序所需数据的格式。例如,word 需要使用 .doc 格式的数据,photoshop 需要使用 .psd 格式的数据。不管这些数据文件是文本的还是二进制的,只有格式确定下来,程序才能按照原定意图解释数据。数据格式就是将用户输入的全部数据都放进一个文件中,然后将 scanf 语句修改为读取该文件(读文件的程序部分请参考相关书籍)。

3. 设置文件关联。在上一步我们规定了一个我们需要的数据格式,现在给这个格式的文件自定一个扩展名,比如叫 .abc,然后双击该文件,Windows 会提示用户选择打开该类型的程序,我们选择步骤 1 编写的

...