#include?<stdlib.h>
#include?<string.h>
#include?<time.h>
/*
*?清除屏幕的shell?命令/控制臺命令,還有壹些依賴平臺的實現
*?如果定義了?__GNUC__?就假定是?使用gcc?編譯器,為Linux平臺
*否則?認為是?Window?平臺
*/
#if?defined(__GNUC__)
//下面是依賴?Linux?實現
#include?<unistd.h>
#define?sleep_ms(m)?\
usleep(m?*?1000)
//向上移動光標函數?Linux
static?void?__curup(int?height)
{
int?i?=?-1;
while?(++i<height)
printf("\033[1A");?//先回到上壹行?
}
#else?
//?創建等待函數?1s?60?幀?相當於?16.7ms?=>?1幀,?我們取16ms
//?咱麽的這屏幕?推薦?1s?25幀吧?40ms
//?這裏創建等待函數?以毫秒為單位?,?需要依賴操作系統實現
#include?<Windows.h>
#define?sleep_ms(m)?\
Sleep(m)
//向上移動光標
static?void?__curup(int?height)
{
COORD?cr?=?{0,0};
//?GetStdHandle(STD_OUTPUT_HANDLE)?獲取屏幕對象,?設置光標
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),?cr);
}
#endif?/*__GNUC__?跨平臺的代碼都很醜陋?*/
//?定義初始屏幕的寬高像素宏
#define?_INT_WIDTH(100)
#define?_INT_HEIGHT(50)
//?屏幕刷新幀的速率
#define?_INT_FRATE(40)
//?雪花飄落的速率,相對於?屏幕刷新幀?的倍數
#define?_INT_VSNOW(10)
/*
*?錯誤處理宏,msg必須是""括起來的字符串常量
*?__FILE__:?文件全路徑
*?__func__:?函數名
*?__LINE__:?行數行
*?__VA_ARGS__:?可變參數宏,
*?##表示直接連接,?例如?a##b?<=>?ab
*/
#define?cerr(msg,...)?\
fprintf(stderr,?"[%s:%s:%d]"?msg?"\n",__FILE__,__func__,__LINE__,##__VA_ARGS__);
/*
*?屏幕結構體,?具有?寬高
*?frate?:?繪制壹幀的周期,?單位是?毫秒
*?width?:?屏幕的寬,基於窗口的左上角(0,0)
*?height?:?屏幕的高
*?pix:?用壹維模擬二維?主要結構如下
*?0?0?0?1?0?0?1?0?1?0
*?0?1?0?1?0?1?0?1?2?0
*?.?.?.
*?=>?0表示沒像素,?1表示1個像素,2表示2個像素....
*/
struct?screen?{
int?frate;?//?也可以用?unsigned?結構
int?width;
int?height;
char?*pix;
};
/*
*?創建壹個?屏幕結構指針?返回
*
*?int?frate:?繪制壹幀的周期
*?int?width:?屏幕寬度
*?int?height:?屏幕高度
*?return:?指向屏幕結構的指針
*?*/
struct?screen*?screen_create(int?frate,?int?width,?int?height);
/*
*?銷毀壹個?屏幕結構指針,?並為其置空
*?struct?screen**?:?指向?屏幕結構指針的指針,?二級銷毀壹級的
*?*/
void?screen_destory(struct?screen**?pscr);
/**
*?屏幕繪制函數,主要生成壹個雪花效果
*
*?struct?screen*?:?屏幕數據
*?return?:?0表示可以繪制了,1表示圖案不變
*/
int?screen_draw_snow(struct?screen*?scr);
/**
*?屏幕繪制動畫效果,?繪制雪花動畫
*
*?struct?screen*?:?屏幕結構指針
*/
void?screen_flash_snow(struct?screen*?scr);
//?主函數,主業務在此運行
int?main(int?argc,?char?*argv[])
{
struct?screen*?scr?=?NULL;
//創建壹個屏幕對象
scr?=?screen_create(_INT_FRATE,?_INT_WIDTH,?_INT_HEIGHT);
if?(NULL?==?scr)
exit(EXIT_FAILURE);
//繪制雪花動畫
screen_flash_snow(scr);
//銷毀這個屏幕對象
screen_destory(&scr);
return?0;
}
/*
*?創建壹個?屏幕結構指針?返回
*
*?int?frate:?繪制壹幀的周期
*?int?width:?屏幕寬度
*?int?height:?屏幕高度
*?return:?指向屏幕結構的指針
*?*/
struct?screen*
screen_create(int?frate,?int?width,?int?height)
{
struct?screen?*scr?=?NULL;
if?(frate<0?||?width?<=?0?||?height?<=?0)?{
cerr("[WARNING]check?is?frate<0?||?width<=0?||?height<=0?err!");
return?NULL;
}
//後面是?為?scr->pix?分配的內存?width*height
scr?=?malloc(sizeof(struct?screen)?+?sizeof(char)*width*height);
if?(NULL?==?scr)?{
cerr("[FATALG]Out?of?memory!");
return?NULL;
}
scr->frate?=?frate;
scr->width?=?width;
scr->height?=?height;
//減少malloc次數,malloc消耗很大,內存泄露呀,內存碎片呀
scr->pix?=?((char?*)scr)?+?sizeof(struct?screen);
return?scr;
}
/*
*?銷毀壹個?屏幕結構指針,?並為其置空
*?struct?screen**?:?指向?屏幕結構指針的指針,?二級銷毀壹級的
*?*/
void
screen_destory(struct?screen**?pscr)
{
if?(NULL?==?pscr?||?NULL?==?*pscr)
return;
free(*pscr);
//?避免野指針
*pscr?=?NULL;
}
//構建開頭?的雪花,下面宏表示每?_INT_SHEAD?個步長,壹個雪花,需要是2的冪
//static?可以理解為?private,?宏,位操作代碼多了確實難讀
#define?_INT_SHEAD?(1<<2)
static?void?__snow_head(char*?snow,?int?len)
{
int?r?=?0;
//數據需要清空
memset(snow,?0,?len);
for?(;;)?{
//取余壹個技巧?2^3?-?1?=?7?=>?111?,?並就是取余數
int?t?=?rand()?&?(_INT_SHEAD?-?1);
if?(r?+?t?>=?len)
break;
snow[r?+?t]?=?1;
r?+=?_INT_SHEAD;
}
}
#undef?_INT_SHEAD
//通過?上壹個?scr->pix[scr->width*(idx-1)]?=>?scr->pix[scr->width*idx]
//下面的宏?規定?雪花左右搖擺?0?向左壹個像素,?1?表示?不變,?2表示向右壹個像素
#define?_INT_SWING?(3)
static?void?__snow_next(struct?screen*?scr,?int?idx)
{
int?width?=?scr->width;
char*?psnow?=?scr->pix?+?width*(idx?-?1);
char*?snow?=?psnow?+?width;
int?i,?j,?t;?//?i索引,?j保存下壹個瞬間雪花的位置,t?臨時補得,解決雪花重疊問題
//為當前行重置memset(snow,?0,?width);
//通過上壹次雪花位置?計算下壹次雪花位置
for?(i?=?0;?i<width;?++i)?{
for?(t?=?psnow[i];?t>0;?--t)?{?//?雪花可以重疊
//?rand()%_INT_SWING?-?1?表示?雪花?橫軸的偏移量,相對上壹次位置
j?=?i?+?rand()?%?_INT_SWING?-?1;
j?=?j<0width?-?1?:?j?>=?width0?:?j;?//?j如果越界了,左邊越界讓它到右邊,右邊越界到左邊
++snow[j];
}
}
}
/**
*?屏幕繪制函數,主要生成壹個雪花效果
*
*?struct?screen*?:?屏幕數據
*?return?:?0表示可以繪制了,1表示圖案不變
*/
int
screen_draw_snow(struct?screen*?scr)
{
//?靜態變量,默認初始化為0,每次都***用
static?int?__speed?=?0;
int?idx;
if?(++__speed?!=?_INT_VSNOW)
return?1;
//下面?就是?到了雪花飄落的時刻了?既?__speed?==?_INT_VSNOW
__speed?=?0;
//這裏重新構建雪花界面,先構建頭部,再從尾部開始構建
for?(idx?=?scr->height?-?1;?idx?>?0;?--idx)
__snow_next(scr,?idx);
//構建頭部
__snow_head(scr->pix,?scr->width);
return?0;
}
//buf?保存scr?中pix?數據,構建後為?(width+1)*height,?後面宏是雪花圖案
#define?_CHAR_SNOW?‘*‘
static?void?__flash_snow_buffer(struct?screen*?scr,?char*?buf)
{
int?i,?j,?rt;
int?height?=?scr->height,?width?=?scr->width;
int?frate?=?scr->frate;?//刷新的幀頻率
//每次都等壹下
for?(;;sleep_ms(frate))?{
//開始繪制屏幕
rt?=?screen_draw_snow(scr);
if?(rt)
continue;
for?(i?=?0;i<height;?++i)?{
char*?snow?=?scr->pix?+?i*width;
for?(j?=?0;?j<width;?++j)
buf[rt++]?=?snow[j]_CHAR_SNOW?:?‘?‘;
buf[rt++]?=?‘\n‘;
}
buf[rt?-?1]?=?‘\0‘;
//正式繪制到屏幕上
puts(buf);
//清空老屏幕,屏幕光標回到最上面
__curup(height);
}
}
#undef?_CHAR_SNOW
/**
*?屏幕繪制動畫效果,?繪制雪花動畫
*
*?struct?screen*?:?屏幕結構指針
*/
void
screen_flash_snow(struct?screen*?scr)
{
char*?buf?=?NULL;
//?初始化隨機數種子,改變雪花軌跡
srand((unsigned)time(NULL));
buf?=?malloc(sizeof(char)*(scr->width?+?1)*scr->height);
if?(NULL?==?buf)?{
cerr("[FATAL]Out?of?memory!");
exit(EXIT_FAILURE);
}
__flash_snow_buffer(scr,?buf);
//1.這裏理論上不會執行到這,沒加控制器.?2.對於buf=NULL,這種代碼?可以省掉,看編程習慣
free(buf);
buf?=?NULL;
}