raylib绘图库--瞿华
2022.7.1
知乎 1、raylib绘图库简介

知乎 2、raylib 2d动画/游戏教程(1)动画基本原理

知乎 3、raylib 2d动画/游戏教程(2)坐标系与颜色系统

知乎 4、raylib 2d动画/游戏教程(3)图像文件与图层

知乎 5、raylib 2d动画/游戏教程(4)raylib-drawing库

知乎 6、raylib 2d动画/游戏教程(5)键盘与鼠标输入

知乎 7、raylib 2d动画/游戏教程(6)游戏手柄输入

知乎 8、raylib 2d动画/游戏教程(7)音乐和音效

知乎 9、raylib绘制中文内容

知乎 10、使用raygui绘制控件

知乎 11、raylib 3d绘图基础教程(1)坐标系和摄像机

知乎 12、raylib 3d绘图基础教程(2)网格(Mesh)

知乎 13、raylib 3d绘图基础教程(3)几何变换

知乎 14、raylib 3d绘图基础教程(4)3d模型的载入与绘制

使用raygui绘制控件
1 raygui简介
raygui是基于raylib开发的实时UI库,可以配合raylib使用,为动画/游戏提供交互界面。它提供了按钮、下拉框、输入框等多种组件。从1.1.2版本开始,小熊猫C++在发行版自带的编译器中集成了raygui。

注意:

对于小熊猫C++1.1.4版本以前自带的raygui,请使用
#include <raygui/raygui.h>
由于raylib字体占用显存的问题,raygui的文本框不太适合用于接收中文输入内容。
raygui.h是一个只有头文件的库,通过RAYGUI_IMPLEMENTATION宏来控制raygui.h是否同时包含函数的实现。如果一个项目中有多个文件包含raygui.h,则必须保证有且仅有一个文件在包含raygui.h前定义了RAYGUI_IMPLEMENTATION宏。否则,要么会在链接时出现函数重复实现的错误(多个文件定义了RAYGUI_IMPLEMENTATION宏);要么会在链接时出现找不到相关函数实现的错误(没有文件定义RAYGUI_IMPLEMENTATION)。
raygui控件的实际绘制在EndDrawing()时完成,因此BeginDrawing()/EndDrawing()内的绘制的内容会显示在控件后面。
2 按钮示例
下面的例子演示了raygui按钮的使用方法,以及如何让raygui的控件显示中文提示。

注意,因为控件上的文字内容很少且基本不变,所以我们直接在绘制循环外载入字体,以方便处理。

#include <raylib.h>
#include <math.h>
//raygui的函数实现也都在raygui.h文件中,如果一个项目中有多个文件
//包含raygui.h,那么只能有一个文件定义RAYGUI_IMPLEMENTATION,否则
//会导致链接时出错。
#define RAYGUI_IMPLEMENTATION
#include <raygui.h>
int main() {
	InitWindow(800,600,"RAYGUI示例");
	SetTraceLogLevel(LOG_WARNING);
	SetTargetFPS(30);
	//读取字体文件
	char sWordNeed[]="太阳月亮您选择了:!";
	//将字符串中的字符逐一转换成Unicode码点,得到码点表
	int nCode;
	int *pCodeNeed=LoadCodepoints(sWordNeed,&nCode);
	//读取仅含码点表中各字符的字体
	Font fontNeed = LoadFontEx("simhei.ttf",64,pCodeNeed,nCode);
	//释放码点表
	UnloadCodepoints(pCodeNeed);
	//设置GUI控件的字体和大小
	GuiSetFont(fontNeed);
	GuiSetStyle(DEFAULT,TEXT_SIZE,20);
	int btnClick = 0;
	while(!WindowShouldClose()) {
		if (btnClick==0) {
			if (GuiButton((Rectangle){ 120, 150, 150, 30 },"太阳"))btnClick = 1;
			if (GuiButton((Rectangle){ 120, 250,150,30},"月亮"))btnClick = 2;
		} 
		BeginDrawing();
			ClearBackground(WHITE);
			if (btnClick==0){EndDrawing();continue;}
			switch(btnClick) {
			case 1:
				DrawTextEx(fontNeed,"您选择了太阳!",Vector2{200,200},64,1,RED);
				break;
			case 2:
				DrawTextEx(fontNeed,"您选择了月亮!",Vector2{200,200},64,1,RED);
				break;
			}
		EndDrawing();
	}
	//释放字体
	UnloadFont(fontNeed);
	CloseWindow();
}



3 外旋螺线示例
下面这个例子演示了raygui的按钮、调节钮、颜色选择等控件的使用。注意本例中使用了rdrawing.h用于绘制2d图形。

#include <raylib.h>
#include <rdrawing.h>
#include <math.h>
//raygui的函数实现也都在raygui.h文件中,如果一个项目中有多个文件
//包含raygui.h,那么只能有一个文件定义RAYGUI_IMPLEMENTATION,否则
//会导致链接时出错。
#define RAYGUI_IMPLEMENTATION
#include <raygui.h>
#define SCREEN_W 1000
#define SCREEN_H 700

void updateRadius(int baseL, int outerL, int pointL, int *pBaseR, int *pOuterR, int *pPointR) {//计算外旋轮、基圆和动点的实际半径,以保证不超出图像范围
	int totalL=baseL+outerL;
	if (pointL>outerL)totalL+=pointL;
	else totalL+=outerL;
	int totalR = SCREEN_H/2;
	int remainder = totalR % totalL;
	if (remainder!=0) {
		if (remainder < totalL / 2)totalR -= remainder;
		else totalR += ( totalL - remainder);
	}
	*pBaseR = (totalR) / totalL * baseL;
	*pOuterR = (totalR) / totalL * outerL;
	*pPointR = (totalR) / totalL * pointL;
}
int main() {
	int baseL=1;//内圆半径
	int outerL=3;//外圆半径
	int pointL=2;//动点半径
	int baseR,outerR,pointR;
	int cx=SCREEN_H/2,cy=SCREEN_H/2;
	int speed = 1;
	Color trackColor = BLUE;
	//计算外旋轮、基圆和动点的实际半径,以保证不超出图像范围
	updateRadius(baseL, outerL, pointL, &baseR, &outerR, & pointR);
	InitWindow(SCREEN_W,SCREEN_H,"外旋螺线");
	SetTraceLogLevel(LOG_WARNING);
	SetTargetFPS(60);
	//读取字体文件
	char sWordNeed[]="外旋轮基圆动点半径速度颜色清除: 0123456789x";
	//将字符串中的字符逐一转换成Unicode码点,得到码点表
	int nCode;
	int *pCodeNeed=LoadCodepoints(sWordNeed,&nCode);
	//读取仅含码点表中各字符的字体
	Font fontNeed = LoadFontEx("simhei.ttf",20,pCodeNeed,nCode);
	//释放码点表
	UnloadCodepoints(pCodeNeed);
	//设置GUI控件的字体和大小
	GuiSetFont(fontNeed);
	GuiSetStyle(DEFAULT,TEXT_SIZE,20);
	Image trackImage=GenImageColor(SCREEN_W-100,SCREEN_W-100,WHITE);
	//border
	ImageFillRectangleEx(&trackImage,0,0,SCREEN_W-100,SCREEN_W-100,LIGHTGRAY);
	ImageFillRectangleEx(&trackImage,5,5,SCREEN_W-110,SCREEN_W-110,WHITE);
	Image circlesImage = GenImageColor(SCREEN_W-100,SCREEN_W-100,BLANK);
	float r=0;
	int lastx,lasty;
	lasty=cy;
	lastx=cx+(baseR+outerR-pointR);
	int frameCount = 0;
	#define BAR_W 50
	#define BAR_H 30
	while(!WindowShouldClose()) {
		int newOuterL = GuiSliderBar((Rectangle){ 120, 20,BAR_W,BAR_H },"外旋轮半径",TextFormat("%i", (int)outerL), outerL, 1, 50);
		int newBaseL = GuiSliderBar((Rectangle){ 120, 60,BAR_W,BAR_H },"基圆半径",TextFormat("%i", (int)baseL), baseL, 1, 50);
		int newPointL = GuiSliderBar((Rectangle){ 120, 100,BAR_W,BAR_H },"动点半径",TextFormat("%i", (int)pointL), pointL, 1, 50);
		speed = GuiSliderBar((Rectangle){ 120, 150,BAR_W,BAR_H },"速度",TextFormat("%i", (int)speed), speed, 1, 50);
		GuiLabel((Rectangle){ 20, 220,BAR_W,BAR_H },TextFormat("颜色: 0x%X%X%X ",(int)(trackColor.r), (int)(trackColor.g),(int)(trackColor.b)));
		trackColor= GuiColorPicker((Rectangle){ 50, 250, BAR_W*2,BAR_H*2 }, NULL, trackColor);
		int doClear = GuiButton((Rectangle){ 120, SCREEN_H-100, BAR_W,BAR_H },"清除");
		if (newOuterL!=outerL || newBaseL!=baseL || newPointL!=pointL) {
			if (newOuterL!=outerL)pointL=newOuterL;
			else pointL=newPointL;
			outerL=newOuterL;
			baseL=newBaseL;
			//重新计算外旋轮、基圆和动点的实际半径,以保证不超出图像范围
			updateRadius(baseL, outerL, pointL, &baseR, &outerR, & pointR);
			lasty=cy;
			lastx=cx+(baseR+outerR-pointR);
			r=0;
			ImageClearBackground(&trackImage,WHITE);
			ImageFillRectangleEx(&trackImage,0,0,SCREEN_W-100,SCREEN_W-100,LIGHTGRAY);
			ImageFillRectangleEx(&trackImage,5,5,SCREEN_W-110,SCREEN_W-110,WHITE);
		} else if (doClear) {
			ImageClearBackground(&trackImage,WHITE);
			ImageFillRectangleEx(&trackImage,0,0,SCREEN_W-100,SCREEN_W-100,LIGHTGRAY);
			ImageFillRectangleEx(&trackImage,5,5,SCREEN_W-110,SCREEN_W-110,WHITE);
		}
		//更新外旋轮和动点的位置
		r+=0.01;
		float outerCX=cx+ (baseR+outerR)*cos(r);
		float outerCY=cy+ (baseR+outerR)*sin(r);
		float theta = r * (baseL+outerL) / outerL; 
		int x=round(outerCX - pointR * cos(theta));
		int y=round(outerCY - pointR * sin(theta));
		//update image (in CPU)
		//ImageClearBackground(&trackImage,WHITE);
		ImageDrawLineEx(&trackImage,lastx,lasty,x,y,3,trackColor);
		frameCount++;
		if (frameCount>=speed) {
			ImageClearBackground(&circlesImage,BLANK);
			//base circle
			ImageDrawCircleEx(&circlesImage,cx,cy,baseR,1,LIGHTRED);
			ImageDrawCircleEx(&circlesImage,outerCX,outerCY,outerR,1,LIGHTSLATEGRAY);
			ImageDrawLineEx(&circlesImage,cx,cy,outerCX,outerCY,1,LIGHTRED);
			ImageDrawLineEx(&circlesImage,x,y,outerCX,outerCY,1,LIGHTSLATEGRAY);
			ImageDrawPointEx(&circlesImage,x,y,7,RED);
			//Drawing in GPU
			Texture trackTexture = LoadTextureFromImage(trackImage);
			Texture circlesTexture = LoadTextureFromImage(circlesImage);
			BeginDrawing();
			ClearBackground(WHITE);
			DrawTexture(trackTexture,200,0,WHITE);
			DrawTexture(circlesTexture,200,0,WHITE);
			EndDrawing();
			UnloadTexture(circlesTexture);
			UnloadTexture(trackTexture);
			frameCount=0;
		}
		lastx=x;
		lasty=y;
	}
	//Clean up
	UnloadImage(circlesImage);
	UnloadImage(trackImage);
	//释放字体
	UnloadFont(fontNeed);
	CloseWindow();
}