Pages

vineri, 21 august 2026

PyGame : using shaders with fonts.

The program shows three text effects using pygame. Each effect is created by slicing the rendered text into thin vertical strips and moving or recoloring those strips. This method works well in Pydroid 3 because it does not require real GPU shaders. The glow effect draws the same text several times with small offsets and low transparency, creating a soft neon halo that pulses over time. The wave effect shifts each vertical slice up or down using a sine function, making the text look like it moves smoothly in a wave pattern. The fire effect applies warm colors and gentle vertical motion to each slice, producing a stable flame‑like animation. The main loop updates a time variable, clears the screen, draws all three effects, and refreshes the display at sixty frames per second. This keeps the animation smooth and consistent.
Let's see the source code:
import pygame
import sys
import math
import random

pygame.init()

WIDTH, HEIGHT = 900, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Optimized Text Effects")

# Larger fonts
font_glow = pygame.font.SysFont("arial", 110)
font_wave = pygame.font.SysFont("comicsansms", 100)
font_fire = pygame.font.SysFont("couriernew", 95)

clock = pygame.time.Clock()
t = 0.0

# Soft pulsing glow effect
def draw_glow(surface, text, font, x, y, color, t):
    pulse = (math.sin(t * 1.5) + 1) * 0.5
    intensity = int(6 + pulse * 8)

    for i in range(1, intensity):
        glow = font.render(text, True, color)
        glow.set_alpha(max(10, 60 - i * 6))
        surface.blit(glow, (x - i, y - i))
        surface.blit(glow, (x + i, y + i))

    main = font.render(text, True, color)
    surface.blit(main, (x, y))

# Smooth wave deformation
def draw_wave(surface, text, font, x, y, t, color):
    base = font.render(text, True, color)
    w, h = base.get_size()

    for i in range(w):
        slice_rect = pygame.Rect(i, 0, 1, h)
        slice_img = base.subsurface(slice_rect)
        offset = int(12 * math.sin(i * 0.04 + t * 1.2))
        surface.blit(slice_img, (x + i, y + offset))

# Stable fire effect
def draw_fire(surface, text, font, x, y, t):
    base = font.render(text, True, (255, 120, 0))
    w, h = base.get_size()

    for i in range(w):
        slice_rect = pygame.Rect(i, 0, 1, h)
        slice_img = base.subsurface(slice_rect)

        offset = int(8 * math.sin(t * 1.5 + i * 0.03))

        r = 255
        g = 100 + int(50 * math.sin(t + i * 0.01))
        b = 20

        slice_img.fill((r, g, b), special_flags=pygame.BLEND_MULT)
        surface.blit(slice_img, (x + i, y + offset))

# Main loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    t += 0.08
    screen.fill((15, 15, 25))

    draw_glow(screen, "NEON GLOW", font_glow, 50, 40, (0, 180, 255), t)
    draw_wave(screen, "WAVE EFFECT", font_wave, 50, 220, t, (255, 255, 255))
    draw_fire(screen, "FIRE TEXT", font_fire, 50, 380, t)

    pygame.display.flip()
    clock.tick(60)

pygame.quit()
sys.exit()

duminică, 16 august 2026

PyGame : drawing SVG image on pydroid 3.

This Pygame script displays an SVG image and status text on Android. It reads the SVG into memory as binary data using io.BytesIO, loads it via pygame.image.load(), scales it by 200%, and dynamically centers it below the status text to prevent visual overlap.
Let's see the source code:
import io
import os
import pygame
import xml.etree.ElementTree as ET

pygame.init()
pygame.font.init()

WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Centered Scaled SVG - Pygame Android")

font = pygame.font.SysFont("Arial", 28)
svg_path = "/storage/emulated/0/Download/imagine.svg"

scaled_svg = None
status_message = ""

def load_svg_to_pygame(path):
    with open(path, "rb") as f:
        svg_data = f.read()
    binary_buffer = io.BytesIO(svg_data)
    return pygame.image.load(binary_buffer)

if not os.path.exists(svg_path):
    status_message = f"File does not exist at:\n{svg_path}"
else:
    try:
        raw_img = load_svg_to_pygame(svg_path)
        
        # Explicit 2.0x enlargement multiplier
        target_w = int(raw_img.get_width() * 2.0)
        target_h = int(raw_img.get_height() * 2.0)
        
        scaled_svg = pygame.transform.scale(raw_img, (target_w, target_h))
        status_message = f"Success: SVG enlarged to 2X ({target_w}x{target_h}px)!"
    except Exception as e:
        try:
            tree = ET.parse(svg_path)
            root = tree.getroot()
            
            w = int(float(root.attrib.get('width', 300))) * 2
            h = int(float(root.attrib.get('height', 300))) * 2
            
            scaled_svg = pygame.Surface((w, h), pygame.SRCALPHA)
            scaled_svg.fill((200, 200, 200, 255))
            status_message = f"Native format error: {e}\nGenerated Fallback ({w}x{h})"
        except Exception as err_xml:
            status_message = f"Total processing error:\n{err_xml}"

def draw_text(text, x, y, color=(0, 255, 100)):
    lines = text.split('\n')
    for i, line in enumerate(lines):
        text_render = font.render(line, True, color)
        screen.blit(text_render, (x, y + i * 34))
    return y + (len(lines) * 34)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    screen.fill((30, 30, 30))

    # 1. Render text at the very top
    text_end_y = draw_text(status_message, 20, 20)

    # 2. Place 2X SVG completely below text area, centered horizontally
    if scaled_svg:
        offset_y = text_end_y + 40  # Ensures a mandatory 40px gap below text
        center_x = WIDTH // 2
        center_y = offset_y + (scaled_svg.get_height() // 2)

        svg_rect = scaled_svg.get_rect(center=(center_x, center_y))
        screen.blit(scaled_svg, svg_rect)

    pygame.display.flip()

pygame.quit()

luni, 10 august 2026

PyGame : Fixing Android Screen Rotation Crashes in Pygame and Pydroid 3.

When developing mobile 2D games using Python, Pygame, and Pydroid 3 on Android, rotating the device frequently causes application crashes or frozen rendering loops. This core instability stems from how the Android operating system handles application lifecycles and SDL video contexts. When an orientation shift occurs, the system destroys the underlying hardware surface. Standard Pygame implementations try to catch this through resize events or re-initialize the surface using display mode settings. On Android architectures, this dynamic re-allocation often triggers memory faults or direct binary crashes. Furthermore, fixed pixel logic locks game entities into rigid coordinate boundaries, causing objects to render off-screen when the display aspect ratio changes
To solve these performance bottlenecks, this script implements a robust mobile rendering pattern that combines three key engineering strategies.
Non-Destructive Context Polling: Instead of intercepting risky window resize events that break SDL bindings, this script queries hardware display properties dynamically per frame using system info calls. This approach preserves the active video memory context, preventing system-level crashes.
Delta Time Frame-Independent Physics: Traditional loop iterations rely on rigid frame counts, which fluctuate wildly during background tasks or screen rotations. This script measures precise time deltas, ensuring that entity velocity calculations remain strictly constant in pixels per second regardless of processor load or refresh rates.
Proportional Relative Mapping: To preserve entity placement during aspect ratio flips, this script tracks horizontal coordinates as normalized fractions. When viewport dimensions swap, entities instantly project to their accurate proportional coordinates without scaling distortion.
Let's see the source code:
import pygame
import random

pygame.init()

info = pygame.display.Info()
WIDTH, HEIGHT = info.current_w, info.current_h

screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.FULLSCREEN)
clock = pygame.time.Clock()

class FallingBox:
    def __init__(self, rel_x):
        self.rel_x = rel_x                      # Horizontal percentage (kept during rotation)
        self.y_pixels = random.uniform(0, 100)  # Current vertical pixel position
        self.size = 50                          # Box size in pixels

        # Maximum falling distance (recalculated dynamically)
        self.max_dist_pixels = random.randint(150, 400)

        # Constant speed in pixels per second
        self.speed_pps = random.uniform(150.0, 300.0)

        self.color = (
            random.randint(100, 255),
            random.randint(100, 255),
            random.randint(100, 255)
        )

    def update(self, dt, current_h):
        # Time‑based movement (dt in seconds)
        self.y_pixels += self.speed_pps * dt

        # Reset if reaching limit or bottom of screen
        if self.y_pixels >= self.max_dist_pixels or self.y_pixels >= current_h - self.size:
            self.y_pixels = 0.0
            self.max_dist_pixels = random.randint(150, int(current_h * 0.6))
            self.speed_pps = random.uniform(150.0, 300.0)

    def draw(self, surface, current_w):
        # X position scales with screen width
        pos_x = int(self.rel_x * current_w) - (self.size // 2)
        pos_y = int(self.y_pixels)

        # Guide line
        limit_y = int(self.max_dist_pixels)
        pygame.draw.line(surface, (70, 70, 90), (pos_x, limit_y), (pos_x + self.size, limit_y), 2)

        # Draw the box
        pygame.draw.rect(surface, self.color, (pos_x, pos_y, self.size, self.size))


# Create 4 falling boxes
boxes = [FallingBox(i / 5.0) for i in range(1, 5)]

running = True
while running:
    dt = clock.tick(60) / 1000.0  # Frame time in seconds

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Update screen size dynamically (rotation, resizing)
    info = pygame.display.Info()
    current_w, current_h = info.current_w, info.current_h

    # Update boxes
    for box in boxes:
        box.update(dt, current_h)

    # Render
    screen.fill((20, 20, 30))

    for box in boxes:
        box.draw(screen, current_w)

    pygame.display.flip()

pygame.quit()