|
各位CZLJ用户们,抱歉托更那么久。由于开学后这篇文章只写了一半,一直耽搁着。这次更新来补补。由于我使用学而思学习机的安卓环境,所以Part 2-3-1后的内容无法实时演示,请各位谅解。
上期我们学习了Raylib的介绍+环境搭建,这一期正式开始项目学习!!
Prat 0:Raylib绘图原理
不只是Raylib,许多3D引擎的画图原理都是这样的:
创建窗口—>刷新相机(根据相机变化计算下帧时每块像素是什么颜色)—>清空画布—>绘制计算好的每个像素点的颜色—>其他(侦测按键⌨、鼠标🖱、麦克风🎤等)—>循环 /终止条件成立:结束循环
↑ 终止条件不成立:继续循环
|_______________________________________________________________________________________________________________________________↓
你可以这样理解:程序就相当于画家👨🎨,窗口就相当于一个画布🎴,画家就会不停地画一封画再擦掉,画
一封画再擦掉...每次画之前或画之后,画家👨🎨就会根据相机在空间里数据的变化(如坐标变化、朝向变化等)计
算出下一次画时画布上每个地方画的颜色;或者根据外部输入(键盘⌨、鼠标🖱等)更改画布上的每个像素的 在现实中,这个过程极其缓慢,但是计算机程序运行效率高,能做到每分钟做几十至几百次“画一封画再擦掉”
的操作,这就让我们看上去这个画布(窗口)里是不停在动的动起来的画面。这在现实中就好像我们上学时玩的手
绘翻页动图一样。每翻一页就相当于画家“画一封画再擦掉”就相当于程序“更新窗口上每个像素点的颜色然后清空
屏幕”。
接下来让我们把这个过程与程序对应起来! Part 1-1:创建项目 在你想要的地方新建一个文件夹,名字随便,比如“Raylib学习”。许多同学没有做C++项目的经验,平时做题的文件都放在一起。因为到后来,除了.CPP源文件,我们做Raylib项目时还要用到贴图文件、读写存档文件和.exe可执行文件,把几个项目的这些文件放在一起,自己难找,程序读写文件时也难找,需要项目隔离。 打开RedPandaIDE v3.1 小熊猫C++,没下载好的看我上期帖子: 【Raylib&C++教程01期】介绍+环境搭建+官网开源学习(出处: CZLJ),单击“文件”-->“新建”-->“新建C/C++文件”新建一个.cpp项目文件(手懒的可以点击菜单栏下面一行图标里的第一个 📄样子的按钮,或者按下Ctrl+N)
Part 1-2:编写控制台程序
接着,在新建的untitled1.cpp文件里写上C++源代码: - #include<bits/stdc++.h>//引入C++万能头文件
- #include<raylib.h>//引入Raylib库头文件
- using namespace std;//使用std命名空间
- int main(){
- //我的Raylib学习代码
- return 0;
- }//定义主函数
复制代码点击上方菜单栏中“运行”-->“运行 F11”或直接点击菜单栏下方一个像Windows的图标的按钮或直接按下F11就会开始编译代码。成功后如下图:
Part 1-3:循环绘图 现在,我们开始Part 0-0中的绘图思路的第一步:创建画布
在main函数里添加代码。使用InitWindow(480,360,"我的Raylib学习程序");可以创建一个长480像素、宽360像素,窗口名称为“我的Raylib学习程序”的窗口。此时,控制台窗口里输出了一大堆东西,这说明Raylib窗口创建成功了。但是这个空白窗口是持续了2秒半就没了,所以我们要在InitWindow下面加一个循环,让窗口持续 - #include<bits/stdc++.h>//引入C++万能头文件
- #include<raylib.h>//引入Raylib库头文件
- using namespace std;//使用std命名空间
- int main(){
- //我的Raylib学习代码
- InitWindow(480,360,"我的Raylib学习程序");//创建一个长480px(像素),宽360px的新窗口,名为“我的Raylib学习程序”
- while(1){
-
- }
- return 0;
- }//定义主函数
复制代码此处while循环的循环条件为1。在C++中非零为真,循环就会执行。这样写循环是永无止的,要手动关闭。你可以填写自己游戏的终止条件,比如按下ESC键、某个变量等于几之类。这个while循环结束后是return 0;那这个循环结束后程序就会结束。你可以大胆发挥,做多重循环。比如第一个循环有个登陆界面,直到用户输入密码后循环才终止,然后在写一个循环为软件界面,或是嵌套循环,外层绘制进入游戏前,里面循环绘制进入游戏后的3D环境...类似这样。 此处可以使用Raylib库自带的一个布尔函数作为循环条件:WindowShouldClose()当用户在Raylib创建的窗口中按下ESC键,这个函数返回true,不按表示不要关闭窗口返回false。这样放进while循环的()去逻辑是反的,所以可以这样写: - //头部同上
- //...
- int main(){
- //我的Raylib学习代码
- InitWindow(480,360,"我的Raylib学习窗口");
- while(!WindowShouldClose()){
- //!WindowShouldClose()就是“窗口需要关闭”不成立,这样的话整个游戏只有按下ESC或手动关闭才会跳出循环
-
- }
- return 0;
- }//定义主函数
复制代码运行效果如图:
此时你把鼠标放进窗口却是无响应,按ESC或点❌都无法关闭。这是因为我们什么都没画呢。你可以点控制台的窗口的❌关闭整个程序。 到这里,我们的“画布”部分已经写好了,我们要改改“画家”往里面绘制内容了 在while循环里加上两个函数,他们是: - BeginDrawing();//开始绘制
- //...其他绘图函数写在中间
- EndDrawing();//结束绘制
复制代码他俩的作用同他俩的名字一样,一个标志开始绘制,一个标志结束绘制。这样是为了让程序知道那一段是绘制的部分。我们所有的绘图函数调用前,都必须写上BeginDrawing();而调用完都必须写上EndDrawing(); 也就是说绘图函数必须在这两个函数中间调用。你可以用一个大括号连接他们,这样不仅好辩认,而且在俩函数中间临时定义的变量用完后都会抹掉。(如下代码块) 你可以把他俩理解为:BeginDrawing();就是“画家👨🎨”落下了笔🖊,而EndDrawing();就是“画家👨🎨”把笔🖌抬起来了。而落笔🖊和抬笔🖌两个动作之间的动作就是绘画。 - //上面代码同上
- while(!WindowShouldClose()){
- BeginDrawing();
- /*
- ...绘制图形的函数在两个函数之间编写
- */
- EndDrawing();
- }
- //下面代码同上
复制代码运行后鼠标伸进窗口就是正常的了,点×、按ESC可以关闭窗口。此时窗口是默认的一片漆黑。我们可以画东西在里面。 首先,是“画家👨🎨清屏”操作。使用ClearBackground(颜色);函数对窗口清屏为填入参数。这里需要补充一下Raylib结构体Color Color的定义如下 - typedef struct Color {
- unsigned char r; // Color red value
- unsigned char g; // Color green value
- unsigned char b; // Color blue value
- unsigned char a; // Color alpha value
- } Color;
复制代码可以看到,Color类型的变量有4个值,r、g、b、a,也就是 Red(红色值)、Green(绿色值)、Blue(蓝色值)、Alpha(透明度)。没错,这就是计算机视觉里的RGBA通道。这四个变量范围都在0~255之间。你可以用3种方法定义一个变量。 方法一: - Color a_colour;//定义一个Color类的变量a_colour
- a_colour.r=255;//红色值为满
- a_colour.g=161;//绿色值为161
- a_colour.b=0;//蓝色值为空
- a_colour.a=255;//Alpha值为满则为不透明颜色,Alpha值越低颜色越透明
复制代码方法二: - Color a_colour={255,161,0,255};//定义一个r、g、b、a值分别为255、161、0、255的Color类变量a_colour
复制代码方法三: - //当使用一个绘图函数(如清屏函数void ClearBackgroung(Color color);)时函数参数为Color类时,可以这样传参,无须刻意定义一个变量
- ClearBackground((Color){255,161,0,255});
复制代码以上3种方法均可以定义一个RGBA值为{255,161,0,255}的变量。这个值的变量表示出来的颜色为橙色。 当然,如果你懒得找每种颜色的RGBA值的话,你可以使用Raylib.h头文件里已经定义好的颜色变量。这些Raylib库自带变量如下表: - //这些可以在raylib.h里找到.你可以按Ctrl+单击上面源码里第二行里的<raylib.h>打开头文件查找
- // Some Basic Colors
- // NOTE: Custom raylib color palette for amazing visuals on WHITE background
- #define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray 译:亮灰色
- #define GRAY CLITERAL(Color){ 130, 130, 130, 255 } // Gray 译:灰色
- #define DARKGRAY CLITERAL(Color){ 80, 80, 80, 255 } // Dark Gray 译:深灰色
- #define YELLOW CLITERAL(Color){ 253, 249, 0, 255 } // Yellow 译:黄色
- #define GOLD CLITERAL(Color){ 255, 203, 0, 255 } // Gold 译:金色
- #define ORANGE CLITERAL(Color){ 255, 161, 0, 255 } // Orange 译:橙色 <-----上面方法定义的值来自这里
- #define PINK CLITERAL(Color){ 255, 109, 194, 255 } // Pink 译:粉色
- #define RED CLITERAL(Color){ 230, 41, 55, 255 } // Red 译:红色
- #define MAROON CLITERAL(Color){ 190, 33, 55, 255 } // Maroon 译:魔力红(深一些的红)
- #define GREEN CLITERAL(Color){ 0, 228, 48, 255 } // Green 译:绿色
- #define LIME CLITERAL(Color){ 0, 158, 47, 255 } // Lime 译:草绿色
- #define DARKGREEN CLITERAL(Color){ 0, 117, 44, 255 } // Dark Green 译:深绿色
- #define SKYBLUE CLITERAL(Color){ 102, 191, 255, 255 } // Sky Blue 译:天蓝色
- #define BLUE CLITERAL(Color){ 0, 121, 241, 255 } // Blue 译:蓝色
- #define DARKBLUE CLITERAL(Color){ 0, 82, 172, 255 } // Dark Blue 译:深蓝色
- #define PURPLE CLITERAL(Color){ 200, 122, 255, 255 } // Purple 译:紫色
- #define VIOLET CLITERAL(Color){ 135, 60, 190, 255 } // Violet 译:紫罗兰(淡一些的紫)
- #define DARKPURPLE CLITERAL(Color){ 112, 31, 126, 255 } // Dark Purple 译:深紫色
- #define BEIGE CLITERAL(Color){ 211, 176, 131, 255 } // Beige 译:米黄色
- #define BROWN CLITERAL(Color){ 127, 106, 79, 255 } // Brown 译:棕色
- #define DARKBROWN CLITERAL(Color){ 76, 63, 47, 255 } // Dark Brown 译:深棕色
- #define WHITE CLITERAL(Color){ 255, 255, 255, 255 } // White 译:白色
- #define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black 译:黑色
- #define BLANK CLITERAL(Color){ 0, 0, 0, 0 } // Blank (Transparent) 译:...?空颜色,值为0,0,0,0
- #define MAGENTA CLITERAL(Color){ 255, 0, 255, 255 } // Magenta 译:...?有点紫有点红有点粉的红
- #define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo) 译:...射线白?这是Raylib Logo中用的白色
复制代码所以你可以直接使用这些定义好的变量。注意:这些变量均为大写。比如ClearBackground(ORANGE); 下面我们使用ClearBackground();和另外两个绘图函数绘制出一个页面.现在暂时不需要理解另外两个函数的用法。 - #include<bits/stdc++.h>//引入C++万能头文件
- #include<raylib.h>//引入Raylib库头文件
- using namespace std;//使用std命名空间
- int main(){
- //我的Raylib学习代码
- InitWindow(480,360,"我的Raylib学习窗口");
- while(!WindowShouldClose()){
- BeginDrawing();
- {
- ClearBackground(BLUE);//“画家👨🎨”把“画布”清空为蓝色(即刷新一帧)
- /*...绘制图形的函数在两个函数之间编写*/
- DrawRectangle(100,130,280,80,YELLOW);
- DrawText("My First Raylib Program",120,160,20,RED);
- }
- EndDrawing();
- }
- return 0;
- }//定义主函数
复制代码运行效果如图:
下面Part 2-1~2-3-6我们开始系统地学习Raylib库的主要2D绘图函数. 我会以绘制内容+函数变体目录+用法+实例的流程为大家讲解。 内容有点复杂,大家坐好扶稳,~%?…,# *'☆&℃$︿★ 启动! Part 2-1:基础图形之像素点Pixel🔸 像素点这个东西是构成一切一切的基础。这里讲的像素点指的是屏幕上的像素点,很小很小的哦 如果你绘制一个像素点,那可是很难找到的。 DrawPixel函数的所有变体: |__RLAPI void DrawPixel(int posX, int posY, Color color);//(本体) |__RLAPI void DrawPixelV(Vector2 position, Color color); ·1 DrawPixel(); 用法:DrawPixel(X坐标,Y坐标,颜色); 例子: - DrawPixel(100,100,(Color){255,255,255,255});//在窗口坐标的100,100处渲染一个白色像素
- DrawText("Can you ^ see the white pixel?",50,100,12,RED);
复制代码·2 DrawPixelV() 用法:DrawPixelV(一个Vector2的变量表示x、y,一个Color变量表示颜色); 例子: - DrawPixelV((Vector2){100,100},(Color){255,255,255,255});//在窗口坐标的100,100处渲染一个白色像素
- DrawText("Can you ^ see the white pixel?",50,100,12,RED);
复制代码两次运行效果均如下: 是不是很小? Part 2-2:基础图形之线Line➿ 线是由像素组成的。Raylib中没有只规定直线or竖线,而是规定线的两端可以是任意坐标,也就是说你不用考虑线的角度,这个角度背后的计算会由Raylib底层数学代码自动执行。 线的变体有5个。你总不会喜欢一条单调、一像素细两端秃秃的线吧。 DrawLine函数的全部变体: |__RLAPI void DrawLine(int startPosX, int startPosY, int endPosX, int endPosY, Color color);//(本体) |__RLAPI void DrawLineV(Vector2 startPos, Vector2 endPos, Color color); |__RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color); |__RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); |__RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); ·1 DrawLine(); 用法:DrawLine(起始的X坐标,起始的Y坐标,结束的X坐标,结束的Y坐标,颜色); 示例: - DrawText("Cut Down!",50,100,30,RED);
- DrawLine(90,50,140,180,ORANGE);//从窗口坐标的90,50处到140,180处画一条橙色的线(只有一像素粗)
复制代码·2 DrawLineV(); 用法:DrawLineV(一个表示起始X、Y坐标的Vector2变量,一个表示结束的X、Y坐标的Vector2变量,一个表示颜色的Color变量); 示例: - Vector2 start_pos={90,50},end_pos={140,180};//起始点坐标为90,50,结束点坐标为140,180
- DrawText("Cut Down!",50,100,30,RED);
- DrawLineV(start_pos,end_pos,ORANGE);//从窗口坐标的90,50处到140,180处画一条橙色的线(只有一像素粗)
复制代码效果均如图:
·3 DrawLineEx(); Ex有增强、更好的意思,这个函数顾名思义就是DrawLine的pro版。具体它是在前一个 Part 2-3-1:基础图形之面Face之矩形Rectangle⬛
从零维的点到一维的线,接着就是二维的面了。
Part 2-3-2:基础图形之面Face之三角形Triangle▲ 三角形作为基础几何元素,在游戏开发中常用于绘制箭头、山体轮廓等。Raylib提供三种三角形绘制方式:
|__RLAPI void DrawTriangle(Vector2 v1, Vector2 v2, Vector2 v3, Color color);//实心三角形 |__RLAPI void DrawTriangleLines(Vector2 v1, Vector2 v2, Vector2 v3, Color color);//线框三角形 |__RLAPI void DrawTriangleFan(Vector2 *points, int pointCount, Color color);//扇形三角集合
·1 DrawTriangle() 示例代码: ```cpp Vector2 top = {240, 50}; Vector2 left = {140, 250}; Vector2 right = {340, 250}; DrawTriangle(top, left, right, SKYBLUE); ``` 效果:绘制实心天蓝色三角形,顶点坐标分别为(240,50)、(140,250)、(340,250)
·2 DrawTriangleLines() 示例代码: ```cpp DrawTriangleLines(top, left, right, DARKBLUE); ``` 效果:用深蓝色线条勾勒三角形轮廓
·3 DrawTriangleFan() 适用于批量绘制共顶点的三角形: ```cpp Vector2 points[] = {{240,120}, {200,200}, {280,200}, {240,280}}; DrawTriangleFan(points, 4, PURPLE); ``` 效果:以第一个点(240,120)为公共顶点,绘制三个相连的紫色三角形
Part 2-3-3:基础图形之面Face之圆Circle⚫ 圆形在游戏开发中常用于碰撞检测、技能范围等场景:
|__RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); |__RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); |__RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); |__RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color);
·1 DrawCircle() ```cpp DrawCircle(240, 180, 80.0f, YELLOW); // 在(240,180)绘制半径80的黄色实心圆 ```
·2 DrawCircleSector() ```cpp DrawCircleSector((Vector2){240,180}, 60, 45, 135, 36, ORANGE); // 绘制45°到135°的扇形 ```
·3 DrawCircleGradient() ```cpp DrawCircleGradient(240, 180, 60, RED, BLANK); // 从中心红色渐变到边缘透明 ``` Part 2-3-4:基础绘图之面Face之椭圆Ellipse🥚
椭圆常用于绘制倾斜的圆
|__RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); |__RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color);
示例: ```cpp DrawEllipse(240, 180, 100, 60, PINK); // 绘制横轴100px,纵轴60px的粉色椭圆 DrawEllipseLines(240, 180, 80, 40, MAROON); // 绘制线框椭圆 ```
环形元素常用于制作加载动画:
|__RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color);
示例: ```cpp DrawRing((Vector2){240,180}, 50, 70, 0, 270, 36, BLUE); // 绘制270°的蓝色圆环 ```
Part 2-3-6:基础绘图之面Face之多边形Poly🗯
多边形是构建复杂图形的基础
|__RLAPI void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color); |__RLAPI void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color);
示例: ```cpp DrawPoly((Vector2){240,180}, 6, 80, 30.0f, GREEN); // 绘制旋转30度的六边形 DrawPolyLines((Vector2){240,180}, 5, 60, 0, DARKGREEN); // 五边形线框 ```
Part 3:Raylib 2D绘图函数速查表 为方便查阅,整理核心绘图函数速查:
【基础图形】 - 点:DrawPixel(), DrawPixelV() - 线:DrawLineEx()支持设置粗细,DrawLineBezier()绘制贝塞尔曲线 - 形状:所有图形均有Fill(填充)和Lines(线框)两种版本
【高级特性】 - 渐变:DrawRectangleGradient(), DrawCircleGradient() - 自定义图形:通过组合基础图形+数学计算实现复杂效果 - 批绘制:使用BeginBlendMode()开启混合模式制作透明效果
【性能提示】 1. 避免在循环内创建Color/Vecter2对象 2. 复杂图形优先使用rlgl模块的底层绘制 3. 静态元素使用纹理渲染替代实时绘制
下期预告:2D绘制Part2-2D贴图加载绘制!记得用F11运行代码观察动态效果哦~(◕ᴗ◕✿) |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|