Compare commits

..

19 Commits
main ... main

Author SHA1 Message Date
6a39ca4aaa
changed super call to match pygame.sprite.Sprite 2025-01-07 16:26:31 -05:00
7e7d412f1e
fixed incorrect comments 2025-01-07 09:33:12 -05:00
d885fd4030
fixed bug where logos would only spawn on the left half of screen 2025-01-07 09:22:21 -05:00
22f7a60499
fixed another typo 2025-01-07 09:20:26 -05:00
eaa4029bff
typo fix 2025-01-07 09:17:56 -05:00
ed266511fb
added a comment 2025-01-07 09:16:41 -05:00
b96954c5c1
fixing some formatting and adding a comment 2025-01-06 23:16:06 -05:00
2957bb867f
added the ability to quit by pressing esc. 2025-01-06 23:12:14 -05:00
afe58c15a8
changed count of mini logos from 20 to 25 2025-01-06 22:53:56 -05:00
827a781f24
changed brightness of mini logos (better for OLED) 2025-01-06 22:53:20 -05:00
d779ea357e
changed how small the mini logos can be 2025-01-06 22:51:49 -05:00
194852b77f
updated README.md 2025-01-06 22:41:23 -05:00
988c7ce248
prevent the logo speed from dropping too low 2025-01-06 22:38:51 -05:00
ae84831c1c
created mini logos, applied scaled speeds and sizes rather than fixed, added random movement 2025-01-06 22:32:14 -05:00
691005a67e
change FPS from 60 to 90 2025-01-06 22:30:38 -05:00
11f5eb5a79
added screen resolution detection 2025-01-06 21:00:36 -05:00
399092238e removed old dvd logo png 2025-01-06 16:59:27 -05:00
dbd78bb5c5 changed the dvd logo to the steam logo
changed the scaling function to a smoother one
2025-01-06 16:59:02 -05:00
31f6593ae0 renamed dvd references to steam references 2025-01-06 16:41:10 -05:00
10 changed files with 243 additions and 165 deletions

View File

@ -1,3 +1,3 @@
# pygame-dvd # Steam Logo Screensaver
It's the DVD logo bouncing around in pygame. It's the Steam logo bouncing around in Pygame.

View File

@ -1 +0,0 @@
from . import dvd_screen

View File

@ -1,149 +0,0 @@
import pygame
import os
import random
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LOGO_SCALING = 0.1
class DVDLogo(pygame.sprite.Sprite):
"""
A Pygame sprite representing a DVD logo that moves around the screen.
Attributes:
base_image (pygame.Surface): The original image of the DVD logo.
x_speed (int): Current speed in the X axis.
y_speed (int): Current speed in the Y axis.
max_x (int): Maximum X position on the screen.
max_y (int): Maximum Y position on the screen.
"""
def __init__(self, window_size: tuple):
"""
Initialize DVDLogo sprite with given window size.
Args:
window_size (tuple): Size of the game window (width, height).
"""
super().__init__()
# Load and scale original logo image
self.base_image = pygame.image.load(SCRIPT_DIR + '/resources/dvd.png')
self.base_image.convert_alpha()
self.base_image = pygame.transform.scale(self.base_image, (self.base_image.get_width() * LOGO_SCALING,
self.base_image.get_height() * LOGO_SCALING))
# Copy the base image and store it in a separate instance variable; this is the image that will be drawn.
self.image = self.base_image.copy()
# Generate random color and apply it to the sprite.
self.random_color()
# Initialize rectangle (position, size) and speed attributes.
self.rect = self.image.get_rect()
self.x_speed = 4
self.y_speed = 4
# Calculate maximum X and Y positions on the screen for boundary checking.
self.max_x = window_size[0] - self.rect.width
self.max_y = window_size[1] - self.rect.height
def random_color(self):
"""
Generate a new, random color and apply it to the sprite.
"""
# Create a surface with alpha channel for generating transparent colors.
color_surface = pygame.Surface(self.image.get_size(), pygame.SRCALPHA)
# Generate random RGB values and fill the color surface accordingly.
new_color = (random.randint(32, 255),
random.randint(32, 255),
random.randint(32, 255))
color_surface.fill(new_color)
# Replace the drawn image with a copy of the base image and apply the randomly generated color.
self.image = self.base_image.copy()
self.image.blit(color_surface, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
def update(self):
"""
Update DVDLogo sprite's position and speed based on its current state.
This method is called each frame during game execution to move the logo around
the screen and to handle collision/color changes.
"""
# Move the logo in the X and Y axis according to its current speed in those directions.
self.rect.x += self.x_speed
self.rect.y += self.y_speed
# Check for collisions with edges of the screen, change direction if necessary,
# and generate a new random color when hitting an edge.
if self.rect.x < 0 or self.rect.x > self.max_x:
self.x_speed *= -1
self.random_color()
if self.rect.y < 0 or self.rect.y > self.max_y:
self.y_speed *= -1
self.random_color()
# Ensure the logo stays within screen boundaries by clamping its position.
self.rect.x = max(0, min(self.rect.x, self.max_x))
self.rect.y = max(0, min(self.rect.y, self.max_y))
class Surface(pygame.Surface):
"""
A custom Pygame surface class for managing game logic and events.
Attributes:
running (bool): Flag indicating whether the surface is still running.
quit (bool): Flag signaling that the program should end rather than moving to a new surface.
next_surface (str): Name of the next surface to display after current one stops.
all_sprites (pygame.sprite.Group): A group containing sprites for easy sprite management.
"""
def __init__(self, window_size: tuple):
"""
Initialize Surface class with given window size.
Args:
window_size (tuple): Size of the game window (width, height).
"""
# Create a Pygame surface with alpha channel for generating transparent colors.
super().__init__(window_size, pygame.SRCALPHA)
# Initialize flags and attributes for managing game state.
self.running = True
self.quit = False
self.next_surface = ''
pygame.mouse.set_visible(False)
# Create a DVDLogo sprite instance and add it to the sprite group.
dvd_logo = DVDLogo(window_size)
self.all_sprites = pygame.sprite.Group(dvd_logo)
def update(self):
"""
Update game state by handling events, updating sprites, and redrawing surfaces.
This method is called every frame during game execution.
"""
# Fill the surface with a black background color
self.fill(pygame.colordict.THECOLORS.get('black'))
# Handle events such as mouse button clicks or key presses.
for event in pygame.event.get():
match event.type:
case pygame.MOUSEBUTTONDOWN:
# Stop the game when a user clicks anywhere on the screen.
self.running = False
self.quit = True
# Update positions and speeds of all sprites (in this case, just one logo sprite).
self.all_sprites.update()
# Draw all sprites onto this surface.
self.all_sprites.draw(self)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View File

@ -1,4 +1,4 @@
import dvd_bounce import steam_saver
import sys import sys
import os import os
@ -11,4 +11,4 @@ if __name__ == '__main__':
if arg == '--window': if arg == '--window':
windowed_mode = True windowed_mode = True
dvd_bounce.main.main(windowed_mode) steam_saver.main.main(windowed_mode)

View File

@ -1,14 +1,26 @@
import pygame import pygame
import dvd_bounce.surfaces as surfaces import subprocess
import steam_saver.surfaces as surfaces
# Global variables # Global variables
SCREEN_SIZE = (1920, 1080) FPS = 90
FPS = 60
VALID_SURFACES = [ VALID_SURFACES = [
'dvd_screen' 'steam_screen'
] ]
def get_screen_resolution():
"""
Gets the current resolution using xrandr
Returns:
tuple: The current resolution as a tuple.
"""
command_output = subprocess.check_output(['xrandr']).decode('utf-8')
for line in command_output.split('\n'):
if '*' in line:
return tuple(map(int, line.split()[0].split('x')))
def main(windowed_mode=False): def main(windowed_mode=False):
""" """
Main scene manager to display the scenes of the application Main scene manager to display the scenes of the application
@ -16,13 +28,16 @@ def main(windowed_mode=False):
""" """
pygame.init() pygame.init()
clock = pygame.time.Clock() clock = pygame.time.Clock()
if windowed_mode:
window = pygame.display.set_mode(SCREEN_SIZE)
else:
window = pygame.display.set_mode(SCREEN_SIZE, pygame.FULLSCREEN)
# Starts the program with the surface 'dial' as the default screen_size = get_screen_resolution()
surface = getattr(globals()['surfaces'], 'dvd_screen').Surface(SCREEN_SIZE)
if windowed_mode:
window = pygame.display.set_mode(screen_size)
else:
window = pygame.display.set_mode(screen_size, pygame.FULLSCREEN)
# Starts the program with the surface 'steam_screen' as the default
surface = getattr(globals()['surfaces'], 'steam_screen').Surface(screen_size)
running = True running = True
while running: while running:
clock.tick(FPS) clock.tick(FPS)
@ -37,7 +52,7 @@ def main(windowed_mode=False):
next_surface = surface.next_surface next_surface = surface.next_surface
if next_surface not in VALID_SURFACES: if next_surface not in VALID_SURFACES:
raise Exception('Given surface is not a valid surface!') raise Exception('Given surface is not a valid surface!')
surface = getattr(globals()['surfaces'], next_surface).Surface(SCREEN_SIZE) surface = getattr(globals()['surfaces'], next_surface).Surface(screen_size)
pygame.display.flip() pygame.display.flip()
pygame.quit() pygame.quit()

View File

@ -0,0 +1 @@
from . import steam_screen

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -0,0 +1,212 @@
import pygame
import os
import random
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
SCALE_RATIO = (0.325 / 800.0)
SPEED_RATIO = (2.5 / 800.0)
class SteamLogo(pygame.sprite.Sprite):
"""
A Pygame sprite representing a Steam logo that moves around the screen.
Attributes:
base_image (pygame.Surface): The original image of the Steam logo.
x_speed (int): Current speed in the X axis.
y_speed (int): Current speed in the Y axis.
max_x (int): Maximum X position on the screen.
max_y (int): Maximum Y position on the screen.
is_mini (bool): Bool to determine whether the logo is a mini (background) logo or not.
"""
def __init__(self, window_size: tuple, is_mini: bool = False, *groups):
"""
Initialize SteamLogo sprite with given window size.
Args:
window_size (tuple): Size of the game window (width, height).
is_mini (bool): Determines if the logo is going to be a background logo (mini) or the foreground one.
"""
super().__init__(*groups)
self.is_mini = is_mini
# Load and scale original logo image
self.base_image = pygame.image.load(SCRIPT_DIR + '/resources/steam_logo.png')
self.base_image.convert_alpha()
logo_scaling = min(window_size) * SCALE_RATIO
# If the logo is mini, make it 2 to 3 times smaller
if is_mini:
logo_scaling /= random.uniform(2, 3)
self.base_image = pygame.transform.smoothscale(self.base_image,
(self.base_image.get_width() * logo_scaling,
self.base_image.get_height() * logo_scaling))
# Copy the base image and store it in a separate instance variable; this is the image that will be drawn.
self.image = self.base_image.copy()
# Generate random color and apply it to the sprite.
self.random_color()
# Initialize rectangle (position, size) and speed attributes.
self.rect = self.image.get_rect()
self.max_speed = min(window_size) * SPEED_RATIO
# Make it so that the logo travels in a random direction when created
self.x_speed = (self.max_speed - 1) * random.choice([-1, 1])
self.y_speed = (self.max_speed - 1) * random.choice([-1, 1])
# Calculate maximum X and Y positions on the screen for boundary checking.
self.max_x = window_size[0] - self.rect.width
self.max_y = window_size[1] - self.rect.height
# The position of the logo is determined by float values that are separate from the Pygame rect position.
# By storing the position as a float and drawing it to the screen after, we are able to apply more precise
# speed values (e.g. 2.124) and it will appear as though it is moving smoothly.
# The starting location of the logo is randomized.
self.float_x = random.random() * self.max_x
self.float_y = random.random() * self.max_y
self.rect.x = self.float_x
self.rect.y = self.float_y
def random_color(self):
"""
Generate a new, random color and apply it to the sprite.
"""
# Create a surface with alpha channel for generating transparent colors.
color_surface = pygame.Surface(self.image.get_size(), pygame.SRCALPHA)
# Generate random RGB values and fill the color surface accordingly.
new_color = (random.randint(64, 255),
random.randint(64, 255),
random.randint(64, 255))
# If this is a mini logo, make it 80% darker
if self.is_mini:
color_surface.fill(tuple(int(x / 5) for x in new_color))
else: # If not, make it the generated color.
color_surface.fill(new_color)
# Replace the drawn image with a copy of the base image and apply the randomly generated color.
self.image = self.base_image.copy()
self.image.blit(color_surface, (0, 0), special_flags=pygame.BLEND_RGBA_MULT)
def update(self):
"""
Update SteamLogo sprite's position and speed based on its current state.
This method is called each frame during game execution to move the logo around
the screen and to handle collision/color changes.
"""
# Move the logo in the X and Y axis according to its current speed in those directions.
self.float_x += self.x_speed
self.float_y += self.y_speed
# Check for collisions with edges of the screen, change direction if necessary,
# and generate a new random color when hitting an edge.
if self.float_x < 0 or self.float_x > self.max_x:
# Mini logos have some randomness built into their bounce function to make the background look more organic.
if self.is_mini:
# Reflect off the left or right wall, clamping the speed (in case it was raised too high)
# The speed gets clamped if it goes above the max speed or below the starting speed (max - 1)
if self.x_speed > 0:
self.x_speed = max(-1 * self.max_speed, min((self.x_speed * -1), (-1 * self.max_speed) + 1))
else:
self.x_speed = max(self.max_speed - 1, min((self.x_speed * -1), self.max_speed))
# Add or subtract anywhere from 0 to 10% of the max speed to the vertical speed component
self.y_speed += random.uniform(self.max_speed * 0.1, self.max_speed * -0.1)
# If the logo isn't mini, do a simple reflection with no fancy stuff.
else:
self.x_speed *= -1
self.random_color()
# Do the same stuff for the top and bottom walls
if self.float_y < 0 or self.float_y > self.max_y:
if self.is_mini:
if self.y_speed > 0:
self.y_speed = max(-1 * self.max_speed, min((self.y_speed * -1), (-1 * self.max_speed) + 1))
else:
self.y_speed = max(self.max_speed - 1, min((self.y_speed * -1), self.max_speed))
self.x_speed += random.uniform(self.max_speed * 0.1, self.max_speed * -0.1)
else:
self.y_speed *= -1
self.random_color()
# Ensure the logo stays within screen boundaries by clamping its position.
self.float_x = max(0, min(self.float_x, self.max_x))
self.float_y = max(0, min(self.float_y, self.max_y))
# Set the Pygame rectangle's position to the stored floating point position
self.rect.x = self.float_x
self.rect.y = self.float_y
class Surface(pygame.Surface):
"""
A custom Pygame surface class for managing game logic and events.
Attributes:
running (bool): Flag indicating whether the surface is still running.
quit (bool): Flag signaling that the program should end rather than moving to a new surface.
next_surface (str): Name of the next surface to display after current one stops.
all_sprites (pygame.sprite.Group): A group containing sprites for easy sprite management.
"""
def __init__(self, window_size: tuple):
"""
Initialize Surface class with given window size.
Args:
window_size (tuple): Size of the game window (width, height).
"""
# Create a Pygame surface with alpha channel for generating transparent colors.
super().__init__(window_size, pygame.SRCALPHA)
# Initialize flags and attributes for managing game state.
self.running = True
self.quit = False
self.next_surface = ''
pygame.mouse.set_visible(False)
self.all_sprites = pygame.sprite.Group()
# Create all the mini Steam logos
for i in range(25):
self.all_sprites.add(SteamLogo(window_size, is_mini=True))
# Create the big Steam logo
self.all_sprites.add(SteamLogo(window_size))
def update(self):
"""
Update game state by handling events, updating sprites, and redrawing surfaces.
This method is called every frame during game execution.
"""
# Fill the surface with a black background color
self.fill(pygame.colordict.THECOLORS.get('black'))
# Handle events such as mouse button clicks or key presses.
for event in pygame.event.get():
match event.type:
case pygame.MOUSEBUTTONDOWN:
# Stop the game when a user clicks anywhere on the screen.
self.running = False
self.quit = True
case pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
# Also stop the game if the Escape key is pressed.
self.running = False
self.quit = True
# Update positions and speeds of all sprites
self.all_sprites.update()
# Draw all sprites onto this surface.
self.all_sprites.draw(self)