2025/February/13/Wednesday, Today's topic: Raylib.>
I recently discovered a great programming language agnostic module called raylib after messing around with libraries like pygame and engines like godot for some time. It is quite an awesome tool for the barebones programming of games and simple apps without relying on a particular engine. I'll go into detail later on, for now showing some examples I figured out doing in Python and in C. Although tutorials regarding raylib are still quite sparse compared to other frameworks, the documentation is well structured.
//This particular example is written in C
#include <raylib.h>
int main(void){
const int screenWidth = 800;
const int screenHeight = 600;
const char * appTitle = "Emmy Walking with Scrolling Background";
InitWindow(screenWidth,screenHeight,appTitle);
const int fpsCap = 60;
SetTargetFPS(fpsCap);
InitAudioDevice();
Music bgm = LoadMusicStream("audio/AnotherSillyMorning.wav");
SetMusicVolume(bgm,0.25);
PlayMusicStream(bgm);
float musicPitchLv = 1.0f;
SetMusicPitch(bgm,musicPitchLv);
Texture2D background = LoadTexture("images/background_layer.png");
Texture2D midground = LoadTexture("images/midground.png");
Texture2D foreground = LoadTexture("images/foreground.png");
float midgroundScroll = 0.0f;
float foregroundScroll = 0.0f;
float midgroundSpeed = 20;
float foregroundSpeed = 40;
Texture2D animationFrames[] = {};
float animFrameSpeed = 8.0f;
float minFrameSpeed = 1.0f;
float maxFrameSpeed = 15.0f;
float currentFrame = 0.0f;
int numberOfFrames = 4;
void loadAnimFrames(){
for(int frameNum = 0;frameNum < numberOfFrames;frameNum++){
animationFrames[frameNum] = LoadTexture(TextFormat("images/emmy_frame_%i.png",frameNum));
}
}
loadAnimFrames();
Vector2 animPos = {screenWidth/2 - screenWidth/4,screenHeight/2};
void updateAnimFrames(float deltaTime){
currentFrame += animFrameSpeed * deltaTime;
if(currentFrame > numberOfFrames){
currentFrame = 0;
}
if(IsKeyPressed(KEY_D)){
animFrameSpeed += 1;
musicPitchLv += 0.1;
}else if(IsKeyPressed(KEY_A)){
animFrameSpeed -= 1;
musicPitchLv -= 0.1;
}
if(animFrameSpeed > maxFrameSpeed){
animFrameSpeed = maxFrameSpeed;
}else if(animFrameSpeed < minFrameSpeed){
animFrameSpeed = minFrameSpeed;
}
}
void drawAnimFrames(){
// DrawTextureV(animationFrames[(int)currentFrame],animPos,WHITE);
DrawTextureEx(animationFrames[(int)currentFrame],animPos,0,0.5,WHITE);
}
void updateBGLayers(float deltaTime){
midgroundScroll -= midgroundSpeed * deltaTime * animFrameSpeed / 2;
foregroundScroll -= foregroundSpeed * deltaTime * animFrameSpeed / 2;
if(midgroundScroll < - midground.width){
midgroundScroll = 0;
}
if(foregroundScroll < -foreground.width){
foregroundScroll = 0;
}
}
void drawLayers(){
DrawTextureEx(background,(Vector2){0,-20},0,1,WHITE);
DrawTextureEx(midground,(Vector2){midgroundScroll,20},0,1,WHITE);
DrawTextureEx(midground,(Vector2){midgroundScroll+midground.width,20},0,1,WHITE);
drawAnimFrames();
DrawTextureEx(foreground,(Vector2){foregroundScroll,20},0,1,WHITE);
DrawTextureEx(foreground,(Vector2){foregroundScroll+foreground.width,20},0,1,WHITE);
}
while(!WindowShouldClose()){
SetMusicPitch(bgm,musicPitchLv);
UpdateMusicStream(bgm);
float deltaTime = GetFrameTime();
updateBGLayers(deltaTime);
updateAnimFrames(deltaTime);
BeginDrawing();
ClearBackground(RAYWHITE);
drawLayers();
DrawText(TextFormat("Framespeed: %00.0f",animFrameSpeed),10,10,20,BLACK);
EndDrawing();
}
UnloadTexture(background);
UnloadTexture(midground);
UnloadTexture(foreground);
for(int frameNum = 0; frameNum < numberOfFrames; frameNum++){
UnloadTexture(animationFrames[frameNum]);
}
UnloadMusicStream(bgm);
CloseWindow();
return 0;
}
And then we'll get something animated with parallax scrolling like this:
The code below uses the python bindings and essentially does exactly the same. Which actually encourages porting back and forth in both languages fostering a deeper understanding beyond the differences in syntax.
#Written in Python
from raylib import *
from pyray import *
from os.path import join
class Game:
def __init__(self):
self.screen_width = 800
self.screen_height = 600
self.app_title = "Emmy Walking Animation and Scrolling BG"
init_window(self.screen_width,self.screen_height,self.app_title)
init_audio_device()
self.fps_cap = 60
set_target_fps(self.fps_cap)
self.bg_layers = {}
self.load_bg_layers()
self.num_of_frames = 4
self.current_frame = 0
self.frame_speed = 8
self.max_framespeed = 15
self.min_framespeed = 1
self.anim_frames = []
self.load_anim_frames()
self.anim_pos = Vector2(self.screen_width/2 - self.screen_width/4,self.screen_height/2 - self.screen_height/10)
self.midground_scroll = 0
self.foreground_scroll = 0
self.midground_speed = 20
self.foreground_speed = 40
self.music_volume = 0.25
self.music_pitch = 1.0
self.bgm = load_music_stream(join("emmy_walk_and_bg_scroll","audio","AnotherSillyMorning.wav"))
set_music_volume(self.bgm,self.music_volume)
play_music_stream(self.bgm)
def load_bg_layers(self):
self.bg_layers = {
"background" : load_texture(join("emmy_walk_and_bg_scroll","images","background_layer.png")),
"midground" : load_texture(join("emmy_walk_and_bg_scroll","images","midground.png")),
"foreground" : load_texture(join("emmy_walk_and_bg_scroll","images","foreground.png"))
}
def load_anim_frames(self):
# anim_frames = [load_texture(join("emmy_walk_and_bg_scroll","images",f"emmy_frame_{f_num}.png"))
# for f_num in range(self.num_of_frames)]
for f_num in range(self.num_of_frames):
self.anim_frames.append(load_texture(join("emmy_walk_and_bg_scroll","images",f"emmy_frame_{f_num}.png")))
def update_anim_frames(self,delta_time):
self.current_frame += self.frame_speed * delta_time
if self.current_frame > self.num_of_frames:
self.current_frame = 0
if is_key_pressed(KEY_D):
self.frame_speed += 1
self.music_pitch += 0.1
if self.music_volume < 1.0:
self.music_volume += 0.1
elif is_key_pressed(KEY_A):
self.frame_speed -= 1
self.music_pitch -= 0.1
if self.music_volume > 0.0:
self.music_volume -= 0.1
if self.frame_speed > self.max_framespeed:
self.frame_speed = self.max_framespeed
elif self.frame_speed < self.min_framespeed:
self.frame_speed = self.min_framespeed
def draw_anim_frames(self):
draw_texture_ex(self.anim_frames[int(self.current_frame)],self.anim_pos,0,0.5,WHITE)
def update_layers(self,delta_time):
self.midground_scroll -= self.midground_speed * delta_time * self.frame_speed/2
if self.midground_scroll < -self.bg_layers["midground"].width:
self.midground_scroll = 0
self.foreground_scroll -= self.foreground_speed * delta_time * self.frame_speed/2
if self.foreground_scroll < - self.bg_layers["foreground"].width:
self.foreground_scroll = 0
def draw_layers(self):
draw_texture_ex(self.bg_layers["background"],Vector2(0,-20),0,1,WHITE)
draw_texture_ex(self.bg_layers["midground"],Vector2(self.midground_scroll,20),0,1,WHITE)
draw_texture_ex(self.bg_layers["midground"],Vector2(self.midground_scroll + self.bg_layers["midground"].width,20),0,1,WHITE)
self.draw_anim_frames()
draw_texture_ex(self.bg_layers["foreground"],Vector2(self.foreground_scroll,0),0,1,WHITE)
draw_texture_ex(self.bg_layers["foreground"],Vector2(self.foreground_scroll + self.bg_layers["foreground"].width,0),0,1,WHITE)
def update(self):
set_music_volume(self.bgm,self.music_volume)
set_music_pitch(self.bgm,self.music_pitch)
update_music_stream(self.bgm)
delta_time = get_frame_time()
self.update_layers(delta_time)
self.update_anim_frames(delta_time)
def draw(self):
begin_drawing()
clear_background(RAYWHITE)
self.draw_layers()
draw_text(f"Framespeed: {self.frame_speed}",10,10,20,BLACK)
end_drawing()
def run(self):
while not window_should_close():
self.update()
self.draw()
unload_music_stream(self.bgm)
for frame in self.anim_frames:
unload_texture(frame)
close_window()
if __name__ == "__main__":
game = Game()
game.run()
Merlin's Maker Spaces 2025