-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
raylib syntax analysis
raylib is a simple and easy-to-use library to enjoy videogames programing... but, what makes the library simple and easy-to-use? For many users the first approach to the library is through its API, so, here it is a small analysis of the API from a syntactic point of view.
How are the functions structured? Which words are used? How many parameters are exposed? How intuitive is it to understand or even guess every function/structure/enum when required? Syntax is the set of rules, principles, and processes that govern the structure of sentences in a given language and defines how we understand it.
To do this analysis a raylib.h
parser has been created. That parser dissects API into small pieces to automatize some parts of the analysis; actually, this parser can also be useful for automatic docs and bindings generation!
The analysis is organized in 3 parts, one for each API element analyzed:
- Functions: Instructions called by the users to make things happen
-
Structures (
struct
): Data types to organize information packages -
Enumerators (
enum
): Sequence of named values used for convenience
Latest raylib version (3.8-dev) exposes a total of 470 functions, a relatively small API for a gamedev library.
All raylib functions try to follow this syntactic structure:
<Verb><Subject><Complement>();
More specifically, every syntactic element implies:
<Action><Object><Attribute/State>(); // Do an Action over some Object Attribute/State
Following this syntactic structure is what make the API comprehensive, intuitive, easy-to-remember and easy-to-use.
Checking the available functions with more detail, they can be divided into 3 groups:
- Functions following a common pattern
- Functions operating over specific type of data
- Functions with unique pattern
Most of the functions of the library go into this first group (359 functions), there is some common <Action>
that prepends the name of most of the functions:
pattern | function format | API count | examples |
---|---|---|---|
01 | void Init*() |
3 |
InitWindow() , InitAudioDevice() , InitAudioStream()
|
02 | void Close*() |
3 |
CloseWindow() , CloseAudioDevice() , CloseAudioStream()
|
03 | void Begin*() |
8 |
BeginDrawing() , BeginBlendMode()
|
04 | void End*() |
8 |
EndDrawing() , EndBlendMode()
|
05 | TYPE Get*() |
79 |
GetKeyPressed() , GetMouseX() , GetRayCollision*()
|
06 | void Set*() |
46 |
SetWindowTitle() , SetTargetFPS() , SetMouseScale()
|
07 | bool Is*() |
33 |
IsKeyPressed() , IsGamepadAvailable() , IsSoundPlaying()
|
08 | TYPE Gen<TYPE>*() |
20 |
GenImage*() , GenMesh*()
|
09 | TYPE Load<TYPE>*() |
33 |
LoadImage*() , LoadTexture*() , LoadSound*()
|
10 | void Unload<TYPE>*(<TYPE>) |
21 |
UnloadImage() , UnloadTexture() , UnloadSound()
|
11 | void Update<TYPE>*(<TYPE>, *) |
8 |
UpdateTexture() , UpdateCamera()
|
12 | bool Save*() |
3 |
SaveFileData() , SaveFileText() , SaveStorageValue()
|
13 | bool Export*() |
5 |
ExportImage() , ExportImageAsCode() , ExportMesh() , ExportWave() , ExportWaveAsCode()
|
14 | void Draw*() |
79 |
DrawRectangle() , DrawTexture*() , DrawModel*()
|
15 | bool Check*() |
10 |
CheckCollisionRecs() , CheckCollisionCircles() , CheckCollisionBoxSphere()
|
Those functions (68 in total) operate over a specific data type, so, it was decided to prepend the DataType
to the function name, they are an exception over the main syntax rule followed by the API:
pattern | function format | API count | examples |
---|---|---|---|
01 | TYPE Color*() |
7 |
ColorAlpha() , ColorFromHSV() , ColorToHSV()
|
02 |
Image Image*() void Image*()
|
40 |
ImageFormat() , ImageCrop() , ImageResize() , ImageFlipVertical()
|
03 | TYPE Text*() |
16 |
TextFormat() , TextReplace() , TextSplit() , TextToLower()
|
04 | Mesh*() |
2 |
MeshTangents() , MeshBinormals()
|
05 |
Wave Wave*() void Wave*()
|
3 |
WaveFormat() , WaveCopy() , WaveCrop()
|
NOTE: Maybe some of them are renamed in the future for consistency.
Remaining functions (43 in total) follow a unique pattern, still, most of them follow the standard syntax pattern of <Verb><Subject><Complement>
.
// core.c
WindowShouldClose(); // Not following pattern
ClearWindowState();
ToggleFullscreen();
MaximizeWindow();
MinimizeWindow();
RestoreWindow();
ShowCursor();
HideCursor();
EnableCursor();
DisableCursor();
ClearBackground();
TakeScreenshot();
TraceLog(); // Not following pattern
MemAlloc(); // Data-type pattern?
MemRealloc(); // Data-type pattern?
MemFree(); // Data-type pattern?
FileExists(); // Not following pattern -> IsFileAvailable()?
DirectoryExists(); // Not following pattern -> IsDirectoryAvailable()?
ClearDirectoryFiles();
ChangeDirectory();
ClearDroppedFiles();
CompressData();
DecompressData();
OpenURL();
// textures.c
Fade(); // Superseded by ColorAlpha()
// text.c
MeasureText();
MeasureTextEx();
CodepointToUtf8(); // Not following pattern -> EncodeCodepointAsUtf8()?
// models.c
UploadMesh();
// raudio.c
PlaySound();
StopSound();
PauseSound();
ResumeSound();
PlaySoundMulti();
StopSoundMulti();
PlayMusicStream();
StopMusicStream();
PauseMusicStream();
ResumeMusicStream();
PlayAudioStream();
StopAudioStream();
PauseAudioStream();
ResumeAudioStream();
A part from the function prefixes that we can find in many functions names (1. common patterns), we can also find some common suffixes used by several functions:
// Suffix: *Ex() -> Used for "Extended" versions of same name functions
Vector2 GetWorldToScreenEx();
void DrawLineEx();
void DrawRectangleGradientEx();
void DrawRectangleLinesEx();
void DrawPolyLinesEx();
Image ImageTextEx();
void ImageDrawTextEx();
void DrawTextureEx();
Font LoadFontEx();
void DrawTextEx();
void DrawTextRecEx();
Vector2 MeasureTextEx();
void DrawSphereEx();
void DrawModelEx();
void DrawModelWiresEx();
// Suffix: *Pro() -> Used for "Professional" versions of same name functions, mode advanced than "Ex"
void DrawRectanglePro();
void DrawTexturePro();
void DrawBillboardPro();
// Suffix: *Rec() -> Used for functions requiring a "Rectangle" as one main input parameter
void DrawRectangleRec();
bool CheckCollisionCircleRec();
bool CheckCollisionPointRec();
Rectangle GetCollisionRec();
void ImageDrawRectangleRec();
void UpdateTextureRec();
void DrawTextureRec();
void DrawTextRec();
void DrawBillboardRec();
// Suffix: *FromMemory() -> Used for functions loading data "from memory" instead of from files
Shader LoadShaderFromMemory();
Image LoadImageFromMemory();
Font LoadFontFromMemory();
Wave LoadWaveFromMemory();
Music LoadMusicStreamFromMemory();
// Suffix: *Callback() -> Used for functions setting a callback function
void SetTraceLogCallback();
void SetLoadFileDataCallback();
void SetSaveFileDataCallback();
void SetLoadFileTextCallback();
void SetSaveFileTextCallback();
Most functions in raylib use a maximum of 4 words on its name. I think that's a good trade-off to remember the name of the function. Still, there is a small set of functions that go beyond that limit:
count | function | words | comments |
---|---|---|---|
001 | GetWindowScaleDPI() |
4 | Acronym used |
002 | GetWorldToScreenEx() |
5 | |
003 | GetWorldToScreen2D() |
5 | |
004 | GetScreenToWorld2D() |
5 | |
005 | SetTargetFPS() |
5 | Acronym used |
006 | SetLoadFileDataCallback() |
5 | Callback function |
007 | SetSaveFileDataCallback() |
5 | Callback function |
008 | SetLoadFileTextCallback() |
5 | Callback function |
009 | SetSaveFileTextCallback() |
5 | Callback function |
010 | GetFileNameWithoutExt() |
5 | |
011 | SetCameraSmoothZoomControl() |
5 | TODO: Review!!! |
012 | ImageToPOT() |
3 | Acronym used |
013 | ImageRotateCCW() |
3 | Acronym used |
014 | ColorToHSV() |
3 | Acronym used |
015 | ColorFromHSV() |
3 | Acronym used |
016 | LoadMusicStreamFromMemory() |
5 | *FromMemory() set of functions |
017 | SetAudioStreamBufferSizeDefault() |
6 | TODO: Review!!! |
Most functions are limited to 5 or less input parameters. I think 5 or less parameters is a good number to keep the function simple and rememberable, more than 5 usually requires the user to use some kind of intellisense system. The number of parameters is a key point for raylib simplicity, still, there are some functions requiring 6 or more parameters:
count | function | param count | comments |
---|---|---|---|
001 | SetCameraMoveControls() |
6 | |
002 | DrawCircleSector() |
6 | |
003 | DrawCircleSectorLines() |
6 | |
004 | DrawRing() |
7 | WARNING: >6 parameters |
005 | DrawRingLines() |
7 | WARNING: >6 parameters |
006 | DrawRectangleGradientV() |
6 | |
007 | DrawRectangleGradientH() |
6 | |
008 | DrawPolyLinesEx() |
6 | |
009 | GenImageChecked() |
6 | |
010 | ImageResizeCanvas() |
6 | |
011 | ImageDrawLine() |
6 | |
012 | ImageDrawRectangle() |
6 | |
013 | ImageDrawText() |
6 | |
014 | ImageDrawTextEx() |
7 | WARNING: >6 parameters |
015 | DrawTextureTiled() |
7 | WARNING: >6 parameters |
016 | DrawTexturePro() |
6 | |
017 | DrawTextureNPatch() |
6 | |
018 | DrawTexturePoly() |
6 | |
019 | LoadFontFromMemory() |
6 | |
020 | LoadFontData() |
6 | |
021 | GenImageFontAtlas() |
6 | |
022 | DrawTextEx() |
6 | |
023 | DrawTextRec() |
7 | WARNING: >6 parameters |
024 | DrawTextRecEx() |
11 | TODO: Review!!! |
025 | DrawCubeTexture() |
6 | |
026 | DrawCylinder() |
6 | |
027 | DrawCylinderWires() |
6 | |
028 | DrawModelEx() |
6 | |
029 | DrawModelWiresEx() |
6 | |
030 | DrawBillboardRec() |
6 | |
031 | DrawBillboardPro() |
8 | WARNING: >6 parameters |
Note that only 7 function out of 470 require more than 6 parameters. This is what makes raylib so special.
raylib defines a total of 31 struct data types, most of those structs use a single word to define the type and some of them use two words.
raylib tries to provide generic data types for most common use scenarios, only some of those structs are required for specific examples: NPatchInfo
, VrDeviceInfo
, VrStereoConfig
.
About the fields contained in every struct, it was tried to just include the minimum required but when moving into 3d Mesh
and Model
, the amount of required data grows fast. For a more detailed analysis on data structures (not purely syntactic), check Wiki entry: raylib data structures.
count | struct name | fields count | comments |
---|---|---|---|
01 | Vector2 |
2 | |
02 | Vector3 |
3 | |
03 | Vector4 |
4 | |
04 | Matrix |
4 | |
05 | Color |
4 | |
06 | Rectangle |
4 | |
07 | Image |
5 | |
08 | Texture |
5 | |
09 | RenderTexture |
3 | 2 words name |
10 | NPatchInfo |
6 | 2 words name |
11 | CharInfo |
5 | |
12 | Font |
6 | |
13 | Camera3D |
5 | |
14 | Camera2D |
4 | |
15 | Mesh |
15 | |
16 | Shader |
2 | |
17 | MaterialMap |
3 | 2 words name |
18 | Material |
3 | |
19 | Transform |
3 | |
20 | BoneInfo |
2 | |
21 | Model |
9 | |
22 | ModelAnimation |
4 | 2 words name |
23 | Ray |
2 | |
24 | RayCollision |
4 | 2 words name |
25 | BoundingBox |
2 | 2 words name |
26 | Wave |
5 | |
27 | AudioStream |
4 | 2 words name |
28 | Sound |
2 | |
29 | Music |
5 | |
30 | VrDeviceInfo |
10 | 3 words name |
31 | VrStereoConfig |
8 | 3 words name |
raylib defines 20 enumerations for convenience. Enum names and contained value names are kept simple and clear. Most of those values are only required to be used by some very specific functions, for a detailed list check Wiki entry: raylib enumerated types.
Personally, I tried to avoid enum values requirement as much as possible within the library, actually, only 35 function in raylib could require using some enum value on some of its input parameters.
count | enum name | values count | comments |
---|---|---|---|
01 | ConfigFlags |
14 | |
02 | TraceLogLevel |
8 | 3 words name |
03 | KeyboardKey |
110 | |
04 | MouseButton |
8 | |
05 | MouseCursor |
11 | |
06 | GamepadButton |
18 | |
07 | GamepadAxis |
6 | |
08 | MaterialMapIndex |
11 | 3 words name |
09 | ShaderLocationIndex |
26 | 3 words name |
10 | ShaderUniformDataType |
9 | 4 words name |
11 | PixelFormat |
21 | |
12 | TextureFilter |
6 | |
13 | TextureWrap |
4 | |
14 | CubemapLayout |
6 | |
15 | FontType |
3 | |
16 | BlendMode |
6 | |
17 | Gesture |
11 | |
18 | CameraMode |
5 | |
19 | CameraProjection |
2 | |
20 | NPatchLayout |
3 | 3 words name |
One of the goals of raylib is being simple and easy-to-use and many hours have been put into the API design; thinking about the best name for every function, the minimum number of parameters required, the right name of each parameter, the data types structure and more.
I think this syntax analysis is useful to see what worked for raylib library and also help to improved it.
www.raylib.com | itch.io | GitHub | Discord | YouTube
- Architecture
- Syntax analysis
- Data structures
- Enumerated types
- External dependencies
- GLFW dependency
- libc dependency
- Platforms and graphics
- Input system
- Default shader
- Custom shaders
- Coding conventions
- Integration with other libs
- Working on Windows
- Working on macOS
- Working on GNU Linux
- Working on Chrome OS
- Working on FreeBSD
- Working on Raspberry Pi
- Working for Android
- Working for Web (HTML5)
- Working on exaequOS Web Computer
- Creating Discord Activities
- Working anywhere with CMake
- CMake Build Options
- raylib templates: Get started easily
- How To: Quick C/C++ Setup in Visual Studio 2022, GCC or MinGW
- How To: C# Visual Studio Setup
- How To: VSCode
- How To: Eclipse
- How To: Sublime Text
- How To: Code::Blocks