动画基础之雪花飘飘
姓名:杨华、联络电话:13844144016、电子信箱:codehunter@sohu.com、通讯地址:长春人民大街吉林大学南岭校区管理学院9883班、邮编:130024。作者简介:本人是吉林大学管理信息系大四本科生,酷爱编程,非常喜欢贵刊。本人愿意将文章在网站上发布。
摘要:本文通过一个下雪的动画来讨论Windows下基于GDI的动画编程。通过本文的学习,读者应该对GDI动画编程有了基本的认识,可以开发一些简单的动画。
关键词:动画,GDI,VCL,C++Builder编程。
容易度:容易。
想必大家对动画编程一定很感兴趣,谁不想自己编一个游戏玩玩,那么好笔者在这就和大家一起来探讨一下动画编程的基础问题。本文旨在用简单明了的实例,来引导读者进行进一步的研究。在这我将用一个下雪的动画作为例子,并在Windows me 和C++Builder 5 环境下对其进行编译,如果你想获得本文的电子版及其源代码,那么请到http://codehunter.1yes.net网站上下载,或者访问《电脑编程与维护》杂志的网站http://www.comprg.com.cn。Come on…
一.动画的基础知识
所谓动画也就使一幅图像“活”起来的过程。使用动画可以清楚的表现出一个事件的过程,或是展现一个活灵活现的画面。动画是由动画对象和背景构成的。动画对象指的是可在屏幕上四处移动的图像,是能在视频屏幕上进行移动的整个位图的集合,在我们编写动画程序时,主要是实时处理动画对象。动画对象是整个过程的核心部分,一个动画对象可能是一个点,一个物体,一个动物,一个人,或是某种自然景象,而我们的实例就是一个自然景象(茫茫的雪花中站着一对情人,他们拥抱在一起,心里的话儿变成文字在雪中慢慢地向上移动,多浪漫呀!)。在我们的实例中卡通图片就是背景,而飘扬的雪花和移动的字幕就是动画对象。事实上产生动画的最好方法是放电影,每秒钟更换24张连续的画面,就可以蒙骗人们的肉眼,使人认为画面是连续的,而电脑动画的原理从本质上来说和放电影是一样的。应用程序以一定的规律快速连续绘制并显示一系列有关联的静止图像就产生了电脑动画。
二.下雪的实现原理
用VCL提供的TCanvas(画布对象)和TBitmap(位图对象)来实现动画很容易,本例中定义了两个内存位图ImgBK(用来存储背景图片),SrcImg(用来生成新的帧)。动画的形成流程看下图:
怎么样很容易吧!说白了就一句话:先将背景拷贝到SrcImg上,然后将连续变化好的文字和雪花画到SrcImg上,接着将画好的SrcImg拷贝到窗口上,循环往复,动画OK!有了以上的知识看下面的源代码就不成问题了。
三.部分源代码解析
头文件Unit1.h
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
#include <Graphics.hpp>
#include <jpeg.hpp>
//---------------------------------------------------------------------------
// 定义雪花结构
struct snow
{
TPoint sp;//雪花的坐标
TColor scolor;//雪花的颜色
unsigned int ssize;//雪花的大小
};
const int MAXSNOWS=250;//雪花总数
const int MAXSNOWSIZE=4;//雪花最大直径
class TForm1 : public TForm
{
__published: // IDE-managed Components
TTimer *Timer1;
TImage *Image1;
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall FormDestroy(TObject *Sender);
private: // User declarations
Graphics::TBitmap *ImgBK;//内存位图用来存储背景图片
Graphics::TBitmap *SrcImg;//内存位图用来生成新的帧
TStrings *StringList;//字幕字符串
snow snows[MAXSNOWS];//定义雪花数组
void InitSnows();//初始化雪花的函数
//画雪花的函数
void __fastcall DrawSnows(TCanvas *DCanvas,snow p);
//显示字幕的函数
void __fastcall DrawText(TCanvas *DCanvas,int x,int y);
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit.cpp文件
#include <vcl.h>
#pragma hdrstop
#include <stdlib.h>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
//设置窗口画布的画刷为透明
this->Canvas->Brush->Style=bsClear;
//设置窗口客户区的大小为图片大小
this->ClientWidth=Image1->Width;
this->ClientHeight=Image1->Height;
//设置定时器为30毫秒
Timer1->Interval=30;
//初始化雪花
InitSnows();
//初始化两个内存图片
ImgBK=new Graphics::TBitmap();
ImgBK->Assign(Image1->Picture->Graphic);
SrcImg=new Graphics::TBitmap();
SrcImg->Assign(Image1->Picture->Graphic);
//初始化字幕
StringList = new TStringList();
StringList->Add("★★★★★★★★★★★★★★★★");
StringList->Add("★★★ 程式猎人 ★★★");
StringList->Add("★ copyright codehunter 2001 ★");
StringList->Add("★★★★★★★★★★★★★★★★");
StringList->Add("");
StringList->Add("可爱的丽丽");
StringList->Add("看到这个程序你开心吗");
StringList->Add("我想你一定很开心");
…………..//其中可以加入很多文字
StringList->Add("☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆");
}
//---------------------------------------------------------------------------
//初始化雪花的函数
void TForm1::InitSnows()
{
randomize();
int temp;
for(int i=0;i<MAXSNOWS;i++)
{
snows[i].sp.x=rand()%this->Width;
snows[i].sp.y=-rand()%this->Height;
snows[i].ssize=rand()%MAXSNOWSIZE;
temp=rand()%255;
if(temp<230) temp=255;
snows[i].scolor=(TColor)RGB(temp,temp,temp);
}
}
//---------------------------------------------------------------------------
//画雪花的函数
void __fastcall TForm1::DrawSnows(TCanvas *DCanvas,snow p)
{
DCanvas->Brush->Color=p.scolor;
DCanvas->Pen->Style=psClear ;
DCanvas->Ellipse(p.sp.x,p.sp.y,p.sp.x+p.ssize,p.sp.y+p.ssize);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
this->Canvas->Draw(0,0,SrcImg);//将画好的帧画到窗口上
SrcImg->Canvas->Draw(0,0,ImgBK);//将背景拷贝到新帧上
static int y=this->Height;
//如果字幕全部移出屏幕,就将字幕移到屏幕底端
//注意:字幕行数决定750这个值
if(y<-750) y=this->Height;
y--;
DrawText(SrcImg->Canvas,SrcImg->Width,y);//将文本画在新的帧上
randomize();//随机数种子函数
int temp;
for(int i=0;i<MAXSNOWS;i++)
{
if(snows[i].sp.y>this->Height)
{
snows[i].sp.y=0;
snows[i].sp.x=rand()%this->Width;
}
snows[i].sp.y+=4;//雪花向下落的速度是4个像素
temp=rand()%100;
if(temp<50)
snows[i].sp.x++;
else
snows[i].sp.x--;
DrawSnows(SrcImg->Canvas,snows[i]);//将新的雪花画到新的帧上
}
}
//---------------------------------------------------------------------------
//显示字幕的函数
void __fastcall TForm1::DrawText(TCanvas *DCanvas,int x,int y)
{
DCanvas->Brush->Style =bsClear;//透明得显示文字
DCanvas->Font->Name="宋体";
DCanvas->Font->Size=9;
DCanvas->Font->Color=clWhite;
int dx;
x/=2;
TEXTMETRIC ReturnResults;
GetTextMetrics(DCanvas->Handle,&ReturnResults);//获取文字的一些属性
for(int i=0;i< StringList->Count;i++)
{
y+=15;
if(y>40&&y<240)
{
//计算出文字在画布上的X轴的偏移量使得文字能显示在画布的中央
dx=x-(ReturnResults.tmAveCharWidth*StringList->Strings[i].Length())/2;
//将文字写入画布
DCanvas->TextOutA(dx,y,StringList->Strings[i]);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
//最后将申请的资源释放
delete ImgBK;
delete SrcImg;
delete StringList;
}
//end.
四.简单总结
下雪的动画笔者曾做过好几个版本,还实现了雪花积累的效果。示例中的雪花只是简单的画个圆,其实雪花可以事先用画图工具画好然后保存为位图,将位图画到背景上,这样实现的雪景更好看。你还可以增加行走的人物等其他动画对象,实现原理和示例基本一样。在这只是简单介绍一下实现方法:先用绘图软件如photoshop画一系列大小和颜色深浅不同的雪花,如下图
背景色用黑色RGB(0,0,0),然后用TransparentBlt这个API函数将雪花拷贝到内存位图上,组装成新的帧,最后显示到窗口上。TransparentBlt函数的具体使用方法请看MSDN,或者参考《电脑编程与维护》杂志2001年度第八期《浅析桌面精灵的实现》这篇文章。
总的来说,动画制作的方法有很多种,只要你有好的创意,就一定会制作出优秀的动画。顺便向大家介绍一个日本人编的VCL组件DelphiDX,它是个DirectX组件(DirectX components for Delphi 3, 4 and 5),用它编游戏很酷呀,还有好多例子在里面,快下载吧!http://www.yks.ne.jp/~hori/。笔者用它也编了个下雪的动画,效果不错,源代码在我的网站上有的。
二○○二年一月二日星期三