??專欄導(dǎo)讀
??作者簡(jiǎn)介:花想云,在讀本科生一枚,致力于 C/C++、Linux 學(xué)習(xí)。
??本文收錄于 初學(xué)C語(yǔ)言必會(huì)的20個(gè)小游戲?qū)?/font>,本專欄主要內(nèi)容為利用C/C++與圖形庫(kù)EasyX實(shí)現(xiàn)各種有趣的小游戲。
??相關(guān)專欄推薦:C語(yǔ)言初階系列 、C語(yǔ)言進(jìn)階系列 、數(shù)據(jù)結(jié)構(gòu)與算法
??文章導(dǎo)讀
本文主要內(nèi)容為,利用圖形庫(kù)
與簡(jiǎn)單的C語(yǔ)言
知識(shí)實(shí)現(xiàn)櫻花樹。文章涉及的C語(yǔ)言語(yǔ)法并不多,但要求了解簡(jiǎn)單的遞歸運(yùn)用
。每一小節(jié)都會(huì)附有完整代碼與實(shí)現(xiàn)效果圖,有需求的小伙伴也可以直接去復(fù)制使用~
特別聲明
——本文內(nèi)容與代碼全部參考書籍《C和C++趣味游戲編程》
,當(dāng)然我也非常推薦這本書。
繪制一根線條
- 初始化畫板窗口;
- 設(shè)置畫板背景顏色(白色);
- 繪制一個(gè)線條;
- 設(shè)置線條顏色(黑色);
2-1
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
#include<math.h>
#define WIDTH 800 // 畫面寬度
#define HEIGHT 600 // 畫面高度
int main()
{
initgraph(WIDTH, HEIGHT); // 初始化窗口
setbkcolor(RGB(225, 225, 225)); //白色背景
setlinecolor(RGB(0, 0, 0)); // 設(shè)定線條顏色為黑色
setlinestyle(PS_SOLID, 3); // 設(shè)定線寬
cleardevice(); // 清屏
BeginBatchDraw(); // 開始批量繪制
line(WIDTH / 2, HEIGHT, WIDTH / 2, 150); //繪制線條
FlushBatchDraw(); // 刷新畫板
_getch(); // 等待輸入
closegraph(); // 關(guān)閉畫板
return 0;
}
效果圖
繪制一個(gè)簡(jiǎn)易的樹干
- 使用函數(shù)遞歸來完成樹干的繪制;
- 利用三角函數(shù)來改變每根線條的傾斜度;
2-2
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
#include<math.h>
#define PI 3.1415926 // 圓周率
#define WIDTH 800 // 畫面寬度
#define HEIGHT 600 // 畫面高度
// 繪制樹干的函數(shù)
void branch(float x_start, float y_start, float angle, int generation)
{
// 利用三角函數(shù)求出當(dāng)前樹枝的終點(diǎn)坐標(biāo)
float x_end, y_end;
x_end = x_start + 150 * cos(angle);
y_end = y_start + 150 * sin(angle);
line(x_start, y_start, x_end, y_end); // 畫出當(dāng)前枝干
// 求出子枝干的代數(shù)
int childGeneration = generation + 1;
// 當(dāng)子枝干的代數(shù)<=4,畫出當(dāng)前枝干,并遞歸調(diào)用產(chǎn)生子枝干
if (childGeneration <= 4)
{
// 產(chǎn)生左右的子枝干
branch(x_end, y_end,angle+PI/6,childGeneration);
branch(x_end, y_end, angle - PI / 6, childGeneration);
}
}
int main()
{
initgraph(WIDTH, HEIGHT); // 初始化窗口
setbkcolor(RGB(225, 225, 225)); // 白色背景
setlinecolor(RGB(0, 0, 0)); // 設(shè)定線條顏色為黑色
setlinestyle(PS_SOLID, 3); // 設(shè)定線寬
cleardevice(); // 清屏
BeginBatchDraw(); // 開始批量繪制
branch(WIDTH/2,HEIGHT,-PI/2,1); // 遞歸繪圖
FlushBatchDraw();
_getch();
closegraph();
return 0;
}
效果圖
優(yōu)化樹干,使其更加細(xì)致
- 使用比例來控制父枝干與子枝干的長(zhǎng)度;
- 控制父枝干與子枝干的夾角變化;
2-3
#define PI 3.1415926 // 圓周率
#define WIDTH 800 // 畫面寬度
#define HEIGHT 600 // 畫面高度
float offsetAngle = PI / 6; // 左右枝干與父枝干偏離的角度
float shortenRate = 0.65; // 左右枝干長(zhǎng)度與父枝干長(zhǎng)度的比例
void branch(float x_start, float y_start, float length,float angle,float thickness, int generation)
{
// 利用三角函數(shù)求出當(dāng)前樹枝的終點(diǎn)坐標(biāo)
float x_end, y_end;
x_end = x_start + length * cos(angle);
y_end = y_start + length * sin(angle);
setlinestyle(PS_SOLID, thickness); //設(shè)置當(dāng)前枝干的寬
setlinecolor(RGB(0, 0, 0)); // 設(shè)置當(dāng)前枝干的顏色
line(x_start, y_start, x_end, y_end); // 畫出當(dāng)前枝干
// 求出子枝干的代數(shù)
int childGeneration = generation + 1;
float childLength = shortenRate * length; // 生成的枝干的長(zhǎng)度逐漸變短
// 當(dāng)子枝干的長(zhǎng)度>=2,且當(dāng)子枝干的代數(shù)<10,畫出當(dāng)前枝干,并遞歸調(diào)用產(chǎn)生子枝干
if (childLength >= 2 && childGeneration <10)
{
// 生成子枝干的寬度逐漸變細(xì)
float childThickness = thickness * 0.8;
if (childThickness < 2)
{
childThickness = 2;
}
// 產(chǎn)生左右的子枝干
branch(x_end, y_end, childLength, angle + offsetAngle, childThickness, childGeneration);
branch(x_end, y_end, childLength, angle - offsetAngle, childThickness, childGeneration);
}
}
int main()
{
initgraph(WIDTH, HEIGHT); // 初始化窗口
setbkcolor(RGB(225, 225, 225)); //白色背景
setlinecolor(RGB(0, 0, 0)); //設(shè)定線條顏色為黑色
setlinestyle(PS_SOLID, 3); //設(shè)定線寬
cleardevice(); // 清屏
BeginBatchDraw(); // 開始批量繪制
branch(WIDTH/2,HEIGHT,0.45*HEIGHT*shortenRate,-PI/2, 15 * shortenRate, 1); // 遞歸繪圖
FlushBatchDraw();
_getch();
closegraph();
return 0;
}
繪制櫻花樹
- 修改樹干顏色為褐色;
- 為末端的樹干添上櫻花(實(shí)際為粉色的小圓);
2-4
#define PI 3.1415926 // 圓周率
#define WIDTH 800 // 畫面寬度
#define HEIGHT 600 // 畫面高度
float offsetAngle = PI / 6; // 左右枝干與父枝干偏離的角度
float shortenRate = 0.65; // 左右枝干長(zhǎng)度與父枝干長(zhǎng)度的比例
// 枝干生成和繪制遞歸函數(shù)
// 輸入?yún)?shù):枝干起始x y坐標(biāo),枝干長(zhǎng)度,枝干角度,枝干繪圖線條寬度,第幾代
void branch(float x_start, float y_start, float length, float angle, float thickness, int generation)
{
// 利用三角函數(shù)求出當(dāng)前枝干的終點(diǎn)x,y坐標(biāo)
float x_end, y_end;
x_end = x_start + length * cos(angle);
y_end = y_start + length * sin(angle);
setlinestyle(PS_SOLID, thickness); // 設(shè)定當(dāng)前枝干線寬
setlinecolor(HSVtoRGB(15, 0.75, 0.5)); // 設(shè)定當(dāng)前枝干顏色為褐色
line(x_start, y_start, x_end, y_end); // 畫出當(dāng)前枝干
// 求出子枝干的代數(shù)
int childGeneration = generation + 1;
// 生成子枝干的長(zhǎng)度,逐漸變短
float childLength = shortenRate * length;
// 當(dāng)子枝干長(zhǎng)度大于等于2,并且代數(shù)小于等于10,遞歸調(diào)用產(chǎn)生子枝干
if (childLength >= 2 && childGeneration <= 9)
{
// 生成子枝干的粗細(xì),逐漸變細(xì)
float childThickness = thickness * 0.8;
if (childThickness < 2) // 枝干繪圖最細(xì)的線寬為2
childThickness = 2;
// 產(chǎn)生左右的子枝干
branch(x_end, y_end, childLength, angle + offsetAngle, childThickness, childGeneration);
branch(x_end, y_end, childLength, angle - offsetAngle, childThickness, childGeneration);
// 產(chǎn)生中間的子枝干
branch(x_end, y_end, childLength, angle, childThickness, childGeneration);
}
else // 畫出最末端的櫻花
{
setlinestyle(PS_SOLID, 1); // 線寬
setlinecolor(HSVtoRGB(325, 0.3, 1)); // 設(shè)定線條顏色 粉色
setfillcolor(HSVtoRGB(325, 0.3, 1)); // 設(shè)定填充顏色 粉色
if (childLength <= 4) // 如果子枝干長(zhǎng)度小于等于4
fillcircle(x_end, y_end, 2); // 圓的半徑為2(再小就看不清了)
else
fillcircle(x_end, y_end, childLength / 2); // 畫一個(gè)圓,半徑為子枝干長(zhǎng)度的一半
}
}
int main()
{
initgraph(WIDTH, HEIGHT); // 初始化窗口
setbkcolor(RGB(225, 225, 225)); //白色背景
setlinecolor(RGB(0, 0, 0)); // 設(shè)定線條顏色為黑色
setlinestyle(PS_SOLID, 3); // 設(shè)定線寬
cleardevice(); // 清屏
BeginBatchDraw(); // 開始批量繪制
branch(WIDTH/2,HEIGHT,0.45*HEIGHT*shortenRate,-PI/2, 15 * shortenRate, 1); // 遞歸繪圖
FlushBatchDraw();
_getch();
closegraph();
return 0;
}
效果圖
增加隨機(jī)樹形與漸變色效果
將櫻花樹的各個(gè)參數(shù)修改為隨機(jī)數(shù),生成各種形態(tài)不同的櫻花樹;
- 控制樹干的顏色漸變(越往上枝干越亮);
- 設(shè)置花瓣的隨機(jī)顏色;
- 控制樹干的長(zhǎng)度與比例具有隨機(jī)性;
如何設(shè)置隨機(jī)數(shù)
- 使用rand函數(shù)來生成隨機(jī)數(shù);
- 定義mapvalue函數(shù)來將生成的隨機(jī)數(shù)映射到某一區(qū)間;
float mapValue(float input, float inputMin, float inputMax, float outputMin, float outputMax)
{
float output;
if (abs((float)(input - inputMin) < 0.000001)) // 防止除以零的bug
output = outputMin;
else
output = (input - inputMin) * (outputMax - outputMin) / (inputMax - inputMin) + outputMin;
return output;
}
// 生成[min,max]之間的隨機(jī)小數(shù)
float randBetween(float min, float max)
{
float t = rand() / double(RAND_MAX); // 生成[0,1]的隨機(jī)小數(shù)
// 調(diào)用mapValue函數(shù),把值范圍從[0,1]映射到[min,max]
float r = mapValue(t, 0, 1, min, max);
return r;
}
2-5
#include<stdio.h>
#include<graphics.h>
#include<conio.h>
#include<math.h>
#include <time.h>
#define PI 3.1415926
#define WIDTH 800 // 畫面寬度
#define HEIGHT 600 // 畫面高度度
float offsetAngle = PI / 6; // 左右枝干和父枝干偏離的角度
float shortenRate = 0.65; // 子枝干比父枝干變短的倍數(shù)
// 把[inputMin,inputMax]范圍的input變量,映射為[outputMin,outputMax]范圍的output變量
float mapValue(float input, float inputMin, float inputMax, float outputMin, float outputMax)
{
float output;
if (abs((float)(input - inputMin) < 0.000001)) // 防止除以零的bug
output = outputMin;
else
output = (input - inputMin) * (outputMax - outputMin) / (inputMax - inputMin) + outputMin;
return output;
}
// 生成[min,max]之間的隨機(jī)小數(shù)
float randBetween(float min, float max)
{
float t = rand() / double(RAND_MAX); // 生成[0,1]的隨機(jī)小數(shù)
// 調(diào)用mapValue函數(shù),把值范圍從[0,1]映射到[min,max]
float r = mapValue(t, 0, 1, min, max);
return r;
}
// 枝干生成和繪制遞歸函數(shù)
// 輸入?yún)?shù):枝干起始x y坐標(biāo),枝干長(zhǎng)度,枝干角度,枝干繪圖線條寬度,第幾代
void branch(float x_start, float y_start, float length, float angle, float thickness, int generation)
{
// 利用三角函數(shù)求出當(dāng)前枝干的終點(diǎn)x,y坐標(biāo)
float x_end, y_end;
x_end = x_start + length * cos(angle);
y_end = y_start + length * sin(angle);
// 畫線條枝干
setlinestyle(PS_SOLID, thickness); // 設(shè)定當(dāng)前枝干線寬
// 設(shè)置枝干為灰褐色,主樹干最黑,子枝干逐漸變亮
COLORREF color = HSVtoRGB(15, 0.75, 0.4 + generation * 0.05);
setlinecolor(color); // 設(shè)定當(dāng)前枝干顏色
line(x_start, y_start, x_end, y_end); // 畫出當(dāng)前枝干(畫線)
// 求出子枝干的代數(shù)
int childGeneration = generation + 1;
// 生成左、右、中間三個(gè)子枝干的長(zhǎng)度,逐漸變短,并有一定隨機(jī)性
float childLength = shortenRate * length;
float leftChildLength = childLength * randBetween(0.9, 1.1);
float rightChildLength = childLength * randBetween(0.9, 1.1);
float centerChildLength = childLength * randBetween(0.8, 1.1);
// 當(dāng)子枝干長(zhǎng)度大于2,并且代數(shù)小于等于10,遞歸調(diào)用產(chǎn)生子枝干
if (childLength >= 2 && childGeneration <= 9)
{
// 生成子枝干的粗細(xì),逐漸變細(xì)
float childThickness = thickness * 0.8;
if (childThickness < 2) // 枝干繪圖最細(xì)的線寬為2
childThickness = 2;
// 一定概率產(chǎn)生左、右、中子枝干
if (randBetween(0, 1) < 0.95)
branch(x_end, y_end, leftChildLength, angle + offsetAngle * randBetween(0.5, 1), childThickness, childGeneration);
if (randBetween(0, 1) < 0.95)
branch(x_end, y_end, rightChildLength, angle - offsetAngle * randBetween(0.5, 1), childThickness, childGeneration);
if (randBetween(0, 1) < 0.85)
branch(x_end, y_end, centerChildLength, angle + offsetAngle / 5 * randBetween(-1, 1), childThickness, childGeneration);
}
else // 最末端繪制櫻花,畫一個(gè)粉色填充圓
{
setlinestyle(PS_SOLID, 1); // 線寬
// 櫻花粉色HSVtoRGB(325,0.3,1),有一定隨機(jī)性
COLORREF color = HSVtoRGB(randBetween(300, 350), randBetween(0.2, 0.3), 1);
setlinecolor(color); // 設(shè)定線條顏色
setfillcolor(color); // 設(shè)定填充顏色
if (childLength <= 4) // 如果子枝干長(zhǎng)度小于等于4
fillcircle(x_end, y_end, 2); // 圓的半徑為2(再小就看不清了)
else
fillcircle(x_end, y_end, childLength / 2); // 畫一個(gè)圓,半徑為子枝干長(zhǎng)度的一半
}
}
void startup() // 初始化
{
srand(time(0)); // 隨機(jī)初始化
initgraph(WIDTH, HEIGHT); // 新開一個(gè)畫面
setbkcolor(RGB(255, 255, 255)); // 白色背景
cleardevice(); // 清屏
BeginBatchDraw(); // 開始批量繪制
brunch(WIDTH / 2, HEIGHT, 0.45 * HEIGHT * shortenRate, -PI / 2, 15 * shortenRate, 1); // 遞歸函數(shù)調(diào)用
FlushBatchDraw(); // 批量繪制
}
void update() // 每幀更新
{
offsetAngle = randBetween(PI/10,PI/6);
shortenRate = randBetween(0.7, 0.3);
cleardevice(); // 清屏
branch(WIDTH / 2, HEIGHT, 0.45 * HEIGHT * shortenRate, -PI / 2, 15 * shortenRate, 1); // 遞歸調(diào)用
}
int main() // 主函數(shù)
{
startup(); // 初始化
while (1) // 重復(fù)循環(huán)
update(); // 每幀更新
return 0;
}
效果圖
進(jìn)階——通過鼠標(biāo)點(diǎn)擊來控制生成櫻花樹
2-5版本缺點(diǎn)在于每次運(yùn)行程序只能生成一棵櫻花樹。我們還可以引進(jìn)鼠標(biāo)點(diǎn)擊的功能來實(shí)現(xiàn)每次鼠標(biāo)點(diǎn)擊生成不同的櫻花樹。
- 檢測(cè)鼠標(biāo)是否發(fā)生移動(dòng),從而更新遞歸函數(shù)的參數(shù);
2-6
if (m.uMsg == WM_MOUSEMOVE) // 當(dāng)鼠標(biāo)移動(dòng)時(shí),設(shè)定遞歸函數(shù)的參數(shù)
{
// 鼠標(biāo)從左到右,左右子枝干偏離父枝干的角度逐漸變大
offsetAngle = mapValue(m.x, 0, WIDTH, PI / 10, PI / 4);
// 鼠標(biāo)從上到下,子枝干比父枝干的長(zhǎng)度縮短的更快
shortenRate = mapValue(m.y, 0, HEIGHT, 0.7, 0.3);
}
- 檢測(cè)鼠標(biāo)是否發(fā)生點(diǎn)擊動(dòng)作,若點(diǎn)擊則開始繪制;
f (m.uMsg == WM_LBUTTONDOWN) // 當(dāng)鼠標(biāo)左鍵點(diǎn)擊時(shí),以當(dāng)前參數(shù)開始繪制一棵新數(shù)
{
cleardevice(); // 清屏
branch(WIDTH / 2, HEIGHT, 0.45 * HEIGHT * shortenRate, -PI / 2, 15 * shortenRate, 1); // 遞歸調(diào)用
FlushBatchDraw(); // 批量繪制
}
效果圖
進(jìn)階——生成櫻花樹并展示生長(zhǎng)過程
- 使用sleep函數(shù)與FlushBatchDraw將每次繪制的結(jié)果間隔一秒刷新,形成櫻花樹生長(zhǎng)的動(dòng)畫。
2-7
void branch(float x_start, float y_start, float length, float angle, float thickness, int generation)
{
//....
if (isShowAnimation) // 如果為1,繪制櫻花樹生成的過程動(dòng)畫
{
FlushBatchDraw(); // 批量繪制
Sleep(1); // 暫停
}
}
效果圖文章來源:http://www.zghlxwxcb.cn/news/detail-418457.html
快去向你喜歡的人展示櫻花樹叭~
點(diǎn)擊下方個(gè)人名片,可添加博主的個(gè)人QQ,交流會(huì)更方便哦~
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓文章來源地址http://www.zghlxwxcb.cn/news/detail-418457.html
到了這里,關(guān)于櫻花樹盛開的季節(jié),我用簡(jiǎn)單的C代碼繪制了一棵櫻花樹向她表白~『C/C++&圖形庫(kù)EasyX』的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!