R-Bus
A horizontally scrolling shooter multiplayer arcade video game.
✈ Cross-Platform
R-Bus is designed to be cross-platform, available on Windows, Linux, and MacOS.
Screenshots
⏬ Installation
Installing R-Bus is easy.
Follow the instructions below based on your preferred method and operating system.
There are two ways to install R-Bus:
- 📦 From packaged binary: Prefer this method if you want to avoid downloading dependencies and compiling R-Bus.
- 🔨 From source: Choose this method if you're comfortable with handling dependencies and compilation.
👾 How to play
Key | Action |
---|---|
Left arrow | Move left |
Right arrow | Move right |
Up arrow | Move up |
Down arrow | Move down |
Space | Shoot normal bullets |
C | Shoot fast bullets |
V | Shoot bouncing bullets |
B | Shoot penetrating bullets |
Esc | Go back |
📖 Documentation
📑 License
R-Bus is licensed under the MIT License.
Created By X-L-R-G-B
This project is a collaborative effort by:
Installation
There are two ways to install R-Bus:
- From packaged binary
- From source
Prefer the packaged binary if you want to find yoursel downloading some dependencies and compiling R-Bus. Prefer the packaged binary if you are not a nerd
Else, go ahead and install R-Bus from source.
Packaged binary
Now, let's install R-Bus from packaged binary.
Please select the right page for your operating system
GNU/Linux
curl -Lo 'r-type-linux.sh' \
'https://github.com/X-R-G-B/R-Bus/releases/latest/download/r-type-linux.sh'
# The following line will Accepts the license
# extract the packaged binary and its dependencies
yes y | bash 'r-type-linux.sh'
cd R-Type-Linux
# your binaries are in ./bin/
ls ./bin/
:warnings: When you want to move this binaries, please move all the folder R-Type-Linux
MacOs
Download https://github.com/X-R-G-B/R-Bus/releases/latest/download/r-type-macos.dmg
And launch the file downloaded.
Windows
Download https://github.com/X-R-G-B/R-Bus/releases/latest/download/r-type-windows.exe
And execute the file downloaded.
Source
Now, let's install R-Bus from source.
Please select the right page for your operating system
GNU/Linux
- You need to have cmake, and a c++ compiler
hint: you can use https://github.com/aminya/setup-cpp to install it
- Git clone the project or download the .zip
git clone -b main https://github.com/X-R-G-B/R-Bus.git R-Bus-main
OR
wget https://github.com/X-R-G-B/R-Bus/archive/main.zip && unzip R-Bus-main.zip
- Change directory
cd R-Bus-main || "Failed to cd to R-Type-Linux, please open an issue: 'https://github.com/X-R-G-B/R-Bus/issues/new?assignees=&labels=bug&projects=&template=install-failed.yml&title=%5BFAIL+INSTALL%5D+-+Title'"
- Install dependencies
sudo ./scripts/install-deps-linux.sh
- Build the project
./scripts/compil.sh
Well Done you have your binaries!
Now, you can run it but dont move it anywhere
MacOs
- You need to have cmake, and a c++ compiler
hint: you can use https://github.com/aminya/setup-cpp to install it
- Git clone the project or download the .zip
git clone -b main https://github.com/X-R-G-B/R-Bus.git R-Bus-main
OR
wget https://github.com/X-R-G-B/R-Bus/archive/main.zip && unzip R-Bus-main.zip
- Change directory
cd R-Bus-main || "Failed to cd to R-Type-Linux, please open an issue: 'https://github.com/X-R-G-B/R-Bus/issues/new?assignees=&labels=bug&projects=&template=install-failed.yml&title=%5BFAIL+INSTALL%5D+-+Title'"
- Install dependencies
./scripts/install-deps-macos.sh
- Build the project
./scripts/compil.sh
Well Done you have your binaries!
Now, you can run it but dont move it anywhere
Windows
- You need to have cmake, and a c++ compiler
hint: you can use https://github.com/aminya/setup-cpp to install it
- Git clone the project or download the .zip
git clone -b main https://github.com/X-R-G-B/R-Bus.git R-Bus-main
OR
wget https://github.com/X-R-G-B/R-Bus/archive/main.zip && unzip R-Bus-main.zip
- Change directory
cd R-Bus-main || "Failed to cd to R-Bus-main, please open an issue: 'https://github.com/X-R-G-B/R-Bus/issues/new?assignees=&labels=bug&projects=&template=install-failed.yml&title=%5BFAIL+INSTALL%5D+-+Title'"
- Install dependencies
./scripts/install-deps-windows.ps1 --interactive
- Build the project
./scripts/compil.ps1 --no-tidy
Well Done you have your binaries!
Now, you can run it but dont move it anywhere
Troubleshooting
If you have any errors, please let us know.
You can fill an issue here https://github.com/X-R-G-B/R-Bus/issues/new/choose and choose a category.
CONTRIBUTING
Here is the instructions if you want to contribute to the project
Submitting a pull request
- Fork and clone the repository
- Create a new branch: See 'creating a branch'
- Make your change, add tests, and make sure the tests still pass
- Make sure that the code is codeQL compliant
- Make sure that you had documentation to the new added code (obviously compliant with our doc)
- Push to your fork and submit a pull request with correct labels added if necessary
- Wait for your pull request to be reviewed and merged.
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
- The security of your code is analysed by codeQL the CI might fail if it's not secure.
- The style of your code must respect the clang-format style.
Normally we have the .clang-format if you want for your IDE. If you don't want to use the .clang-format intergration in your IDE, you can still
launch the script format.sh (Linux and MacOs) or format.ps1 (Windows), this will format automaticlly your code.
/!\ Warning ! If your code is not clang format compliant your CI will fail and your pr will be refused. - Write tests.
- Keep your change as focused as possible.
If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. - Write a commit message that respect the commit norm that we have, see 'Commit message norm'.
Commit message norm
- Define your context.
- CICD
- CLIENT-GAME
- DOCUMENTATION
- POC
- NETWORK
- SERVER
- Followed by ":" + "action verb"
- : Add
- : Fix
- : Update
- etc ...
- Explain what your commit introduce
- new feature for client-game
- bug inside network
- documentation for CLIENT-GAME
- etc...
- Just after your commit message put a newline to indicate semver
- PATCH (If your commit introduce a bug fix)
- MINOR (If your commit introduce a new feature)
- MAJOR (If your commit introduce a new feature and the older version doesn't work anymore)
/!\ Warning ! If your commit message doesn't respect those rules your pr might not be accepted
Creating a branch
If you need to contribute to our project, you will need to create a branch to submit a pull request.
Basicly, you must indicate what is the context of your branch followed by a '/'.
For exemple :
- feature/BRANCH-NAME
- doc/BRANCH-NAME
- refactor/BRANCH-NAME
- etc...
That's all you need to do to create a valid branch.
CICD
Compilation test
When you modify the src
directory, the workflow https://github.com/X-R-G-B/R-Bus/actions/workflows/compil.yml will run
Documentation
When you modify the docs
directory or book.tml
, the workflow https://github.com/X-R-G-B/R-Bus/actions/workflows/documentation.yml will run
Format
The code is automatically formatted by the workflow https://github.com/X-R-G-B/R-Bus/actions/workflows/format.yml when your create a pull request.
If it can't be automatically formated, please use clang-format with the script ./scripts/format.sh
or ./scripts/format.ps1
Release
On the main branch, and the dev branch, the workflow https://github.com/X-R-G-B/R-Bus/actions/workflows/release.yml will run
In dev branch it don't push the packaged binary or the source code to a release but in the workflow artifact
Network
The communication protocol is documented with an RFC document.
Communication Between Client and Server
------------------------------------------------------------------------------
Abstract
This document describes the communication between the client and the
server for the R-Bus game.
If you want to implement a custom client or server, please follow
miticulously these RFC.
------------------------------------------------------------------------------
Copyright Notice
This document is licensed under the MIT License with all resources used by
the R-Bus game.
------------------------------------------------------------------------------
Table of Contents
1. Introduction
2. Network Packet Global Structure
2.1. Header
2.2. Body
2.2.1. Action Header
2.2.2. Action Body
3. Network Packet Actions
3.1. Client -> Main Server
3.1.1. CONNECT_MAIN_SERVER
3.1.2. LIST_LOBBY
3.1.3. CREATE_LOBBY
3.2. Client -> Lobby Server
3.2.1. CONNECT_LOBBY
3.2.2. INIT
3.2.3. READY
3.2.4. POSITION_RELATIVE
3.2.5. POSITION_ABSOLUTE
3.2.6. NEW_MISSILE
3.2.7. LIFE_UPDATE
3.2.8. ENEMY_DEATH
3.2.9. PLAYER_DEATH
3.2.10. MISSILE_DEATH
3.2.11. DISCONNECT_LOBBY
3.3. Main Server -> Client
3.3.1. CONNECT_MAIN_SERVER_RESP
3.3.2. LIST_LOBBY
3.4. Lobby Server -> Client
3.4.1. CONNECT_LOBBY_RESP
3.4.2. START_WAVE
3.4.3. LIFE_UPDATE
3.4.4. ENEMY_DEATH
3.4.5. NEW_ENEMY
3.4.6. NEW_PLAYER
3.4.6. NEW_MISSILE
3.4.8. POSITION_ABSOLUTE_BROADCAST
3.4.9. POSITION_RELATIVE_BROADCAST
3.4.10. PLAYER_DEATH
3.4.11. MISSILE_DEATH
3.4.12. END_GAME
3.5. Lobby Server -> Main Server
3.5.1. INFO_LOBBY
4. References
3.1. R-Bus
3.2. RFC
5. Appendix
5.1. Header
5.1.1. Message
5.1.1. Network
------------------------------------------------------------------------------
1. Introduction
The purpose of this document is to facilitate the implementation of custom
clients and servers for the R-Bus game, as well as for the developers of
the R-Bus game to know how to develope further new actions for the game.
This is intended to be a reference for the developers of the R-Bus game.
This is not intended to be a reference for the players of the R-Bus game.
------------------------------------------------------------------------------
2. Network Packet Global Structure
The packets are compressed using the zstd library before being sent over
the network using the UDP protocol. Upon receipt, the packets are
decompressed using the zstd library
To add some better reliability, each packet sent has a header with some
information about all the packets received.
2.1. Header
The header is composed of the following fields:
- `magick1`
- `ids_received`
- `last_id_received`
- `id`
- `nb_action`
- `magick2`
*** Magick1
This field is used to know the packet received is a packet sent by and for
the R-Bus game.
This field must be of size 1 byte.
This field must be equal to the ascii `\x01`
*** Ids Received
This field is one part of the header that helps to achieve the reliability
we need to have to ensure all the important packets are received.
This field must be of size 4 bytes.
This field is an unsigned integer (so starting from 0 to 2^32)
To construct these field, here are an example of its implementation in
pseudo-code:
- The value of `ids_received` is zero'd
- Start a loop with a counter that starts at `last_id_received` and end at
`last_id_received - 16`
- Shift all the bit of `ids_received` to the left
- If the packet with the id of the value of the counter is received, add 1
to the `ids_received`
- End the loop
*** Last Id Received
This field is one part of the header that helps to achieve the reliability
we need to have to ensure all the important packets are received.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field need to be set to the last id of the packet received.
*** Id
This field is one part of the header that helps to achieve the reliability
we need to have to ensure all the important packets are received.
This field must be of size 4 bytes.
This field is an unsigned integer (so starting from 0 to 2^32)
This field will start from 0 at start of the connection, and each time a
packet is sent, it will be incremented by 1.
When the maximum id is reached, it will be reset to 0.
*** Nb Action
This field is used to know how many action have been sent in the same packet.
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
*** Magick2
This field is used to know the packet received is a packet sent by and for
the R-Bus game.
This field must be of size 1 byte.
This field must be equal to the ascii `\x03`
2.2. Body
The body is composed of `n` actions. The number of actions that have
been sent is in the `nb_action` field in the header of the packet.
2.2.1. Action Header
The action header is composed of the following fields:
- `magick`
*** Magick
This field describe how to interpret the action body.
This field must be of size 4 byte.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
2.2.2. Action Body
The next bytes in the packet are specified in the list of available
actions.
------------------------------------------------------------------------------
3. Network Packet Actions
This section specify each action that a Client, Main Server or Lobby Server
can send.
3.1. Client -> Main Server
3.1.1. CONNECT_MAIN_SERVER
To understand this action, the action header `magick` must be equal to `19`
The action body is composed of the following fields:
- `magick`
*** Magick
This field help to know the packet is realy a connect action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x18`
3.1.2. LIST_LOBBY
To understand this action, the action header `magick` must be equal to `16`
The action body is composed of the following fields:
- `magick`
*** Magick
This field help to know the packet is realy a list lobby action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x16`
3.1.3. CREATE_LOBBY
To understand this action, the action header `magick` must be equal to `23`
The action body is composed of the following fields:
- `magick`
- `name`
- `gameType`
- `maxNbPlayer`
- `ip`
- `port`
*** Magick
This field help to know the packet is realy a create lobby action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x13`
*** Name
This field correspond to the name of the lobby.
This field must be of size 32 bytes.
Each byte is a signed integer (so starting from -((2^8)/2) to
+(((2^8)/2)-1)).
Each byte correspond to the ascii character.
After the last byte that you set, you must add a null character.
The null character is the ascii character `\x00`
The null character must be on the range of the 32 bytes.
*** Game Type
This field correspond to the type of the game created.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
This field must be equal to one of this value:
- 0 : classic game
*** Max Nb Player
This field correspond to the maximum number of player in the lobby.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
*** IP
This field correspond to the ip of the Main Server.
This field must be of size 16 bytes.
Each byte is a signed integer (so starting from -((2^8)/2) to
+(((2^8)/2)-1)).
Each byte correspond to the ascii character.
After the last byte that you set, you must add a null character.
The null character is the ascii character `\x00`
The null character must be on the range of the 16 bytes.
*** Port
This field correspond to the port of the Main Server.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
3.2. Client -> Lobby Server
3.2.1. CONNECT_LOBBY
To understand this action, the action header `magick` must be equal to `20`
The action body is composed of the following fields:
- `magick`
*** Magick
This field help to know the packet is realy a connect lobby action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x19`
3.2.2. INIT
To understand this action, the action header `magick` must be equal to `1`
This packet initialize the connection to the lobby, and by extension,
register to the game that will be created in that lobby.
The Lobby Server will respond to this packet with a `NEW_PLAYER` (3.4.5.)
action.
The action body is composed of the following fields:
- `magick`
*** Magick
This field help to know the packet is realy an init action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x06`
3.2.3. READY
To understand this action, the action header `magick` must be equal to `2`
This packet tell the Lobby Server that the client is ready to play.
The Lobby Server will wait that all players connected are ready to start
the game.
The action body is composed of the following fields:
- `magick`
*** Magick
This field help to know the packet is realy a ready action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x17`
3.2.4. POSITION_RELATIVE
To understand this action, the action header `magick` must be equal to `7`
The action body is composed of the following fields:
- `magick`
- `x`
- `y`
*** Magick
This field help to know the packet is realy a position relative action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x08`
*** X
This field correspond to the difference between the current position of
the client and the position of the client 10 milliseconds before.
This field must be of size 1 bytes.
This field is signed (so starting from -((2^8)/2) to +(((2^8)/2)-1))
*** Y
This field correspond to the difference between the current position of
the client and the position of the client 10 milliseconds before.
This field must be of size 1 bytes.
This field is signed (so starting from -((2^8)/2) to +(((2^8)/2)-1))
3.2.5. POSITION_ABSOLUTE
To understand this action, the action header `magick` must be equal to `8`
The action body is composed of the following fields:
- `magick`
- `x`
- `y`
*** Magick
This field help to know the packet is realy a position absolute action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x09`
*** X
This field correspond to the absolute position of the client.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Y
This field correspond to the absolute position of the client.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
3.2.6. NEW_MISSILE
To understand this action, the action header `magick` must be equal to `9`
The action body is composed of the following fields:
- `magick`
- `x`
- `y`
- `missileId`
- `missileHealth`
- `missileType`
*** Magick
This field help to know the packet is realy a new bullet action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0d`
*** X
This field correspond to the absolute position of the bullet when created.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Y
This field correspond to the absolute position of the bullet when created.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Missile ID
This field correspond to the ID of the missile.
This is useless because this is the server that will set the ID of the
missile.
This field must be of size 4 byte.
This field must be equal to `0`
*** Missile Health
This field correspond to the health of the missile.
This is useless because this is the server that will set the health of the
missile.
This field must be of size 4 byte.
This field must be equal to `0`
*** Missile Type
This field correspond to the type of the missile.
This field must be of size 4 byte.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
This field must be equal to one of this value:
- 0 : classic
- 1 : fast
- 2 : bounce
- 3 : perforant
3.2.7. LIFE_UPDATE
To understand this action, the action header `magick` must be equal to `5`
The action body is composed of the following fields:
- `magick`
- `playerId`
- `hp`
*** Magick
This field help to know the packet is realy a life update action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0b`
*** Player ID
This field correspond to the ID of the client (the server will verify it).
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each client.
*** HP
This field correspond to the life of the client.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
3.2.8. ENEMY_DEATH
To understand this action, the action header `magick` must be equal to `6`
The action body is composed of the following fields:
- `magick`
- `ennemyId`
*** Magick
This field help to know the packet is realy an enemy death action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0c`
*** Ennemy ID
This field correspond to the ID of the ennemy that has been killed.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each ennemy.
3.2.9. PLAYER_DEATH
To understand this action, the action header `magick` must be equal to `14`
The action body is composed of the following fields:
- `magick`
- `playerId`
*** Magick
This field help to know the packet is realy a player death action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x11`
*** Player ID
This field correspond to the ID of the player that has been killed.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each player.
3.2.10. MISSILE_DEATH
To understant This action, the aciton header `magick` must be equal to `15`
The action body is composed of ther following fields:
- `magick`
- `missileId`
*** Magick
This field help to know the packet is realy missile death action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x1c`
*** Missile ID
This field correspond to the ID of the missile that died
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each missile.
3.2.11. DISCONNECT_LOBBY
To understant this aciton, the action header `magick` must be equal to `22`
The action body is composed of the following fields:
- `magick`
*** Magick
This field help to know the packet is realy a disconnect lobby action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x1b`
3.3. Main Server -> Client
3.3.1. CONNECT_MAIN_SERVER_RESP
To understand this action, the action header `magick` must be equal to `20`
The action body is composed of the following fields:
- `magick`
*** Magick
This field help to know the packet is realy a connect main server response
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x14`
3.3.2. LIST_LOBBY
To understand this action, the action header `magick` must be equal to `20`
The action body is composed of the following fields:
- `magick`
- `name`
- `maxNbPlayer`
- `gameType`
- `lobbyIp`
- `lobbyPort`
- `ownerIp`
- `ownerPort`
*** Magick
This field help to know the packet is realy a list lobby action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x12`
*** Name
This field correspond to the name of the lobby.
This field must be of size 32 bytes.
Each byte is a signed integer (so starting from -((2^8)/2) to
+(((2^8)/2)-1)).
Each byte correspond to the ascii character.
After the last byte that you set, you must add a null character.
The null character is the ascii character `\x00`
The null character must be on the range of the 32 bytes.
*** Max Nb Player
This field correspond to the maximum number of player in the lobby.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
*** Game Type
This field correspond to the type of the game created.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
This field must be equal to one of this value:
- 0 : classic game
*** Lobby IP
This field correspond to the ip of the Lobby Server.
This field must be of size 16 bytes.
Each byte is a signed integer (so starting from -((2^8)/2) to
+(((2^8)/2)-1)).
Each byte correspond to the ascii character.
After the last byte that you set, you must add a null character.
The null character is the ascii character `\x00`
The null character must be on the range of the 16 bytes.
*** Lobby Port
This field correspond to the port of the Lobby Server.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Owner IP
This field correspond to the ip of the Main Server.
This field must be of size 16 bytes.
Each byte is a signed integer (so starting from -((2^8)/2) to
+(((2^8)/2)-1)).
Each byte correspond to the ascii character.
After the last byte that you set, you must add a null character.
The null character is the ascii character `\x00`
The null character must be on the range of the 16 bytes.
*** Owner Port
This field correspond to the port of the Main Server.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
3.4. Lobby Server -> Client
3.4.1. CONNECT_LOBBY_RESP
To understand this action, the action header `magick` must be equal to `21`
The action body is composed of the following fields:
- `magick`
- `isOk`
*** Magick
This field help to know the packet is realy a connect lobby response
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x1a`
*** Is Ok
This field correspond to the status of the connection to the lobby
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field is equal to `1` if the connection is ok
This field is equal to `0` if the connection is not ok
3.4.2. START_WAVE
To understand this action, the action header `magick` must be equal to `3`
The action body is composed of the following fields:
- `magick`
- `ennemyId`
*** Magick
This field help to know the packet is realy a start wave action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x07`
*** Ennemy Id
This field correspond to the id of the first ennemy that will spawn and
that is part of the wave.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each ennemy.
3.4.3. LIFE_UPDATE
To understand this action, the action header `magick` must be equal to `5`
The action body is composed of the following fields:
- `magick`
- `playerId`
- `hp`
*** Magick
This field help to know the packet is realy a life update action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0b`
*** Player ID
This field correspond to the ID of the client whose life has been updated.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each player.
*** HP
This field correspond to the life of the player.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
3.4.4. ENEMY_DEATH
To understand this action, the action header `magick` must be equal to `6`
The action body is composed of the following fields:
- `magick`
- `enemyId`
*** Magick
This field help to know the packet is realy an enemy death action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0c`
*** Enemy ID
This field correspond to the id of the ennemy that has been killed.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each ennemy.
3.4.5. NEW_ENEMY
To understand this action, the action header `magick` must be equal to `10`
The action body is composed of the following fields:
- `magick`
- `ennemyId`
- `hp`
- `x`
- `y`
- `ennemyType`
*** Magick
This field help to know the packet is realy a new enemy action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0e`
*** Ennemy ID
This field correspond to the id of the new ennemy.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each ennemy.
*** HP
This field correspond to the life of the new ennemy.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** X
This field correspond to the absolute x position of the new ennemy.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Y
This field correspond to the absolute y position of the new ennemy.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Ennemy Type
This field correspond to the type of the new ennemy.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
This field must be equal to one of this value:
- 0 : classic
- 1 : terminator
3.4.6. NEW_PLAYER
To understand this action, the action header `magick` must be equal to `11`
The action body is composed of the following fields:
- `magick`
- `playerId`
- `x`
- `y`
- `hp`
- `isOtherPlayer`
*** Magick
This field help to know the packet is realy an init action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0a`
*** Player ID
This field correspond to the ID of the client that joined.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each client.
*** X
This field correspond to the absolute x position of the client.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Y
This field correspond to the absolute y position of the client.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** HP
This field correspond to the life of the client.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** isOtherPlayer
This field is an information to let you know if this player is you.
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field is equal to `1` if it is you
This field is equal to `0` if it is not you
3.4.7. NEW_MISSILE
To understand this action, the action header `magick` must be equal to `9`
The action body is composed of the following fields:
- `magick`
- `x`
- `y`
- `missileId`
- `missileHealth`
- `missileType`
*** Magick
This field help to know the packet is realy a new bullet action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0d`
*** X
This field correspond to the absolute x position of the bullet.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Y
This field correspond to the absolute y position of the bullet.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Missile ID
This field correspond to the ID of the missile.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Missile Health
This field correspond to the health of the missile.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Missile Type
This field correspond to the type of the bullet.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
This field must be equal to one of this value:
- 0 : classic
- 1 : fast
- 2 : bounce
- 3 : perforant
3.4.8. POSITION_ABSOLUTE_BROADCAST
To understand this action, the action header `magick` must be equal to `13`
The action body is composed of the following fields:
- `magick`
- `x`
- `y`
- `playerId`
*** Magick
This field help to know the packet is realy a position absolute broadcast
action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x10`
*** X
This field correspond to the absolute x position of the bullet.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Y
This field correspond to the absolute y position of the bullet.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
*** Player Id
This field correspond to the id of the player.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each player.
3.4.9. POSITION_RELATIVE_BROADCAST
To understand this action, the action header `magick` must be equal to `12`
The action body is composed of the following fields:
- `magick`
- `x`
- `y`
- `playerId`
*** Magick
This field help to know the packet is realy a position relative broadcast
action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x0f`
*** X
This field correspond to the relative x position of the bullet since last
packet send.
This field must be of size 1 byte.
This field is signed (so starting from -((2^8)/2) to +(((2^8)/2)-1))
*** Y
This field correspond to the relative y position of the bullet since last
packet send.
This field must be of size 1 byte.
This field is signed (so starting from -((2^8)/2) to +(((2^8)/2)-1))
*** Player Id
This field correspond to the id of the player.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each player.
3.4.10. PLAYER_DEATH
To understand this action, the action header `magick` must be equal to `14`
The action body is composed of the following fields:
- `magick`
- `playerId`
*** Magick
This field help to know the packet is realy a player death action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x11`
*** Player Id
This field correspond to the id of the player.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each player.
3.4.11. MISSILE_DEATH
To understant This action, the aciton header `magick` must be equal to `15`
The action body is composed of ther following fields:
- `magick`
- `missileId`
*** Magick
This field help to know the packet is realy missile death action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x16`
*** Missile ID
This field correspond to the ID of the missile that died
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
This field is unique for each missile.
3.4.12. END_GAME
To understant This action, the aciton header `magick` must be equal to `24`
The action body is composed of ther following fields:
- `magick`
*** Magick
This field help to know the packet is realy missile death action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x1d`
3.5. Lobby Server -> Main Server
3.5.1. INFO_LOBBY
To understand this action, the action header `magick` must be equal to `18`
This action is used to let the Main Server know the current Lobby Server
exists.
The action body is composed of the following fields:
- `magick`
- `name`
- `maxNbPlayer`
- `gameType`
- `ip`
- `port`
*** Magick
This field help to know the packet is realy an info lobby action
This field must be of size 1 byte.
This field is unsigned (so starting from 0 to 2^8)
This field must be equal to the ascii `\x15`
*** Name
This field correspond to the name of the lobby.
This field must be of size 32 bytes.
Each byte is a signed integer (so starting from -((2^8)/2) to
+(((2^8)/2)-1)).
Each byte correspond to the ascii character.
After the last byte that you set, you must add a null character.
The null character is the ascii character `\x00`
The null character must be on the range of the 32 bytes.
*** Max Nb Player
This field correspond to the maximum number of player in the lobby.
This field must be of size 4 bytes.
This field is unsigned (so starting from 0 to 2^32)
*** Game Type
This field correspond to the type of the game created.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
This field must be equal to one of this value:
- 0 : classic game
*** IP
This field correspond to the ip of the Main Server.
This field must be of size 16 bytes.
Each byte is a signed integer (so starting from -((2^8)/2) to
+(((2^8)/2)-1)).
Each byte correspond to the ascii character.
After the last byte that you set, you must add a null character.
The null character is the ascii character `\x00`
The null character must be on the range of the 16 bytes.
*** Port
This field correspond to the port of the Main Server.
This field must be of size 4 bytes.
This field is signed (so starting from -((2^32)/2) to +(((2^32)/2)-1))
------------------------------------------------------------------------------
4. References
Link to some mentioned word
4.1. R-Bus
- The R-Bus game project source code <https://github.com/X-R-G-B/R-Bus>
- The R-Bus game documentation <https://x-r-g-b.github.io/R-Bus/>
4.2. RFC
- How to write an RFC <https://www.rfc-editor.org/rfc/rfc7322>
------------------------------------------------------------------------------
5. Appendix
Files that helps understand the RFC
5.1. Header
Header files used by the R-Bus game
5.1.1. Message
Structures used by the R-Bus game inside the ECS
<https://github.com/X-R-G-B/R-Bus/blob/dev/src/ECS/MessageTypes.h>
5.1.1. Network
Structures used by the R-Bus game inside the network
<https://github.com/X-R-G-B/R-Bus/blob/dev/src/Nitwork/Nitwork.h>
Clock
A clock can be used to measure time.
You need to create a clock, it will give you the ID to retrieve it and get ellapsed time
Methods
std::size_t Clock::create();
std::size_t Clock::elapsedSecondesSince(std::size_t id); // 1
std::size_t Clock::elapsedMillisecondsSince(std::size_t id); // 10^-3
std::size_t Clock::elapsedNanosecondsSince(std::size_t id); // 10^-9
void Clock::restart(std::size_t id);
Example
#include "Registry.hpp"
std::size_t id = Registry::getInstance().getClock().create();
std::cout << Registry::getInstance().getClock().elapsedMillisecondsSince(id) << std::endl;
Registry::getInstance().getClock().restart(id);
Maths
The Maths namespace provides a collection of utility functions for precise mathematical operations involving decimal numbers. The namespace includes functions for converting between float and integer representations, performing arithmetic operations with preserved decimal precision, and modifying decimal integers using normal integers and floats. We use a lot of these functions to reduce network traffic by sending integers instead of floats, and to ensure that calculations are performed with the same precision on both the client and server.
You can use it including the following header file:
#include "Maths.hpp"
Constants
static constexpr int Maths::DECIMALS_TO_CONSERVE = 2;
This constant specifies the number of decimal places to preserve during calculations, ensuring precision in decimal arithmetic operations.
For example, if DECIMALS_TO_CONSERVE
is 2, we keep 2 decimal precision points. This means that (float)99.99 is converted to (int)9999, and (float) 0.09 is converted to (int)9.
Functions
- Calculates and returns a multiplier based on the
DECIMALS_TO_CONSERVE
constant.
static constexpr int Maths::getMultiplier()
// For example, `DECIMALS_TO_CONSERVE = 2` results in `100`
- Converts a floating-point number to an integer while conserving the specified number of decimals.
int Maths::floatToIntConservingDecimals(const float normalFloat)
// For example, `99.99` becomes `9999` for `DECIMALS_TO_CONSERVE = 2`
- Converts an integer to a floating-point number with the specified number of decimals.
float Maths::intToFloatConservingDecimals(const int decimalInt)
// For example, `9999` becomes `99.99` for `DECIMALS_TO_CONSERVE = 2`
- Removes decimals from an integer, producing a truncated integer result.
int Maths::removeIntDecimals(const int decimalInt)
// For example, `9999` becomes `99` for `DECIMALS_TO_CONSERVE = 2`
- Adds the specified number of decimals to an integer.
int Maths::addIntDecimals(const int normalInt)
// For instance, `99` becomes `9900` for `DECIMALS_TO_CONSERVE = 2`
- Performs addition on two integers with preserved decimals.
int Maths::additionWithTwoIntDecimals(const int decimalInt, const int otherDecimalInt)
// For example, `9999 + 9999` results in `199.98` for `DECIMALS_TO_CONSERVE = 2`
- Performs subtraction on two integers with preserved decimals.
int Maths::subtractionWithTwoIntDecimals(const int minuend, const int subtrahend)
// For instance, `9999 - 9999` results in `0` for `DECIMALS_TO_CONSERVE = 2`
- Performs multiplication on two integers with preserved decimals.
int Maths::multiplicationWithTwoIntDecimals(const int decimalInt, const int otherDecimalInt)
// For example, `9999 * 2` results in `199.98` for `DECIMALS_TO_CONSERVE = 2`
- Performs division on two integers with preserved decimals.
int Maths::divisionWithTwoIntDecimals(const int dividend, const int divisor)
// For instance, `9999 / 2` results in `49.995` for `DECIMALS_TO_CONSERVE = 2`
- Adds a normal integer to a decimal integer, modifying the decimal integer in place.
void Maths::addNormalIntToDecimalInt(int &decimalInt, const int normalInt)
// For example, `500 + 5` results in `550` for `DECIMALS_TO_CONSERVE = 2`
- Subtracts a normal integer from a decimal integer, modifying the decimal integer in place.
void Maths::subNormalIntToDecimalInt(int &decimalInt, const int normalInt)
// For instance, `550 - 5` results in `500` for `DECIMALS_TO_CONSERVE = 2`
- Adds a floating-point number to a decimal integer, modifying the decimal integer in place.
void Maths::addFloatToDecimalInt(int &decimalInt, const float normalFloat)
// For example, `500 + 5.5` results in `555` for `DECIMALS_TO_CONSERVE = 2`
- Subtracts a floating-point number from a decimal integer, modifying the decimal integer in place.
void Maths::subFloatToDecimalInt(int &decimalInt, const float normalFloat)
// For example, `500 - 5.5` results in `445` for `DECIMALS_TO_CONSERVE = 2`
Logger
The logging system allows you to print messages of different levels to facilitate tracking and debugging of your game.
Log Levels
- NoLog: No messages are recorded.
- Fatal: Only "fatal" level messages are recorded.
- Error: "error" and "fatal" level messages are recorded.
- Warn: "warn", "error", and "fatal" level messages are recorded.
- Info: "info", "warn", "error", and "fatal" level messages are recorded.
- Debug: "debug", "info", "warn", "error", and "fatal" messages.
- Trace: "trace", "debug", "info", "warn", "error", and "fatal" messages.
Usage
Watch out: Debug and Trace levels are only available in debug mode.
Header file: #include "Logger.hpp"
Methods
Methods used to log messages of different levels.
void Logger::fatal(const std::string &message);
void Logger::error(const std::string &message);
void Logger::warn(const std::string &message);
void Logger::info(const std::string &message);
void Logger::debug(const std::string &message);
void Logger::trace(const std::string &message);
Callback methods used to subscribe to log messages of different levels.
void Logger::subscribeCallback(LogLevel type, const std::string &name, std::function<void(const std::string &)> callback);
void Logger::unsubscribeCallback(LogLevel type, const std::string &name);
Methods used to change and retrieve the current log level.
void Logger::setLogLevel(LogLevel logLevel);
LogLevel Logger::getLogLevel() const;
Examples
Exemple of how to use the logger to print messages of different levels.
Logger::fatal("fatal message");
Logger::error("error message");
Logger::warn("warn message");
Logger::info("info message");
Logger::debug("debug message");
Logger::trace("trace message");
Exemple of how to use the logger to change and retrieve the current log level.
void Logger::setLogLevel(LogLevel::Info);
LogLevel logLevel = Logger::getLogLevel();
Exemple of how to use the logger to subscribe to log messages of different levels.
Logger::subscribeCallback(LogLevel::Info, "myCallback", [](const std::string &message) {
std::cout << message << std::endl;
});
Logger::unsubscribeCallback(LogLevel::Info, "myCallback");
Json Class
Introduction
The Json class provides ease in loading and accessing data from JSON files. Thanks to the Singleton design pattern, only one instance of this class exists and can be used all inside the program, preventing unnecessary reloading of files.
Json library
We use the library of nlohmann called json used for modern C++. We choosed this library because of it's simplicity of usage and for the good features inside.
Usage
JsonType available
- DEFAULT_ENEMY,
- DEFAULT_PLAYER,
- DEFAULT_PARALLAX,
Methods
- Get the instance of the class :
Json &Json::getInstance();
- Get a data with JsonType :
nlohmann::json Json::getDataByJsonType(JsonType dataType);
- Get a data with a key :
nlohmann::json Json::getDataByJsonType(const std::string &index, JsonType dataType);
- Get a data with vector of key :
nlohmann::json Json::getDataByVector(const std::vector<std::string> &indexes, JsonType dataType);
- Get a data from a json list :
This function is useful when you want to take a json list inside your json because all of your items of the list are in the vector.
std::vector<nlohmann::json> Json::getDatasByJsonType(const std::vector<std::string> &indexes, JsonType dataType);
- Get a data but precise the type of what you want with template :
template <typename T>
T Json::getDataFromJson(nlohmann::json jsonData, const std::string &index);
- Get a data from a vector of json data :
std::vector<nlohmann::json> Json::getDatasFromList(const std::vector<nlohmann::json> &list, const std::string &key);
- Get a json object from a jsonType and an id :
nlohmann::json Json::getJsonObjectById(JsonType type, const std::string &id, const std::string &arrayName);
- Get a json list from a json data :
std::vector<nlohmann::json> Json::getDatasFromList(const nlohmann::json &list, const std::string &key);
- Get a json list from a json data :
std::vector<nlohmann::json> Json::getDatasFromList(const nlohmann::json &list);
- Check if the data exists in the json :
bool Json::isDataExist(nlohmann::json jsonData, const std::string &index);
Errors handling
All the methods above Log an error with the class Logger and throw an std::runtime_error if the arguments are incorect or the key in the json is not found because if the data is not get correctly the rest of rest programm might crash.
Here's a json with spritePath of enemy missing :
{
"enemy" : {
}
}
Here's a code that will try to acces it :
Json::getInstance().getDataByVector({"enemy", "spritePath"},
JsonType::DEFAULT_ENEMY)
This won't work and output this :
2023-10-15 13:29:57.813141624 [FATAL] (getDataByVector) Key : spritePath is not valid
However you can handle you proper way the errors with the method isDataExist listed above.
Some examples
Basic example
Here's a json :
{
"enemy" : {
"spritePath" : "path"
}
}
Here's how you can get spritePath data :
std::cout << Json::getInstance().getDataByVector({"enemy", "spritePath"},
JsonType::DEFAULT_ENEMY) << std::endl;
Using list data
Here's a new json but with a list inside :
{
"enemy" : [
{
"spritePath" : "path"
},
{
"spritePath" : "path"
}
]
}
You can see that all spritePath are in a list.
Here's how to proceed :
std::vector<nlohmann::json> enemyData =
Json::getInstance().getDataByJsonType("enemy", enemyType);
And now you can iterate on your datas :
for (auto &data : enemyData) {
std::cout << Json::getInstance().getDataFromJson<std::string>(elem, "spritePath") <<
std::endl;
}
Get a json object from a jsonType and an id
Here's a json file with an array of enemy objects :
{
"enemy" : [
{
"id" : "1",
"spritePath" : "path"
},
{
"id" : "2",
"spritePath" : "path"
}
]
}
Here's how to get an enemy object from his id :
nlohmann::json object = Json::getJsonObjectById(enemyType, "1", "enemy");
//the returned object will be :
// {
// "id" : "1",
// "spritePath" : "path"
// }
Bullets
We use differents types of bullets in the game. Each type of bullet has its own characteristics.
Bullets are defined in the bullets.json
file.
Json structure
We use the following structure to define bullets:
{
"bullets": [
{
"id": "classic",
"velocity": {
"speedX": 120,
"speedY": 0
},
"spritePath": "assets/R-TypeSheet/WaterBullets.png",
"soundPath": "assets/Audio/Sounds/laser.ogg",
},
{
"id": "perforant",
"velocity": {
"speedX": 400,
"speedY": 0
},
"spritePath": "assets/R-TypeSheet/FireBullets.png",
"soundPath": "assets/Audio/Sounds/laser2.ogg",
}
]
}
It is composed of an array of bullets. Each bullet has its own id and its own characteristics.
Characteristics
id
: The id of the bullet. It is used to identify the bullet in the game.damage
: The damage of the bullet. It is used to damage the enemies.health
: The health of the bullet. It is used to destroy the bullet when it collides with an enemy.velocity
: The velocity of the bullet. It is used to move the bullet.physics
: The physics of the bullet. It is used to move the bullet.waitTimeBullet
: The wait time of the bullet. It is used to wait before shooting again.collisionRect
: The collision rectangle of the bullet. It is used to detect collisions with enemies.spritePath
: The sprite path of the bullet. It is used to display the bullet.soundPath
: The sound path of the bullet. It is used to play the sound of the bullet.spriteRect
: The sprite rectangle of the bullet. It is used to display the bullet.animRect
: The animation rectangle of the bullet. It is used to display the bullet.
Physics
Physics is represented by an array of strings. Each string represents a physics. You can put as many physics as you want. So far, we have the following physics:
zigzag
: The bullet moves in a zigzag way.boncing
: The bullet bounces on the top and bottom of the screen.
{
"bullets": [
{
"id": "perforant",
"physics": [
"zigzag"
]
}
]
}
Bullet types
For now, we have 4 types of bullets:
classic
: The classic bullet.fast
: The fast bullet.bounce
: The bouncing bullet.perforant
: The perforant bullet.
To add a new bullet type, you have to add it in the missileTypes_e
enum in the Missile.hpp
file. Then, you have to add it in the bulletKeyMap
map in the EventsSystems.cpp
file.
C++ Client Documentation
Table of Contents
Introduction
The C++ client is a simple game client that uses the Raylib library to handle graphics and audio. The client is designed to be simple and easy to use, but it is also very flexible and can be used to create more complex games.
Raylib Library
We use the Raylib library to handle our graphics and audio. Raylib is a simple and easy-to-use library that provides a wide range of features. The Raylib library is written in C, but we have wrapped it in C++ classes to make it easier to use.
Using Audio in our game client
In your C++ client application, you can manage audio using the Raylib library. This guide explains how to work with sounds and music using our Raylib wrapper classes. Before looking at the other pages, we advise you to look at the rest of this page. Initialize the audio correctly is essential to use it in our client.
Audio Device Management
Functions
void Raylib::initAudioDevice();
void Raylib::closeAudioDevice();
bool Raylib::isAudioDeviceReady();
void Raylib::setMasterVolume(float volume);
Example usage
You need to initialize the audio device before using any audio functions. You can do this by calling the initAudioDevice() function. You can then use the other functions to manage the audio device. If you want to player music, every frame you need to call the UpdateMusicStream() function.
int main() {
Raylib::Music music("path/to/musicfile.ogg");
Raylib::initAudioDevice();
// game loop
while (!Raylib::WindowShouldClose()) {
// stuff
//for every music
Raylib::UpdateMusicStream(music);
}
Raylib::closeAudioDevice();
}
Volume, pitch, and pan
- The pitch base level is 1.0f (normal pitch).
- The volume base level is 1.0f (maximum volume).
- The pan base level is 0.5f (center).
Audio File Formats
Raylib supports the following audio file formats:
- WAV
- OGG
- MP3
- XM
- QOA
- MOD
- FLAC
Music Class
Constructors
Raylib::Music(const std::string& fileName, float volume = 0.5f);
Create a music object from the specified audio file.
Methods
void Raylib::Music::unload();
bool Raylib::Music::isReady() const;
void Raylib::Music::play() const;
bool Raylib::Music::isPlaying() const;
void Raylib::Music::update() const;
void Raylib::Music::stop() const;
void Raylib::Music::pause() const;
void Raylib::Music::resume() const;
void Raylib::Music::setVolume(float volume) const;
void Raylib::Music::setPitch(float pitch) const;
void Raylib::Music::setPan(float pan) const;
float Raylib::Music::getTimeLength() const;
float Raylib::Music::getTimePlayed() const;
bool Raylib::Music::NeedToPlay() const;
void Raylib::Music::setNeedToPlay(bool needToPlay);
std::string Raylib::Music::getPath() const;
Example usage
Raylib::Music myMusic("path/to/musicfile.ogg");
myMusic.play();
myMusic.setVolume(0.6f);
Sound Class
Constructors
Raylib::Sound(const std::string& fileName, float volume = 0.5f);
Create a sound object from the specified audio file.
Methods
void Raylib::Sound::unload();
void Raylib::Sound::play() const;
void Raylib::Sound::stop() const;
void Raylib::Sound::pause() const;
void Raylib::Sound::resume() const;
bool Raylib::Sound::isPlaying() const;
void Raylib::Sound::setVolume(float volume) const;
void Raylib::Sound::setPitch(float pitch) const;
void Raylib::Sound::setPan(float pan) const;
bool Raylib::Sound::NeedToPlay() const;
void Raylib::Sound::setNeedToPlay(bool needToPlay);
std::string Raylib::Sound::getPath() const;
Example usage
Raylib::Sound mySound("path/to/soundfile.wav");
mySound.play();
mySound.setVolume(0.8f);
Input-related functions: keyboard
-
bool Raylib::isKeyPressed(Raylib::KeyboardKey key);
Check if a key has been pressed once. -
bool Raylib::isKeyDown(Raylib::KeyboardKey key);
Check if a key is being pressed. -
bool Raylib::isKeyReleased(Raylib::KeyboardKey key);
Check if a key has been released once. -
bool Raylib::isKeyUp(Raylib::KeyboardKey key);
Check if a key is NOT being pressed. -
void Raylib::setExitKey(Raylib::KeyboardKey key);
Set a key to exit the application. -
int Raylib::getKeyPressed();
Get the key pressed (keycode). -
int Raylib::getCharPressed();
Get the last character pressed (unicode).
Input-related functions: mouse
-
bool Raylib::isMouseButtonPressed(Raylib::MouseButton button);
Check if a mouse button has been pressed once. -
bool Raylib::isMouseButtonDown(Raylib::MouseButton button);
Check if a mouse button is being pressed. -
bool Raylib::isMouseButtonReleased(Raylib::MouseButton button);
Check if a mouse button has been released once. -
bool Raylib::isMouseButtonUp(Raylib::MouseButton button);
Check if a mouse button is NOT being pressed. -
int Raylib::getMouseX();
Get the X position of the mouse cursor. -
int Raylib::getMouseY();
Get the Y position of the mouse cursor. -
Vector2 Raylib::getMousePosition();
Get the current position of the mouse cursor. -
Vector2 Raylib::getMouseDelta();
Get the mouse delta movement. -
void Raylib::setMousePosition(int x, int y);
Set the position of the mouse cursor. -
void Raylib::setMouseOffset(int offsetX, int offsetY);
Set an offset for the mouse position. -
void Raylib::setMouseScale(float scaleX, float scaleY);
Set the scaling factor for the mouse position. -
float Raylib::getMouseWheelMove();
Get the mouse wheel movement. -
Vector2 Raylib::getMouseWheelMoveV();
Get the mouse wheel movement as a vector. -
void Raylib::setMouseCursor(int cursor);
Set the mouse cursor style.
Example
if (Raylib::isKeyDown(Raylib::KeyboardKey::KB_RIGHT))
{
// Move right
}
else if (Raylib::isKeyDown(Raylib::KeyboardKey::KB_LEFT))
{
// Move left
}
Color Structure
Represents a color.
Raylib::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
Constructor to create a color with specifiedr
(red),g
(green),b
(blue), anda
(alpha) values.
Color Constants
Raylib::DarkGray
Raylib::Yellow
Raylib::Gold
Raylib::Orange
Raylib::Pink
Raylib::Red
Raylib::Maroon
Raylib::Green
Raylib::Lime
Raylib::DarkGreen
Raylib::SkyBlue
Raylib::Blue
Raylib::DarkBlue
Raylib::Purple
Raylib::Violet
Raylib::DarkPurple
Raylib::Beige
Raylib::Brown
Raylib::DarkBrown
Raylib::White
Raylib::Black
Raylib::Blank
Raylib::Magenta
Raylib::RayWhite
Rectangle Structure
Represents a rectangle.
Raylib::Rectangle(float x, float y, float width, float height);
Constructor to create a rectangle with specifiedx
,y
,width
, andheight
values.
Vector2 Structure
Represents a 2D vector.
Raylib::Vector2(float x, float y);
Constructor to create a 2D vector with specifiedx
andy
values.
Vector3 Structure
Represents a 3D vector.
Raylib::Vector3(float x, float y, float z);
Constructor to create a 3D vector with specifiedx
,y
, andz
values.
Vector4 Structure
Represents a 4D vector.
Raylib::Vector4(float x, float y, float z, float w);
Constructor to create a 4D vector with specifiedx
,y
,z
, andw
values.
Colors/pixels related functions
-
Raylib::Color Raylib::fade(Raylib::Color color, float alpha);
Fade a color by a specified alpha value. -
int Raylib::colorToInt(Raylib::Color color);
Convert a Color to a 32-bit integer. -
Vector4 Raylib::colorNormalize(Raylib::Color color);
Normalize a Color struct to a Vector4. -
Raylib::Color Raylib::colorFromNormalized(Raylib::Vector4 normalized);
Create a Color from a normalized Vector4. -
Raylib::Color Raylib::getColor(unsigned int hexValue);
Create a Color from a hex value.
Enum ConfigFlags
You can use the following flags to configure the window:
Raylib::ConfigFlags::ConfigFlags::FLAG_FULLSCREEN_MODE
: Set fullscreen mode.Raylib::ConfigFlags::FLAG_WINDOW_RESIZABLE
: Allow the window to be resized.Raylib::ConfigFlags::FLAG_WINDOW_UNDECORATED
: Disable window decoration (frame and buttons).Raylib::ConfigFlags::FLAG_WINDOW_TRANSPARENT
: Allow the window to be transparent.Raylib::ConfigFlags::FLAG_WINDOW_HIDDEN
: Hide the window.Raylib::ConfigFlags::FLAG_WINDOW_MINIMIZED
: Minimize the window.Raylib::ConfigFlags::FLAG_WINDOW_MAXIMIZED
: Maximize the window.Raylib::ConfigFlags::FLAG_WINDOW_UNFOCUSED
: Disable window focus.Raylib::ConfigFlags::FLAG_WINDOW_TOPMOST
: Set the window to be always on top.Raylib::ConfigFlags::FLAG_WINDOW_HIGHDPI
: Enable high-DPI mode.Raylib::ConfigFlags::FLAG_WINDOW_ALWAYS_RUN
: Allow the window to run in the background.Raylib::ConfigFlags::FLAG_MSAA_4X_HINT
: Enable 4x MSAA.Raylib::ConfigFlags::FLAG_VSYNC_HINT
: Enable V-Sync.
Example
Raylib::setWindowState(Raylib::ConfigFlags::WINDOW_RESIZABLE);
Raylib::setWindowState(Raylib::ConfigFlags::WINDOW_RESIZABLE | Raylib::ConfigFlags::FLAG_VSYNC_HINT);
Cursor-related functions
-
void Raylib::showCursor();
Show the cursor. -
void Raylib::hideCursor();
Hide the cursor. -
bool Raylib::isCursorHidden();
Check if the cursor is currently hidden. -
void Raylib::enableCursor();
Enable cursor (unlock it). -
void Raylib::disableCursor();
Disable cursor (lock it). -
bool Raylib::isCursorOnScreen();
Check if the cursor is within the game window.
Drawing-related functions
-
void Raylib::clearBackground(Raylib::Color color);
Clear the background with a specified color. -
void Raylib::beginDrawing();
Begin drawing. -
void Raylib::endDrawing();
End drawing and swap buffers.
Timing-related functions
-
void Raylib::setTargetFPS(int fps);
Set the target frames-per-second. -
int Raylib::getFPS();
Get the current frames-per-second. -
float Raylib::getFrameTime();
Get the time in seconds for a frame. -
double Raylib::getTime();
Get the current time in seconds.
Image Class
Constructors
Raylib::Image(const std::string& fileName);
Raylib::Image(int width, int height, Raylib::Color color);
Create an image object from the specified file or create a blank image with the given width, height, and color.
Methods
bool Raylib::Image::isImageReady() const;
void Raylib::Image::unloadImage();
int Raylib::Image::getWidth() const;
int Raylib::Image::getHeight() const;
int Raylib::Image::getMipmaps() const;
int Raylib::Image::getFormat() const;
void* Raylib::Image::getData() const;
Example usage
Raylib::Image myImage("path/to/imagefile.png");
int width = myImage.getWidth();
std::cout << "Image width: " << width << std::endl;
myImage.unloadImage();
Misc. functions
void Raylib::takeScreenshot(const std::string &fileName);
Take a screenshot and save it to a file with the specified name.
Shapes-related functions
-
void Raylib::drawPixel(int posX, int posY, Raylib::Color color);
Draw a pixel at the specified position with the given color. -
void Raylib::drawCircle(int centerX, int centerY, float radius, Raylib::Color color);
Draw a circle with the specified center, radius, and color. -
void Raylib::drawRectangle(int posX, int posY, int width, int height, Raylib::Color color);
Draw a rectangle at the specified position with the given width, height, and color.
Example usage
Raylib::drawPixel(100, 100, Raylib::RED);
Raylib::drawCircle(200, 200, 50, Raylib::GREEN);
Raylib::drawRectangle(300, 300, 80, 120, Raylib::BLUE);
Sprite Class
Constructors
Raylib::Sprite(const std::string &fileName, float width, float height);
Raylib::Sprite(Image image, float width, float height);
Create a sprite object from the specified file and set its width and height or create a sprite from an image object with the given width and height. The width and height are in percentage of the screen.
Methods
unsigned int Raylib::Sprite::getId() const;
float Raylib::Sprite::getWidth() const;
float Raylib::Sprite::getHeight() const;
int Raylib::Sprite::getTextureWidth() const;
int Raylib::Sprite::getTextureHeight() const;
int Raylib::Sprite::getMipmaps() const;
int Raylib::Sprite::getFormat() const;
void Raylib::Sprite::unloadSprite();
Example usage
Raylib::Sprite mySprite("path/to/spritefile.png", 100.0f, 150.0f);
float spriteWidth = mySprite.getWidth();
std::cout << "Sprite width: " << spriteWidth << std::endl;
mySprite.unloadSprite();
Text Class
Constructors
Raylib::Text(
std::string text,
Raylib::Vector2 position = {0, 0},
float fontSize = 5.0f,
Raylib::Color color = Raylib::BLACK);
Create a text object with the given text, position, font size, and color.
Methods
void Raylib::Textdraw();
void Raylib::TextdrawEx(float spacing);
void Raylib::TextdrawPro(Raylib::Vector2 origin, float rotation, float spacing);
float Raylib::Text::x() const;
float Raylib::Text::y() const;
float Raylib::Text::getFontSize() const;
void Raylib::Text::setFontSize(float fontSize);
Raylib::Vector2 Raylib::Text::getPosition() const;
void Raylib::Text::setPixelPosition(Raylib::Vector2 position);
void Raylib::Text::setCurrentFontSize(float fontSize);
std::string &Text::getCurrentText();
void Text::setText(const std::string &text)
getPosition is in percentage of the screen size. For each draw, we recommand to compute and set the position in pixel with setPixelPosition to have a responsive text.
Example usage
Raylib::Text myText("Hello, World!", {100, 200}, 20.0f, Raylib::BLUE);
myText.setPixelPosition({300, 400});
myText.draw();
Window-related functions
-
void Raylib::initWindow(int width, int height, const std::string &title);
Initialize the game window with the specified width, height, and title. -
bool Raylib::windowShouldClose();
Check if the window should close (user pressed close button or escape key). -
void Raylib::closeWindow();
Close the game window. -
bool Raylib::isWindowReady();
Check if the window has been initialized successfully. -
bool Raylib::isWindowFullscreen();
Check if the window is in fullscreen mode. -
bool Raylib::isWindowHidden();
Check if the window is currently hidden. -
bool Raylib::isWindowMinimized();
Check if the window is currently minimized. -
bool Raylib::isWindowMaximized();
Check if the window is currently maximized. -
bool Raylib::isWindowFocused();
Check if the window is currently focused. -
void Raylib::setConfigFlags(ConfigFlags flags);
Set configuration flags for the window. -
bool Raylib::isWindowResized();
Check if the window has been resized. -
bool Raylib::isWindowState(ConfigFlags flag);
Check if a specific window state flag is set. -
void Raylib::setWindowState(ConfigFlags flag);
Set a specific window state flag. -
void Raylib::clearWindowState(ConfigFlags flags);
Clear specific window state flags. -
void Raylib::toggleFullscreen();
Toggle fullscreen mode. -
void Raylib::maximizeWindow();
Maximize the window. -
void Raylib::minimizeWindow();
Minimize the window. -
void Raylib::setWindowTitle(const std::string &title);
Set the title of the window. -
int Raylib::getScreenWidth();
Get the current screen width. -
int Raylib::getScreenHeight();
Get the current screen height. -
int Raylib::getRenderWidth();
Get the current rendering width -
int Raylib::getRenderHeight();
Get the current rendering height -
int Raylib::getMonitorWidth(int monitor);
Get the width of the specified monitor. -
int Raylib::getMonitorHeight(int monitor);
Get the height of the specified monitor. -
int Raylib::getMonitorRefreshRate(int monitor);
Get the refresh rate of the specified monitor. -
int Raylib::getCurrentMonitor();
Get the index of the current monitor. -
void Raylib::setClipboardText(const std::string &text);
Set the text to be copied to the clipboard. -
std::string Raylib::getClipboardText();
Get the text currently copied to the clipboard. -
void Raylib::setWindowIcon(Image image);
Set the window icon.
Contributors
Thanks to this amazing contributors that made this project possible!