c 屏幕铺满星星

snow_small

#include <Windows.h>
#include<time.h>

HDC hScreenDC;		// 屏幕画图设备句柄
POINT pData[500];	// 存储星星坐标
long pColor[500];	// 存取原来坐标的颜色值
int Vx;				// vx星星整体水平漂移速度
int Vy;				// vy星星总体垂直下落速度
int PVx;			// pvx单个星星实际水平速度
int PVy;			// pvy单个星星实际垂直速度
int timeCount;		// 计时循环次数

const int StarNum = 500;// 星星数量为500
const int ID_TIMER = 1; // 定时器标识符

const long   StarColor = 0xFEFFFE;  // 星星颜色
const long StarColDown = 0xFFFFFF;  // 积雪星星颜色
const long StarColDuck = 0xFFDDDD;  // 深色积雪颜色

const int ScrnWidth = GetSystemMetrics ( SM_CXSCREEN ); // 全屏宽度
const int ScrnHight = GetSystemMetrics ( SM_CYSCREEN ); // 全屏高度

long Abs(long num)
{
    if (num >= 0)
		return (num);
    else
		return (-num);
}

int Random(int max)
{
    return (rand() % max);
}

void InitPoint(int i)
{
    pData[i].x = Random(ScrnWidth);
    pData[i].y = Random(5);
    pColor[i]  = GetPixel(hScreenDC, pData[i].x, pData[i].y);
}

long GetContrast(int i)
{
    long ColorCmp;	//存取作对比点的颜色值
	long tempR;		//存取ColorCmp的红色值
	long tempG;		//同理
	long tempB;		//同理
	int  Slope;		//存取星星飘落的方向

    if (PVy != 0) 
		Slope = PVx / PVy; // 若pvx/pvy在-1和1之间则slope=0 就取正下方的的像素点
    else // 若pvx/pvy>1,取右下方的点 pvx/pvy<-1 则取左下方
		Slope = 2; // 根据星星飘落方向决定取哪一点做对比点

    if (Slope == 0)
		ColorCmp = GetPixel(hScreenDC, pData[i].x, pData[i].y + 1);
    else if (Slope > 1)
		ColorCmp = GetPixel(hScreenDC, pData[i].x + 1, pData[i].y + 1);
    else
		ColorCmp = GetPixel(hScreenDC, pData[i].x - 1, pData[i].y + 1);

    //确定当前位置没有与另一个星星重叠,重叠就返回0;用于防止由于不同星星重叠造成星星乱堆
    if (ColorCmp == StarColor)
		return 0;

    //分别获取ColorCmp与对比点的蓝,绿,红的差值
    tempB = Abs((ColorCmp >> 16) & 0xff - (pColor[i] >> 16) & 0xff);
    tempG = Abs((ColorCmp >> 8) & 0xff - (pColor[i] >> 8) & 0xff);
    tempR = Abs((ColorCmp) & 0xff - (pColor[i]) & 0xff);

    return ( (tempR + tempG + tempB) / 3 ); //返回对比度
}

void DrawPoint(void)
{
    int i;
    for (i = 0; i < StarNum; i++) //防止星星重叠造成干扰
    {
        if (pColor[i] != StarColor && pColor[i] != -1)
		{
            SetPixel(hScreenDC, pData[i].x, pData[i].y, pColor[i]); //还原上一个位置的颜色
		}

        //设置新的位置,i%3用于将星星分为3类采用不同的速度,以便形成参次感
        PVx = Random(2) - 1 + Vx * (i % 3);
        PVy = Vy * (i % 3 + 1);
        pData[i].x = pData[i].x + PVx; //+pvx与前一点形成的距离差,形成速度的差别
        pData[i].y = pData[i].y + PVy; //同理

        //取的新位置的原始点的颜色值,用于下一步星星飘过时恢复此处的颜色
        pColor[i] = GetPixel(hScreenDC, pData[i].x, pData[i].y);

        //如果获取颜色值失败,表明星星已经飘出屏幕,重新初始化,GetPixel如果获取失败就返回-1.
        if (pColor[i] == -1)
		{
            InitPoint(i);
		}
        else
        {
            //如果星星没有重叠,若对比度较小(即不能堆积)就画出星星
            //Random(16)>5用于防止某些连续而明显的边界截获所有的星星
            if (pColor[i] != StarColor)
            {
				//GetContrast(i)<50判断是否超出对比度50这个值就认为是边缘就会堆积星星
                if (Random(16) > 5 || GetContrast(i) < 50)
                {
                    SetPixel(hScreenDC, pData[i].x, pData[i].y, StarColor);
                } else
                {
				//否者表明找到明显的边界,画出堆积的学,并初始化以便画新的的星星
                    SetPixel(hScreenDC, pData[i].x, pData[i].y - 1, StarColDuck);
                    SetPixel(hScreenDC, pData[i].x - 1, pData[i].y, StarColDuck);
                    SetPixel(hScreenDC, pData[i].x + 1, pData[i].y, StarColDown);
                    InitPoint(i);
                }
            }
        }
    }
}

int main()
{
	MSG msg;
	HWND hwnd = GetActiveWindow(); // 获取当前窗口的句柄

	int i;
	srand(time(0));
	Vx = Random(4) - 2;
	Vy = Random(2) + 2;

	// 随机获取屏幕上一点
	for (i = 0; i < StarNum; i++)
	{
		// 保存坐标
		pData[i].x = Random(ScrnWidth);
		pData[i].y = Random(ScrnHight);
		// 保存颜色
		pColor[i] = GetPixel(hScreenDC, pData[i].x, pData[i].y);
	}

	// 设置计时器
	SetTimer(hwnd, ID_TIMER, 5, NULL);

	// 获得整个屏幕画图设备
	hScreenDC = GetDC(0);

	// 计时
	timeCount = 0;

	// 循环获取操作系统发来的消息
	while (GetMessage(&msg, NULL, 0, 0) != 0)
	{
		switch(msg.message)
		{
		case WM_TIMER: // 时钟信号
			{
				if (timeCount > 200)
				{
					timeCount = 0;
					Vx = Random(4) - 2;
					Vy = Random(2) + 2;
				}
				else
				{
					timeCount += 1;
				}
				DrawPoint();
			}
			break;
		}
	}
}

先随机取的一个屏幕的坐标值(放在pData[i]),对应点的颜色存储在 pcolor[i] 中。
然后将其设置成星星的颜色值 #HFEFFFE(就是画出星星),

到下一个计时器触发时先根据原来存储的 pDate[i],恢复原来点的颜色,再随机取新的坐标值,画新的颜色。反复重复······从而形成星星飘动的效果

在此基础上加上判断屏幕边缘(用自定义的对比度函数 GetContrast,它返回与点 pDate[i]下面相邻的点的颜色值各 RGB 分量之差的和。当这个差大于某一个值,比喻程序用50是认为此处达到边缘)随机,风向等。星星颜色值是 Starcol = #HFEFFFE ,之所以取这一个值,是因为发现有时候2个星星重叠会混乱(如上下2个星星相邻,这样下一个星星会干扰上一个星星取得屏幕原来的颜色),
所以将星星设置成这种接近白色当不如白色FFFFFF常见颜色,当遇到取得的屏幕原始值是 StarColor 就认为出现重叠,就跳过这一个计时器事件不进行屏幕操作

Advertisements

1 thought on “c 屏幕铺满星星

  1. 博主的文章确实很不错,让我受益颇深,希望博主能够再接再厉写出更多更加精彩的文章,同时我希望博主能否提供一些有关stm32以及8051单片机方面的教程与文章呢?

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google+ photo

You are commenting using your Google+ account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s