一步步拓展程序,实现文字在背景上的移动
本文适用读者:已经学完 C 语言基本知识,并对 EasyX 略有了解。
本文范例实现的功能:文字在背景上移动,并且不破坏背景。
本文范例编译平台:VC6 / VC2010 + EasyX 20140321(beta)。
本文并不直接写出最终代码,而是从最开始绘制背景,一步步拓展到我们需要的功能。希望这个过程能对初学者有所启发。
一、绘制背景
为了实现文字移动而不破坏背景的效果,我们先画个背景。做法很简单,就是画一连串的竖线,并且每次变换颜色,这样就能弄一个渐变色的背景,代码如下:
#include <graphics.h>
#include <conio.h>
// 主函数
void main()
{
// 创建绘图窗口
initgraph(640, 480);
// 绘制背景
for(int i = 0; i < 640; i++)
{
setlinecolor(i);
line(i, 0, i, 479);
}
// 退出
_getch();
closegraph();
}
这个效果不够好看,因为这样通过 setlinecolor 设置一个整数,只能设置 RGB 颜色的最后表示红色的一个字节,所以我们只能看到红色的渐变色。于是,我们考虑用 HSL 颜色模型实现一个彩虹渐变色。HSL 模型的分量如下:
- H 是 HSL 颜色模型的 Hue(色相) 分量,0 <= H < 360。
- S 是 HSL 颜色模型的 Saturation(饱和度) 分量,0 <= S <= 1。
- L 是 HSL 颜色模型的 Lightness(亮度) 分量,0 <= L <= 1。
只需要将前面代码的颜色部分修改一下就好,将:
setlinecolor(i);
修改为:
setlinecolor( HSLtoRGB(i, 1, 0.5) );
修改之后,尝试编译,发现一个警告:
warning C4244: 'argument' : conversion from 'int' to 'float', possible loss of data
因为 HSLtoRGB 要求参数是 float 类型,但是传入了一个 int 类型的参数 i,所以 VC 给了警告。这个容易解决,强制转换一下:
setlinecolor( HSLtoRGB((float)i, 1, 0.5) );
(这里提醒一下初学者:警告虽然不影响程序执行,但是务必要修改,切记切记!)
为了将之后文字的效果突出,我们再把亮度分量降低:
setlinecolor( HSLtoRGB((float)i, 1, 0.25) );
好了,这回背景效果满意了。
二、添加文字移动效果
现在有背景了,再实现文字的移动。这个不难,用一个循环加 Sleep 延时,实现文字从左到右的移动,代码如下:
#include <graphics.h>
#include <conio.h>
// 主函数
void main()
{
// 定义字符串
TCHAR s[] = _T("测试文字");
// 创建绘图窗口
initgraph(640, 480);
// 绘制背景
for(int i = 0; i < 640; i++)
{
setlinecolor( HSLtoRGB((float)i, 1, 0.25) );
line(i, 0, i, 479);
}
// 设置文字效果
settextcolor(WHITE); // 设置文字颜色为白色
setbkmode(TRANSPARENT); // 设置文字背景为透明色
// 绘制移动的文字
for(int j = 0; j < 600; j++)
{
outtextxy(j, 100, s);
Sleep(20);
}
// 退出
_getch();
closegraph();
}
这个代码在文字移动后没有擦掉文字,所以每次的文字输出都重叠在一起,看不出来效果。但是,如果用黑色矩形“覆盖”文字实现擦掉文字,那就会使文字移动后的区域变成黑色,达不到不破坏背景的要求。
三、重绘全部场景实现不破坏背景的文字移动
这里先尝试一个霸道点的办法:每次文字移动前,将背景重新画一遍,以便擦掉上次的文字,代码如下:
#include <graphics.h>
#include <conio.h>
// 主函数
void main()
{
// 定义字符串
TCHAR s[] = _T("测试文字");
// 创建绘图窗口
initgraph(640, 480);
// 设置文字效果
setbkmode(TRANSPARENT); // 设置文字背景为透明色
// 绘制移动的文字
for(int j = 0; j < 600; j++)
{
// 绘制背景
for(int i = 0; i < 640; i++)
{
setlinecolor( HSLtoRGB((float)i, 1.0, 0.25) );
line(i, 0, i, 479);
}
settextcolor(WHITE); // 设置文字颜色为白色
// 绘制文字
outtextxy(j, 100, s);
Sleep(20);
}
// 退出
_getch();
closegraph();
}
以上代码虽然实现了效果,但是由于每次都花大量时间绘制背景,导致闪烁厉害。这里用批量绘图方法将所有绘图一次性显示出来,以解决闪烁的问题。代码如下:
#include <graphics.h>
#include <conio.h>
// 主函数
void main()
{
// 定义字符串
TCHAR s[] = _T("测试文字");
// 创建绘图窗口
initgraph(640, 480);
// 开启批量绘图模式
BeginBatchDraw();
// 设置文字效果
setbkmode(TRANSPARENT); // 设置文字背景为透明色
// 绘制移动的文字
for(int j = 0; j < 600; j++)
{
// 绘制背景
for(int i = 0; i < 640; i++)
{
setlinecolor( HSLtoRGB((float)i, 1.0, 0.25) );
line(i, 0, i, 479);
}
settextcolor(WHITE); // 设置文字颜色为白色
// 绘制文字
outtextxy(j, 100, s);
FlushBatchDraw(); // 绘制
Sleep(20);
}
// 退出
EndBatchDraw(); // 关闭批量绘图模式
_getch();
closegraph();
}
这段代码已经实现了我们要求的。不过,由于每次都重新绘制背景,代价有点大。下面再尝试另一个办法。
(注:这个“代价”也是相对而言的。对于现在流行的许多游戏,很多绚丽的效果都会导致全屏幕的改动,因此与其恢复局部,不如全部重绘。)
四、恢复局部背景实现不破坏背景的文字移动
这个方法在绘制文字前,先把将要破坏的背景临时保存起来,等需要擦掉文字的时候,再把保存的局部背景显示出来。这里涉及到两个语句:
- getimage(); // 保存指定区域的图像
- putimage(); // 在指定位置显示图像
两个函数的使用可以在 EasyX 的在线帮助中找到,点击函数名称可以直接跳转,这里不再多说。看代码:
#include <graphics.h>
#include <conio.h>
// 主函数
void main()
{
// 定义字符串
TCHAR s[] = _T("测试文字");
// 创建绘图窗口
initgraph(640, 480);
// 初始化
int w = textwidth(s); // 获取字符串占用的宽度
int h = textheight(s); // 获取字符串占用的高度
IMAGE tmp; // 定义临时对象,保存被文字破坏的背景
// 绘制背景
for(int i = 0; i < 640; i++)
{
setlinecolor( HSLtoRGB((float)i, 1.0, 0.25) );
line(i, 0, i, 479);
}
// 设置文字效果
settextcolor(WHITE); // 设置文字颜色为白色
setbkmode(TRANSPARENT); // 设置文字背景为透明色
// 绘制移动的文字
for(int j = 0; j < 600; j++)
{
// 保存区域
getimage(&tmp, j, 100, w, h);
// 绘制文字
outtextxy(j, 100, s);
Sleep(20);
// 恢复区域
putimage(j, 100, &tmp);
}
// 退出
_getch();
closegraph();
}
这个代码看起来没问题了吧。
五、最后一步:封装代码
虽然前面的代码没有问题,但是代码间的耦合度有点大,我们希望有一种清晰明了的方式将我们实现的功能封装起来,方便以后使用。封装的方式有很多,封装的格式也有很多,这里说一个最简单的方法:将代码写到一个函数中,如下:
#include <graphics.h>
#include <conio.h>
// 文字输出(不破坏背景)
// 参数:
// x, y: 输出文字的位置
// s: 输出文字的指针。如果指针为 NULL,表示恢复背景。
void myouttextxy(int x, int y, LPCTSTR s)
{
static IMAGE tmp; // 用来保存被文字覆盖的区域背景
if (s == NULL)
{
// 恢复区域
putimage(x, y, &tmp);
}
else
{
int w = textwidth(s); // 获取字符串占用的宽度
int h = textheight(s); // 获取字符串占用的高度
// 保存区域
getimage(&tmp, x, y, w, h);
// 文字输出
outtextxy(x, y, s);
}
}
// 主函数
void main()
{
// 定义字符串
TCHAR s[] = _T("测试文字");
// 创建绘图窗口
initgraph(640, 480);
// 绘制背景
for(int i = 0; i < 640; i++)
{
setlinecolor( HSLtoRGB((float)i, 1.0, 0.25) );
line(i, 0, i, 479);
}
// 设置文字效果
settextcolor(WHITE); // 设置文字颜色为白色
setbkmode(TRANSPARENT); // 设置文字背景为透明色
// 绘制移动的文字
for(int j = 0; j < 600; j++)
{
// 绘制文字
myouttextxy(j, 100, s);
Sleep(20);
// 擦掉文字
myouttextxy(j, 100, NULL);
}
// 退出
_getch();
closegraph();
}
至此,大功告成!
如果其中有哪个步骤讲述的不很清楚,欢迎到百度 EasyX 贴吧交流!
添加评论
取消回复